基于COM Interop的AutoCAD二次开发如何在程序内部加载dll文件
前言
我的上一篇文章已经介绍了如何通过COM Interop创建一个独立的AuoCAD二次开发应用程序。但是在开发过程感觉有些吃力,一方面是对这种开发方式运用还不熟练,另一方面是这种方法网上的资料真的很少。而基于.net api来开发AutoCAD的资料却很多,但是这种方式只能编译出一个dll文件,用起来不是很方便。于是我灵机一动,能否用COM Interop方式做主界面,然后在程序内部调用.net api方式生成的dll文件呢?嘿嘿,好一个围魏救赵,曲线救国…
一、开发工具
我使用的是 Visual Studio 2017 + AutoCAD 2019 + .NET Framework 4.7.2。版本不是重点,可根据自己实际来选择。
二、通过.net api创建类库项目
(一)新建项目
打开Visual Studio,新建一个类库(.Net Framework)项目。

(二)添加引用
- 选择创建的项目然后右键→添加→引用→浏览,在弹出的窗口中找到AutoCAD安装目录,依次添加
accoremgd、AcCui、acdbmgd、acmgd这4个dll文件。注意:我用的是 AutoCAD 2019对应这4个类库文件,其它低版本可能只有其中3个,可自行去网上查询。

- 选中这4个引用,在属性栏中将复制本地属性改为false。

- 选择创建的项目然后右键→属性→调试,启动操作一栏选择启动外部程序,点击浏览并定位到AutoCAD安装目录,选择AutoCAD主程序“acad.exe”文件。

(三)代码编写
回到Class1.cs代码编辑界面,添加如下代码:
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.EditorInput;
namespace DrawCircle
{
public class Class1
{
[CommandMethod("Drawcircle")]//注册一个CAD命令
public void drawcircle()
{
Transaction trans = HostApplicationServices.WorkingDatabase.TransactionManager.StartTransaction();
BlockTable bt = (BlockTable)trans.GetObject(HostApplicationServices.WorkingDatabase.BlockTableId, OpenMode.ForRead);
BlockTableRecord btr = (BlockTableRecord)trans.GetObject(HostApplicationServices.WorkingDatabase.CurrentSpaceId, OpenMode.ForWrite);
Circle circle = new Circle(new Point3d(0, 0, 0), Vector3d.ZAxis, 100);
btr.AppendEntity(circle);
trans.AddNewlyCreatedDBObject(circle, true);
trans.Commit();
trans.Dispose();
}
}
}
(四)运行调试
在Visual Studio界面点击运行按钮,此时AutoCAD会自动打开。在CAD界面输入命令“netload”,定位到本项目“…\bin\Debug”文件夹,选择生成的"DrawCircle.dll"文件,然后点击打开按钮。在弹出的安全性窗口中选择“始终加载”。在CAD界面输入命令“Drawcircle”,然后就能在CAD中生成一个如下图所示的圆。至此,已经成功通过.net api生成了dll文件,且该dll文件运行无误。


三、通过COM Interop窗体应用
(一)新建项目
打开Visual Studio,新建一个Windows 窗体应用(.Net Framework)。

(二)添加引用
选择创建的项目然后右键→添加→引用,在弹出的窗口中选择COM目录下的AutoCAD 20xx Type Library。我安装的是AutoCAD 2019,所以这里就选AutoCAD 2019 Type Library。注意:这里出现了多个AutoCAD 2019 Type Library选项,勾选第一个就行。然后点击确定。

(三)窗体布局
由于本次只是为了验证在程序内部加载dll功能,因此窗体只是简单的添加了两个Button,如下图:

(四)代码编写
- 添加引用AutoCAD 命名空间(
using AutoCAD),申明3个全局变量(acadApp、doc、modelSpace)为后面开发做准备。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using AutoCAD;
namespace LoadDllFile
{
public partial class Form1 : Form
{
private AcadApplication acadApp;
private AcadDocument doc;
private AcadModelSpace modelSpace;
public Form1()
{
InitializeComponent();
}
}
}
- 回到窗体设计界面,双击“Start”按钮,在弹出的button1_Click事件中添加如下代码:
private void button1_Click(object sender, EventArgs e)
{
if (!isCADopen())//如果AutoCAD没有打开
{
OpenAutoCAD();
}
if (acadApp == null)
{
acadApp = (AcadApplication)System.Runtime.InteropServices.Marshal.GetActiveObject("AutoCAD.Application.23");//避免acadApp=null而报错
}
int count = acadApp.Documents.Count;//如果用户只打开了CAD但是没有打开文档(通过count值判断),则需要新建一个空白文档
if (count == 0)
{
doc = acadApp.Documents.Add("acad.dwt");
}
else
{
doc = acadApp.ActiveDocument;
}
int secureLoad = (int)doc.GetVariable("SECURELOAD");
if (secureLoad != 0)
{
DialogResult dr = MessageBox.Show($"当前系统安全级别过高(安全级别:{secureLoad}),无法自动加载插件。请确认是否降低安全级别?", "提示",
MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
if (dr == DialogResult.Yes)
{
doc.SendCommand("(setvar \"SECURELOAD\" 0) ");
}
else
{
goto remark;
}
}
string path = @"E:\Demo\DrawCircle\DrawCircle\bin\Debug\DrawCircle.dll";
string dllPath = path.Replace("\\", "\\\\");
string command = $"(command \"netload\" \"{dllPath}\")" + " ";
doc.SendCommand(command);
doc.SendCommand("DRAWCIRCLE" + "\n");
acadApp.ZoomAll();
remark:;
}
public bool isCADopen()
{
Process[] processes = Process.GetProcesses();
foreach (Process process in processes) // 遍历每个进程,检查进程名称是否为 "acad(AutoCAD的进程名)
{
if (process.ProcessName.Equals("acad", StringComparison.OrdinalIgnoreCase)) return true;
}
return false;
}
public AcadApplication OpenAutoCAD()
{
try
{
acadApp = (AcadApplication)System.Runtime.InteropServices.Marshal.GetActiveObject("AutoCAD.Application.23"); // 版本23代表AutoCAD 2019
return acadApp;
}
catch
{
acadApp = new AcadApplication(); // 未找到实例则启动新进程
acadApp.Visible = true;
acadApp.WindowState = AutoCAD.AcWindowState.acMax;//窗口最大化
return acadApp;
}
}
简单说明:
secureLoad表示当前CAD的安全级别,其值0、1、2分别代表“关”、“中等”、“高”。

当secureLoad的值为1或2时,调用“netload”命令加载dll文件时会弹出系统提示窗口,阻断程序运行。因此需要提前将secureLoad的值设置为0。

path表示dll文件的路径,但是在CAD中这个路径中的“\”需要转义,dllPath为转义后的路径。- 成功加载dll后,通过
SendCommand方法调用dll中注册的方法。
- 回到窗体设计界面,双击“Exit”按钮,在弹出的button2_Click事件中添加如下代码:
private void button2_Click(object sender, EventArgs e)
{
Application.Exit();
}
(五)运行调试
在Visual Studio界面点击运行按钮,系统会弹出我们自定义的窗体。点击“Start”按钮,此时:
- 若AutoCAD没有打开,则程序会先打开AutoCAD并新建一个空白文档;
- 若AutoCAD已经打开,则程序会直接执行后续操作。
执行途中程序会弹出一个MessageBox用于提示用户是否同意降低安全级别,如下图:

- 若用户点击“是”,则会成功加载指定的dll文件并运行,最终在模型空间绘制一个圆;
- 若用户点击“否”,则不会执行任何操作。

至此,已经成功完成在程序内部自动加载dll文件。其核心操作是先通过 SendCommand方法将secureLoad值设置为0。然后通过 SendCommand方法调用AutoCAD中的“netload”指令。
值得一提的是,DeepSeak给出了另一种改变secureLoad值方法,不需要使用SendCommand,代码如下:
acadApp.ActiveDocument.SetVariable("SECURELOAD", 0);
然而实际运行中会报错,目前还没找到原因(也许是AI自己编造的方法?),有兴趣的童鞋可以帮忙研究下。


534

被折叠的 条评论
为什么被折叠?



