How to migrate an AutoCAD based application to .NET Core 8.0 [Part I]

With .NET Core 8.0, there has been a new kid on the block which provides (and promises) a multiplatform framework for the software developer. Naturally, the newest iteration of AutoCAD with Version 2025 has been incorporating .NET Core 8.0, switching from the (now) legacy .NET Framework 4.x. This has serious implications for all developers that develop applications for AutoCAD and AutoCAD OEM. A mayor migration to the (almost) newest .NET incarnation (not counting .NET 9.0) is needed to make all AutoCAD based applications build- and runnable for AutoCAD 2025, its siblings (like AutoCAD OEM) and future versions.

Autodesk has published some migration guidelines on its DevBlog, but this leaves out some important specifics that may be important to the AutoCAD developer.

One mayor showstopper can be, for example, the need to target different product versions of AutoCAD with the application code. Maybe, for legacy or business reasons, the applications need to run with older AutoCAD versions like 2022 or 2023. Usually, within the old .NET Framework, this has been easy with creating a new project configuration, selecting the target framework version and, maybe, set some additional parameters. But .NET Core has evolved and advanced in a different direction, making it harder to keep everything together for an easy developer and build experience like it used to be with the old .NET Framework.

In this two-part blog series, I will try to give a detailed description of the best practices for the migration of Visual Studio solutions from .NET Framework 4.x to .NET Core 8.0, while still being able to target older AutoCAD version with the same codebase.

In the first part, I will show the migration of a managed C++/CLI solution from .NET Framework 4.x to .NET Core 8.0. Usually, you use managed C++/CLI solutions when working with Custom Entities or exotic AutoCAD API’s that are only available in the native C++ world. The managed C++/CLI solutions works as a wrapper around the native objects and API’s to make it available for the consummation by .NET.

The second part will describe the migration from .NET Framework 4.x to .NET Core 8.0 for fully managed applications, usually realized with C#.

Let’s get started!

(but some disclaimers first)

(We use the German edition of Windows 11 and Visual Studio 2022. Some screenshots appear with text in German language. I think that people not speaking (or reading) German will get the gist of what is happening, despite the language barrier.)

(When files are edited outside of Visual Studio (VS), I recommend the use of Microsoft Visual Studio Code (VS Code) as an editor. VS Code has some handy features that makes editing easy. But remember: Visual Studio (VS) and VS Code are very different animals!)

Part 1: Migrate managed C++/CLI solution from .NET Framework 4.x to .NET Core 8.0

I am demonstrating the migration for a C++/CLI solution with a simple wrapper solution called AuStTpWrapper from our AutoSTAGE application. (Disclaimer: I am the lead developer for AutoSTAGE). This solution wraps some AutoCAD C++ API calls for the tool palette that are not available with the regular AutoCAD .NET API.

This solution is targeting the AutoCAD version 2020 to 2024, including respective builds for AutoCAD OEM.

001_AuStTpWrapper_Solution_Targets

To make this buildable for AutoCAD 2025, we need to add a new configuration target, which we will call ACAD2025 in this demonstration.

This is the Explorer file view of the solution. The files that we need to edit to include the AutoCAD 2025 targets are selected and shown with the blue background colour.

002_AuStTpWrapper_Explorer

First, we need to open the *.sln file in VS Code and add the configurations for AutoCAD 2025:

Then open the .vcxproj file (not .filters and .user) and copy all sections where ACAD2024 occurs and rename the copied sections to ACAD2025. The screenshot shows an abbreviated version, the file is quite long.

After saving of these two files, reload the solution in Visual Studio.

Sometimes, when editing the *.sln and *.vcxproj files, not all configuration targets show up in the project configuration drop down. Here we would expect to see ACAD2025 as well.

005_Missing_Target

To fix this, open the Configuration Manager (last item in the drop down menu) and add this manually in the manager with New.

Now the project configuration looks like expected:

007_Targets_complete

Trying to build the new configuration for AutoCAD 2025 will not work, because we are still stuck with .NET Framework 4.8, which has been used by AutoCAD 2024.

Now we need to deal with the actual porting for .NET. Microsoft has released a short porting manual for this.

The most important points are:

  • C++/CLI projects don’t support the newer SDK-style project file format. Instead, C++/CLI projects use the same .vcxproj file format that other Visual Studio C++ projects use.

  • C++/CLI projects can’t target multiple .NET platforms. If you need to build a C++/CLI project for both .NET and .NET Framework, use separate project files.

As said in the second point, we need to introduce another .vcxproj file to be able to target ACAD2025.

Before we proceed, it is advisable to unload the solution in Visual Studio.

Copy the *.vcxproj files for this solution and rename the copied files to (project-name)_net4.vcxproj.

The new files with the _net4 addition target the old legacy .NET Framework. Eventually, a couple of years down the line, we can phase them out when we don’t support AutoCAD 2024 anymore. To be future oriented, we keep the original project name for .NET Core.

Open the *.sln file with VS Code and change the names in Project()

Open the new (project-name)_net4.vcxproj file with VS Code and change the <ProjectName> node for all AutoCAD targets up until ACAD2024 to mirror the name in the sln-file. Do not change the <ProjectName> for ACAD2025!

Now open the solution in Visual Studio again and check if you can build this for ACAD2024 and .NET Framework 4.x.

014_Check_build_for_net4

Die Neuerstellung wurde um 16:21 gestartet...
1>------ Neues Erstellen gestartet: Projekt: AuStTpWrapper_net4, Konfiguration: ACAD2024 x64 ------
1>AuStTpWrapperClass.cpp
1>AuStTpWrapper.cpp
1>stdafx.cpp
1>.NETFramework,Version=v4.8.AssemblyAttributes.cpp
1>   Bibliothek "D:\REL\RELEASE\ACAD2024\/AuStTpWrapper.lib" und Objekt "D:\REL\RELEASE\ACAD2024\/AuStTpWrapper.exp" werden erstellt.
1>AuStTpWrapper_net4.vcxproj -> D:\REL\RELEASE\ACAD2024\AuStTpWrapper.dll
1>D:\REL\RELEASE\ACAD2024\AuStTpWrapper.dll
1>***
1>*** Build to D:\REL\RELEASE\ACAD2024\AuStTpWrapper.dll
1>***
========== Alle neu erstellen: 1 erfolgreich, 0 fehlgeschlagen, 0 ĂĽbersprungen ==========
========== Neu erstellen abgeschlossen um 16:21 und dauerte 03,878 Sekunden ==========

(The echo with the *** are pre- and post-build messages used for our build automation. This will not occur normally while building).

This builds fine for ACAD2024, as expected.

Now, let’s work on the project file for .NET Core 8.0. Before we proceed, it is advisable to unload the solution in Visual Studio again.

Open the original *.vcxproj file with VS Code and replace in the PropertyGroups for ACAD2025 the <CLRSupport> node with <CLRSupport>NetCore</CLRSupport>

The PlatformToolset needs to be v143 for AutoCAD 2025.

Replace in the PropertyGroups for ACAD2025 the <TargetFrameworkVersion> node with <TargetFramework>net8.0-windows</TargetFramework>

You could also just specify net.8.0, but with the addition of -windows you make it clear that you want to explicitly target windows only, as is customary with application development for the windows version of AutoCAD.

Change the ProjectGuid of all PropertyGroups to a new GUID.

Last, we should add the <CompileAsManaged>NetCore</CompileAsManaged>
in the ClCompile section of the ItemDefinitionGroups. Omitting this may result in a nasty compiler error.

Now we have two project files:

  • AuStTpWrapper.vcxproj (.NET Core 8.0)
  • AuStTpWrapper_net4.vcxproj (.NET Framework 4.x)

Each project has a unique GUID.

The Visual Studio solution knows only about the second .NET Framework project. We need to open the *.sln-file and add the project for .NET Core.

Copy the first Project-EndProject lines and add this below the first project. Change the first project name and the name of the *.vcxproj file to match the names from the .NET Core 8.0 *.vcxproj file. Change the GUID for the first project to the ProjectGuid as set in the *.vcxproj file. Do not forget to change the GUID for the first project!

Copy all entries in the GlobalSection that refer to the second .NET Framework project and paste the copy into the GlobalSection. Change the GUID’s of the copied entries.

Open the solution again in Visual Studio. Now we have two projects in our solution:

031_Projects_in_solution

To be sure that we did everything right, select the _net4 project and try to build this for ACAD2024 (or any other version below ACAD2025). Trying to build this project for ACAD2025 will result in failure.

Let’s test this again for the .NET Core 8.0 project. Select the first project in the project explorer and set the configuration to ACAD2025.

Die Neuerstellung wurde um 16:20 gestartet...
1>------ Neues Erstellen gestartet: Projekt: AuStTpWrapper, Konfiguration: ACAD2025 x64 ------
1>AuStTpWrapperClass.cpp
1>AuStTpWrapper.cpp
1>stdafx.cpp
1>.NETCoreApp,Version=v8.0.AssemblyAttributes.cpp
1>   Bibliothek "D:\REL\RELEASE\ACAD2025\/AuStTpWrapper.lib" und Objekt "D:\REL\RELEASE\ACAD2025\/AuStTpWrapper.exp" werden erstellt.
1>AuStTpWrapper.vcxproj -> D:\REL\RELEASE\ACAD2025\AuStTpWrapper.dll
1>D:\REL\RELEASE\ACAD2025\AuStTpWrapper.dll
1>***
1>*** Build to D:\REL\RELEASE\ACAD2025\AuStTpWrapper.dll
1>***
========== Alle neu erstellen: 1 erfolgreich, 0 fehlgeschlagen, 0 ĂĽbersprungen ==========
========== Neu erstellen abgeschlossen um 16:20 und dauerte 04,077 Sekunden ==========

This also builds fine for ACAD2025!

Conclusion

When building the solution for AutoCAD 2025, select the first project and the ACAD2025 configuration. Building for AutoCAD 2024 and older, you need to select the project ending with _net4 and the desired configuration.

As we have seen, preparing a solution for the parallel build to the .NET Framework 4.x as well as to .NET Core 8.0 is not straightforward. But when adhering to the above outlined steps, this will work, albeit not as easy as it used to be.

(To look on the bright side: When you eventually phase out your support for AutoCAD 2024, there is only .NET Core left over. So it is just a matter of time!)

Some final Gotchas

  • When you add a new file or an existing file to one of the projects and this should be used also in the other project, you have to add it in both projects in the VS project explorer.

  • When there is some build automation, you have to make sure that you build for the right project when targeting different AutoCAD versions.

  • If you happen to run into the following linker error, then add in the linker the additional dependencies to the acpal.lib.
    1>AuStTpWrapperClass.obj : error LNK2028: Nicht aufgelöstes Token (0A00086C) ""void * __cdecl acHeapAlloc(void *,unsigned __int64)" (?acHeapAlloc@@$$FYAPEAXPEAX_K@Z)", auf das in Funktion ""private: void __cdecl AcString::makeAndCopy(wchar_t const *,unsigned int,unsigned int)" (?makeAndCopy@AcString@@$$FAEAAXPEB_WII@Z)" verwiesen wird.

  • When you happen to get the error message (Compilerfile "msc1.cpp", Line 1587) from the compiler, you most likely have to add the <CompileAsManaged>NetCore</CompileAsManaged> in the ClCompile section of the ItemDefinitionGroups.

2 Likes

Great article Jens!
Thanks
Shawn Golden
Microvellum

1 Like

Really great stuff as always Jens!

Thank you Jens, but I am wondering why you created two different vcxproj files?

While you could have used conditions to filter configurations before adding references or assigning a .net version.

  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='AutoCAD_2025'" Label="Configuration">
    <ConfigurationType>DynamicLibrary</ConfigurationType>
    <UseDebugLibraries>true</UseDebugLibraries>
    <PlatformToolset>v143</PlatformToolset>
    <CharacterSet>Unicode</CharacterSet>
    <UseOfMfc>Dynamic</UseOfMfc>
    <CLRSupport>NetCore</CLRSupport>
    <TargetFramework>net8.0-windows</TargetFramework>
    <UseWindowsForms>true</UseWindowsForms>
    <VCToolsVersion>14.38.33130</VCToolsVersion>
  </PropertyGroup>

  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='AutoCAD_2024" Label="Configuration">
    <ConfigurationType>DynamicLibrary</ConfigurationType>
    <UseDebugLibraries>true</UseDebugLibraries>
    <PlatformToolset>v143</PlatformToolset>
    <CharacterSet>Unicode</CharacterSet>
    <UseOfMfc>Dynamic</UseOfMfc>
    <CLRSupport>true</CLRSupport>
    <TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
    <ResolveAssemblyReferenceIgnoreTargetFrameworkAttributeVersionMismatch>true</ResolveAssemblyReferenceIgnoreTargetFrameworkAttributeVersionMismatch>
    <VCToolsVersion>14.32.31326</VCToolsVersion>
  </PropertyGroup>

Filtering Condition.

  <Choose>
    <When Condition="('$(Configuration)|$(Platform)'=='AutoCAD_2025') Or ('$(Configuration)|$(Platform)'=='AutoCAD_2025|x64') ">
       <ItemGroup>
        <FrameworkReference Include="Microsoft.WindowsDesktop.App" />
      </ItemGroup>
      <ItemGroup>
        <Reference Include="accoremgd">
          <HintPath>$(API_OBJECTARX_2025)\inc\AcCoreMgd.dll</HintPath>
          <Private>false</Private>
          <CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
        </Reference>
        <Reference Include="accui">
          <HintPath>$(API_OBJECTARX_2025)\inc\AcCui.dll</HintPath>
          <Private>false</Private>
          <CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
        </Reference>
        <Reference Include="acdbmgd">
          <HintPath>$(API_OBJECTARX_2025)\inc\AcDbMgd.dll</HintPath>
          <Private>false</Private>
          <CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
        </Reference>
        <Reference Include="acdbmgdbrep">
          <HintPath>$(API_OBJECTARX_2025)\inc\acdbmgdbrep.dll</HintPath>
          <Private>false</Private>
          <CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
        </Reference>
        <Reference Include="acdx">
          <HintPath>$(API_OBJECTARX_2025)\inc\AcDx.dll</HintPath>
          <Private>false</Private>
          <CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
        </Reference>
        <Reference Include="acmgd">
          <HintPath>$(API_OBJECTARX_2025)\inc\AcMgd.dll</HintPath>
          <Private>false</Private>
          <CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
        </Reference>
        <Reference Include="acmr">
          <HintPath>$(API_OBJECTARX_2025)\inc\AcMr.dll</HintPath>
          <Private>false</Private>
          <CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
        </Reference>
        <Reference Include="actcmgd">
          <HintPath>$(API_OBJECTARX_2025)\inc\AcTcMgd.dll</HintPath>
          <Private>false</Private>
          <CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
        </Reference>
        <Reference Include="adwindows">
          <HintPath>$(API_OBJECTARX_2025)\inc\AdWindows.dll</HintPath>
          <Private>false</Private>
          <CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
        </Reference>
      </ItemGroup>
    </When>
    <When Condition="('$(Configuration)|$(Platform)'=='AutoCAD_2024')">
      <ItemGroup>
        <Reference Include="accoremgd">
          <HintPath>$(API_OBJECTARX_2024)\inc\AcCoreMgd.dll</HintPath>
          <Private>false</Private>
          <CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
        </Reference>
        <Reference Include="accui">
          <HintPath>$(API_OBJECTARX_2024)\inc\AcCui.dll</HintPath>
          <Private>false</Private>
          <CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
        </Reference>
        <Reference Include="acdbmgd">
          <HintPath>$(API_OBJECTARX_2024)\inc\AcDbMgd.dll</HintPath>
          <Private>false</Private>
          <CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
        </Reference>
        <Reference Include="acdbmgdbrep">
          <HintPath>$(API_OBJECTARX_2024)\inc\acdbmgdbrep.dll</HintPath>
          <Private>false</Private>
          <CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
        </Reference>
        <Reference Include="acdx">
          <HintPath>$(API_OBJECTARX_2024)\inc\AcDx.dll</HintPath>
          <Private>false</Private>
          <CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
        </Reference>
        <Reference Include="acmgd">
          <HintPath>$(API_OBJECTARX_2024)\inc\AcMgd.dll</HintPath>
          <Private>false</Private>
          <CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
        </Reference>
        <Reference Include="actcmgd">
          <HintPath>$(API_OBJECTARX_2024)\inc\AcTcMgd.dll</HintPath>
          <Private>false</Private>
          <CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
        </Reference>
        <Reference Include="adwindows">
          <HintPath>$(API_OBJECTARX_2024)\inc\AdWindows.dll</HintPath>
          <Private>false</Private>
          <CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
        </Reference>
      </ItemGroup>
      <ItemGroup>
        <Reference Include="PresentationCore" />
        <Reference Include="PresentationFramework" />
        <Reference Include="System" />
        <Reference Include="System.Core" />
        <Reference Include="System.Data" />
        <Reference Include="System.Drawing" />
        <Reference Include="System.Windows.Presentation" />
        <Reference Include="System.Xaml" />
        <Reference Include="System.Xml" />
        <Reference Include="System.Windows.Forms" />
        <Reference Include="System.Xml.Linq" />
        <Reference Include="WindowsBase" />
      </ItemGroup>
    </When>
  </Choose>

Hello,

the reason is that the project files for .NET Core use a different format than the .NET Framework. Because of this, you have to have two different project files. It is not just different configurations.

In the .NET documentation, it is stated that

Project files for .NET use a different format than .NET Framework.

and

The project files for .NET use a different format than .NET Framework, known as the SDK-style project format. Even if you’re not moving from .NET Framework to .NET, you should still upgrade the project file to the latest format. The way to specify a target framework is different in SDK-style projects.

Here you find the full article: Port from .NET Framework to .NET 7 - .NET Core | Microsoft Learn

When I started the migration, I tried to find some middle ground to keep the effort manageable, but this quickly turned into developer hell where Visual Studio behaved very unpredictable. My learning has been to stick with the recommendation from Microsoft, hence the separate project files.

-Jens