Motivation #
To have a AutoCAD/Civil 3D plugin project in Visual Studio setup and running is not easy. Not least because you have to know how to launch AutoCAD/Civil 3D from the Visual Studio to load your plugin . To get it to work, each developers might have to make modifications to their config files due to that the directory of the repos are different for different developers. This makes collaboration and the whole plugin setup process hard, because you might have to exclude an otherwise important configuration files from your git repo.
So here, I would like to provide the simplest plugin project that just works – you can copy and paste everything here into respective locations, and it will just work. You and your fellow developers can check in the code and configs to the git repo, and everyone can modify the code and configs confidently, knowing that once the other parties checkout the code, it will just work, no need to do extra fiddling because now the repo doesn’t depend on the absolute path of your repo.
Machine and AutoCAD setup #
But still, you have to do a bit of environmental setup.
First, you have to define a new environment variable %AutoCADDir%
in your Windows machine ( I assume that you are running on Windows, because Civil 3D can only run on Windows). This is essentially just the directory of your AutoCAD
program. Usually it’s the C:\Program Files\Autodesk\AutoCAD %Year%
Then, you have to ensure that the directory where your plugin DLL is located is in a Trusted Location
of AutoCAD. If not then your plugin won’t run.
The minimum project #
After you have done the setup properly as per the above, then everything is easy; you just have to copy and paste the below code and scripts to their respective place, and compile them in VS 2022. Everything will just work.
The csproject
#
Here’s the code for csproject
.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<EnableDefaultItems>true</EnableDefaultItems>
</PropertyGroup>
<ItemGroup>
<None Remove="Properties\launchSettings.template.json" />
<None Remove="LoadPlugin.scr.template" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="AutoCAD.NET" Version="25.0.1" />
<PackageReference Include="Civil3D.NET" Version="13.7.1175" /> <!-- civil 3D 2025, you can have your own Civil 3D version -->
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
<Target Name="GenerateLaunchSettings" BeforeTargets="Build"> <!-- Generate LaunchSettings.json on the fly based on a template-->
<PropertyGroup>
<ProjectDir>$(MSBuildProjectDirectory)</ProjectDir>
<LaunchTemplate>$(ProjectDir)\Properties\launchSettings.template.json</LaunchTemplate>
<LaunchOutput>$(ProjectDir)\Properties\launchSettings.json</LaunchOutput>
<ProjectDirEscaped>$([System.String]::Copy('$(ProjectDir)').Replace('\', '\\'))</ProjectDirEscaped>
</PropertyGroup>
<ReadLinesFromFile File="$(LaunchTemplate)">
<Output TaskParameter="Lines" ItemName="LaunchJsonLines" />
</ReadLinesFromFile>
<WriteLinesToFile File="$(LaunchOutput)" Lines="@(LaunchJsonLines->Replace('{{LaunchDir}}', '$(ProjectDirEscaped)'))" Overwrite="true" />
</Target>
<Target Name="GenerateLoadPluginScript" AfterTargets="Build"> <!-- Generate LoadPlugin.scr on the fly based on a template-->
<PropertyGroup>
<TemplatePath>$(MSBuildProjectDirectory)\LoadPlugin.scr.template</TemplatePath>
<OutputScriptPath>$(MSBuildProjectDirectory)\LoadPlugin.scr</OutputScriptPath>
<DllFullPath>$(TargetPath)</DllFullPath>
</PropertyGroup>
<Message Text="Reading template from: $(TemplatePath)" Importance="High" />
<Message Text="Writing output to: $(OutputScriptPath)" Importance="High" />
<Message Text="Replacing $DLLPATH$ with: $(DllFullPath)" Importance="High" />
<ReadLinesFromFile File="$(TemplatePath)">
<Output TaskParameter="Lines" ItemName="TemplateLine" />
</ReadLinesFromFile>
<Message Text="Template content read: @(TemplateLine)" Importance="High" />
<ItemGroup>
<ProcessedLines Include="$([System.String]::Copy('%(TemplateLine.Identity)').Replace('$DLLPATH$', '$(DllFullPath)'))" />
</ItemGroup>
<WriteLinesToFile File="$(OutputScriptPath)" Lines="@(ProcessedLines)" Overwrite="true" />
<Message Text="Generated LoadPlugin.scr with contents:" Importance="High" />
<Message Text="@(ProcessedLines)" Importance="High" />
</Target>
</Project>
What is important about the csproject
is that it generates the launchSettings.json
and LoadPlugin.scr
files on the fly because these two files must contain the absolute path of your plugin DLL, by taking into the consideration of where the $(ProjectDir)
is located. So after your project is compiled, the VS 2022 will create/update the launchSettings.json
and LoadPlugin.scr
so that they insert in proper absolute path.
The LoadPlugin.scr.template
#
NETLOAD "$DLLPATH$"
Note that in the template
file, we don’t hardcode the DLLPath
, instead we will let the after build event in VS 2022 to generate it when we create the LoadPlugin.scr
file
The Properties\launchSettings.template.json
#
As per the convention with launchSettings.json
, the launchSettings.template.json
must also located in the Properties
folder. Here’s the content of it
{
"profiles": {
"AutoCAD": {
"commandName": "Executable",
"executablePath": "%AutoCADDir%\\acad.exe",
"commandLineArgs": "/ld \"%AutoCADDir%\\AecBase.dbx\" /p \"<<C3D_Metric>>\" /product C3D /b \"{{LaunchDir}}\\LoadPlugin.scr\"",
"workingDirectory": "{{LaunchDir}}\\bin\\Debug"
}
}
}
HookEntry.cs
#
In the code I just alert the user if the relevant sections are entered
using System.Windows;
using Autodesk.AutoCAD.Runtime;
using DummDLL;
[assembly: ExtensionApplication(typeof(HookEntry))]
[assembly: CommandClass(typeof(HookEntry))]
namespace DummDLL
{
public class HookEntry : IExtensionApplication
{
public HookEntry()
{
MessageBox.Show($"Constructor entered,{this.GetHashCode()}");
}
public void Initialize()
{
MessageBox.Show($"Initialize method entered,{this.GetHashCode()}");
}
public void Terminate()
{
}
[CommandMethod(nameof(RunCommand2))]
public void RunCommand2()
{
MessageBox.Show($"{nameof(RunCommand2)},{this.GetHashCode()}");
}
[CommandMethod(nameof(RunCommand))]
public void RunCommand()
{
MessageBox.Show($"{nameof(RunCommand)},{this.GetHashCode()}");
}
}
}
The .gitignore
file #
We want to store our code in the git repository, and we want to make sure that the stage area is clean when we are done, that’s why we must exclude the launchSettings.json
and LoadPlugin.scr
from the source control. This won’t be a problem for other developers who clone our repo because as mentioned, the After Build Event
in Visual Studio will be able to recreate the necessary files.
/Properties/launchSettings.json
LoadPlugin.scr
Recap #
So that’s it. Once you do the machine/AutoCAD setup, and you put the above files into appropriate location, and you compile the csproject, and you press F5
in the Visual Studio, your plugin will automatically be loaded, and you will be greeted with MessageBox
indicating that you have successfully setup your plugin for development and debugging.