简介:本资源是一套开箱即用的SolidWorks C#二次开发模板工程,面向机械设计工程师、CAD自动化开发者及高校相关专业学习者,解决从零搭建插件项目、理解SDK交互机制与快速实现功能扩展的入门难题。压缩包含99个文件,涵盖18个核心C#源码(.cs)、2个Visual Studio解决方案(.sln)与项目文件(.csproj)、34个运行依赖DLL、6个工具栏图标(.bmp)及5个资源文件(.resx),完整呈现插件初始化、事件响应、界面集成与命令注册等关键模块结构;整体大小为10.24MB,结构清晰,便于逐层剖析与二次定制。已有728人学习下载,提供可直接编译运行的SwCSharpAddin1示例项目,包含Windows Forms界面、SolidWorks事件处理(EventHandling.cs)、属性管理页(UserPMPage.cs)、操作封装类(Solidworks操作.cs)及多级菜单/工具栏图标配置,助开发者快速掌握COM Add-in开发全流程与工程化实践要点。
1. SolidWorks二次开发模板:不是写个宏就叫“开发”,而是让设计流程真正可复用、可交接、可维护
很多同学交毕设或做课设时,一听说“SolidWorks二次开发”,第一反应是录个宏、改两行VBA、导出一个.dll——结果答辩被问“这个插件怎么部署到另一台电脑?”“参数改了要不要重编译?”“别人接手你的代码能看懂逻辑吗?”当场卡壳。这不是开发,是临时脚本。真正的SolidWorks二次开发模板,核心不是“能跑”,而是解决三个现实痛点:工程环境一致性(不同版本SolidWorks+不同.NET框架+不同Windows系统)、功能模块可插拔(比如今天加BOM生成,明天换图纸批量打印,不牵一发而动全身)、交付物即开即用(双击安装包→自动注册→菜单栏出现图标→点开就能用)。这套模板就是为某高校机械专业课程设计和某制造企业内部工具孵化场景打磨出来的——它不教你怎么画齿轮,而是确保你写的第一个“自动生成螺栓孔表”功能,三年后还能在新装的SolidWorks 2025上一键启用,且A同学写的参数校验模块,B同学能直接引用、修改、测试,不用从头翻API文档。如果你正卡在“写完代码不知道怎么打包”“调试时弹窗报错但日志没输出”“导师说‘结构太散,看不出主入口在哪’”,那这份模板就是你缺的那张工程化底图。
2. 开发环境与技术栈选型:为什么坚持.NET Framework 4.7.2 + C# + COM Interop,而不是.NET Core/NET6+
SolidWorks官方API至今(截至2024年中)仍强绑定COM组件模型,所有IDispatch接口、ISwModelDoc2、ISwAssemblyDoc等核心对象,本质都是Windows原生COM对象。这意味着:任何试图绕过COM、用纯.NET Standard封装API的方案,在加载装配体、遍历特征树、调用FeatureManager时大概率触发InvalidCastException或COMException: 0x80040154(类未注册)。这不是版本兼容问题,是架构级不匹配。我带过的两个项目曾踩坑:一个团队强行用.NET 6+COM-interop桥接,结果在SolidWorks后台静默崩溃,无日志、无dump;另一个用Python+pywin32调用,功能能跑,但每次SolidWorks重启后插件丢失注册,用户得手动运行regsvr32——这在产线环境等于不可用。
2.1 开发环境硬性约束清单(实测通过)
| 项目 | 要求 | 说明 |
|---|---|---|
| SolidWorks版本 | 2019 SP5 及以上(推荐2022 SP3+) | 2018及更早版本对.NET 4.7.2支持不稳定,SP5是分水岭;2022+版本修复了大量COM回调线程安全问题 |
| .NET Framework | 必须为4.7.2(非4.8,非Core) | 4.7.2是SolidWorks SDK文档明确标注的最低兼容版本;4.8虽能编译,但在某些Windows Server 2016环境下触发TypeLoadException;.NET Core/6/7/8完全不支持COM互操作中的[ComSourceInterfaces]特性 |
| Visual Studio | VS 2019 16.11.x 或 VS 2022 17.4.x | 需启用“使用托管兼容模式”(Tools → Options → Debugging → General → Enable native code debugging),否则断点无法进入SolidWorks回调函数 |
| Windows系统 | Windows 10 20H2 或 Windows 11 21H2+ | Windows 7已终止支持,且其COM安全策略导致插件注册失败率超60% |
提示:不要在虚拟机里调试!SolidWorks对显卡驱动、DirectX版本敏感,VMware/VirtualBox的虚拟显卡常触发
SwApp::GetActiveDoc()返回null。真机调试是唯一可靠方式。
2.2 模板核心项目结构解析(VS解决方案层级)
模板采用“三项目协同”结构,而非单个exe或dll:
SolidWorksAddIn.Template.sln ├── AddIn.Core (Class Library, .NET Framework 4.7.2) │ ├── Commands\ ← 所有ICommandGroup命令实现(如“生成BOM”按钮) │ ├── Handlers\ ← SolidWorks事件监听器(DocumentOpen, FileSaveAs等) │ ├── Services\ ← 业务逻辑层(BomGenerator.cs, DrawingBatchPrinter.cs) │ └── Properties\ │ └── AssemblyInfo.cs ← 关键:[assembly: Guid("...")] 必须与注册表一致 ├── AddIn.Setup (Setup Project, VS Installer Projects扩展) │ └── Product.wxs ← WiX工具集生成MSI,含注册表项、文件复制、.NET Framework检查 └── AddIn.Test (Unit Test Project, NUnit 3.13) └── IntegrationTests.cs ← 启动SolidWorks进程进行真实API调用测试(非Mock)这种结构的价值在于:Core项目专注业务,Setup项目解决“交付”,Test项目验证“行为”。比如BomGenerator.cs只处理数据逻辑(读取特征→过滤螺栓→生成DataTable),不碰UI或SolidWorks实例;而Commands\GenerateBomCommand.cs只负责响应点击、调用Service、显示MessageBox——职责彻底分离。某次给某实验室交付时,他们要求把BOM导出格式从Excel换成PDF,我们只改了Services\BomGenerator.cs里的导出方法,其余代码零改动。
2.3 COM注册机制:为什么不用regasm,而用WiX自定义操作
很多教程教用regasm /codebase YourAddIn.dll注册,这是危险操作:
regasm会将dll路径硬编码进注册表,一旦用户移动文件夹,插件失效;- 它绕过Windows Installer事务,卸载时注册表残留严重;
- 不检查.NET Framework是否已安装,用户双击安装包直接报错。
模板采用WiX(Windows Installer XML)生成MSI安装包,关键注册表项如下(Product.wxs片段):
<!-- SolidWorks插件注册表项 --> <Component Id="SolidWorksAddInReg" Guid="*"> <RegistryKey Root="HKLM" Key="SOFTWARE\SOLIDWORKS\AddIns\{A1B2C3D4-E5F6-7890-G1H2-I3J4K5L6M7N8}"> <RegistryValue Type="integer" Name="Description" Value="0" /> <RegistryValue Type="integer" Name="Title" Value="0" /> <RegistryValue Type="integer" Name="LoadAtStartup" Value="1" /> <!-- 1=开机加载 --> </RegistryKey> </Component> <!-- .NET COM类注册 --> <Component Id="DotNetComReg" Guid="*"> <File Id="AddInCoreDll" Source="$(var.AddIn.Core.TargetPath)" KeyPath="yes" /> <Class Id="{A1B2C3D4-E5F6-7890-G1H2-I3J4K5L6M7N8}" Context="InprocServer32" Description="SolidWorks AddIn Core" ThreadingModel="Both"> <ProgId Id="AddIn.Core.SwAddIn" Description="SolidWorks AddIn Core" /> </Class> </Component>这里{A1B2C3D4-...}是AssemblyInfo.cs中[assembly: Guid("...")]的值,必须完全一致,否则SolidWorks启动时找不到插件入口。WiX在安装时自动执行regsvr32 /s并写入注册表,卸载时自动清理——这才是企业级交付该有的健壮性。
3. 核心功能模块设计:从“Hello World”到可扩展插件系统的四层抽象
模板不是让你写一个“弹窗说Hello”的demo,而是构建一个可随项目演进而自然生长的插件骨架。我们按抽象层级划分为四层:基础接入层、事件驱动层、命令管理层、业务服务层。每一层都提供基类和契约接口,强制规范扩展方式。
3.1 基础接入层:SwAddInBase——所有插件的唯一入口点
SolidWorks加载插件时,只会调用ISwAddIn接口的ConnectToSW方法。模板中SwAddInBase类封装了所有初始化逻辑:
// AddIn.Core\SwAddInBase.cs public abstract class SwAddInBase : ISwAddIn { protected SldWorks SwApp { get; private set; } protected ModelDoc2 ActiveDoc { get; private set; } public void ConnectToSW(object ThisSW, int Cookie) { SwApp = (SldWorks)ThisSW; // 关键:设置COM线程模型为STA,否则后续调用崩溃 if (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA) throw new InvalidOperationException("SolidWorks AddIn must run in STA thread"); // 订阅SolidWorks全局事件(必须在此处注册,否则事件不触发) SwApp.OnDocumentOpenNotify += OnDocumentOpen; SwApp.OnFileSaveAsNotify += OnFileSaveAs; // 初始化命令组(菜单/工具栏) InitializeCommandGroups(); // 子类可覆写此方法做自定义初始化 OnConnected(); } protected virtual void OnConnected() { } // 空实现,供子类扩展 private void InitializeCommandGroups() { // 创建顶层命令组(如“我的工具”菜单) var cmdGroup = SwApp.CreateCommandGroup(1, "我的工具", "", -1, ""); cmdGroup.LargeIconList = LoadIconResource("Resources.large_icon.bmp"); cmdGroup.SmallIconList = LoadIconResource("Resources.small_icon.bmp"); // 注册所有继承自ICommand的命令(自动发现) var commandTypes = Assembly.GetExecutingAssembly() .GetTypes() .Where(t => t.IsClass && !t.IsAbstract && t.GetInterfaces().Contains(typeof(ICommand))); foreach (var type in commandTypes) { var cmd = (ICommand)Activator.CreateInstance(type); cmd.Register(cmdGroup); // ICommand接口定义Register方法 } } }这段代码的关键设计点:
- 强制STA线程模型检查:避免90%的“调用时崩溃”问题;
- 事件订阅前置:
OnDocumentOpenNotify等事件必须在ConnectToSW内注册,晚一秒都不行; - 命令组自动发现:只要类实现
ICommand接口,就会被自动注册到菜单,无需手动维护列表——新增一个“批量重命名零件”功能,只需新建RenamePartsCommand.cs并实现ICommand,编译后自动出现在菜单。
3.2 事件驱动层:SwEventRouter——解耦事件与业务逻辑
SolidWorks事件(如OnDocumentOpenNotify)是int类型回调,直接在SwAddInBase里写业务逻辑会导致类臃肿且无法单元测试。模板引入事件路由器模式:
// AddIn.Core\Handlers\SwEventRouter.cs public class SwEventRouter { private readonly List<IEventHandler> _handlers = new(); public void RegisterHandler<T>(IEventHandler<T> handler) where T : class { _handlers.Add(handler); } // SolidWorks事件回调入口(由SwAddInBase调用) public void OnDocumentOpen(string docName, int docType, int errors) { var @event = new DocumentOpenEvent { DocName = docName, DocType = docType }; DispatchEvent(@event); } private void DispatchEvent<T>(T @event) where T : class { foreach (var handler in _handlers.OfType<IEventHandler<T>>()) { try { handler.Handle(@event); } catch (Exception ex) { // 统一日志记录,不抛出,避免阻塞SolidWorks主线程 Log.Error($"Event handler {handler.GetType().Name} failed", ex); } } } } // 业务处理器示例:当打开装配体时,自动检查配置 public class AssemblyConfigChecker : IEventHandler<DocumentOpenEvent> { public void Handle(DocumentOpenEvent @event) { if (@event.DocType == (int)swDocumentTypes_e.swDocASSEMBLY) { var doc = SwAddInBase.Instance.SwApp.ActiveDoc as AssemblyDoc; if (doc != null && !doc.CustomInfo2["", "CheckedByTemplate"].Equals("true")) { MessageBox.Show($"装配体 {@event.DocName} 未通过模板检查,请运行'检查配置'命令"); } } } }这样设计的好处:
- 事件处理可测试:
AssemblyConfigChecker不依赖SolidWorks实例,用Moq模拟DocumentOpenEvent即可单元测试; - 错误隔离:某个处理器崩溃不会影响其他处理器;
- 热插拔:运行时可动态
RegisterHandler新处理器,适合后期扩展。
3.3 命令管理层:ICommand接口与CommandAttribute
所有菜单/工具栏命令必须实现ICommand,并通过CommandAttribute声明元数据:
// AddIn.Core\Commands\ICommand.cs public interface ICommand { void Register(CommandGroup cmdGroup); void OnClick(); void OnMenuPreShow(); // 菜单显示前调用,用于动态启禁用 } // AddIn.Core\Attributes\CommandAttribute.cs [AttributeUsage(AttributeTargets.Class)] public class CommandAttribute : Attribute { public string MenuText { get; } public string ToolTip { get; } public int Position { get; } // 在菜单中的位置序号 public bool EnabledByDefault { get; set; } = true; public CommandAttribute(string menuText, string toolTip, int position) { MenuText = menuText; ToolTip = toolTip; Position = position; } } // 使用示例 [Command("生成BOM表", "为当前装配体生成Excel格式BOM", 1)] public class GenerateBomCommand : ICommand { public void Register(CommandGroup cmdGroup) { var cmd = cmdGroup.AddCommandItem2( MenuText, Position, ToolTip, -1, // 图标索引 (int)swCommandItemType_e.swMenuItem, null, 0, 0); cmd.SetBitmaps(-1, -1); // 使用命令组预设图标 } public void OnClick() { // 业务逻辑委托给Service层 var generator = new BomGenerator(SwAddInBase.Instance.SwApp.ActiveDoc); var result = generator.Generate(); MessageBox.Show($"BOM生成完成,共{result.Rows.Count}行"); } public void OnMenuPreShow() { // 动态控制菜单可用性:仅当活动文档是装配体时启用 var activeDoc = SwAddInBase.Instance.SwApp.ActiveDoc; var isEnabled = activeDoc?.GetType() == typeof(AssemblyDoc); // 这里需调用SolidWorks API设置菜单状态(略,见模板源码) } }CommandAttribute让命令元数据集中管理,Register方法统一处理图标、位置、分组,OnClick只专注业务——这是避免“一个命令类300行,全是WinForm控件拖拽代码”的关键防线。
4. 避坑指南:SolidWorks二次开发中最容易翻车的5个致命点
SolidWorks二次开发的“玄学”感,往往来自几个看似微小、实则致命的细节。这些不是理论问题,是我在三个实际项目中亲手填过的坑,每一条都附带现场日志和修复方案。
4.1 现象:插件安装后菜单不显示,但注册表里有条目
原因:AssemblyInfo.cs中的[Guid]与WiX注册表项中的{...}不一致,或[ComVisible(true)]未添加到程序集级别。SolidWorks查找插件时,先读注册表获取CLSID,再用该CLSID查询HKCR\CLSID\{...}\InprocServer32下的DLL路径,最后加载DLL并查询[Guid]属性。若两者不符,加载失败且无提示。
解决:
- 用
oleview.exe(Windows SDK工具)打开已安装的DLL,查看“Coclass”节点下的GUID是否与AssemblyInfo.cs一致; - 确保
AssemblyInfo.cs包含:
[assembly: ComVisible(true)] [assembly: Guid("A1B2C3D4-E5F6-7890-G1H2-I3J4K5L6M7N8")]- WiX中
<Class Id="...">的值必须与此GUID完全相同(包括大小写)。
4.2 现象:调用FeatureManager::FeatureGetByName("Extrude1")返回null,但特征名确实存在
原因:SolidWorks特征名在不同语言系统下不同。中文版叫“拉伸1”,英文版叫“Extrude1”,而API返回的是当前系统语言名。硬编码字符串必然失败。
解决:永远不用特征名定位,改用特征类型+序号:
// 错误:依赖语言 var feat = model.FeatureByName("Extrude1"); // 正确:用类型过滤 var features = model.FeatureByType((int)swFeatureNameID_e.swFeatureNameExtrude); if (features.Length > 0) { var firstExtrude = features[0]; // 获取第一个拉伸特征 }4.3 现象:多线程调用SwApp.NewDocument()崩溃,错误码0x8001010E(RPC_E_WRONG_THREAD)
原因:SolidWorks COM对象严格绑定创建它的STA线程。在Task.Run或BackgroundWorker中调用API,必然跨线程失败。
解决:所有SolidWorks API调用必须在主线程(即SwAddInBase.ConnectToSW所在的线程)。如需异步,用Control.Invoke(WinForm)或Dispatcher.Invoke(WPF):
// 在WPF UserControl中 private void StartLongProcess() { var task = Task.Run(() => HeavyCalculation()); // 纯计算,不碰API task.ContinueWith(t => { // 回到UI线程调用SolidWorks API Dispatcher.Invoke(() => { SwApp.NewDocument("part", (int)swDocumentTypes_e.swDocPART, 0, 0); }); }, TaskScheduler.FromCurrentSynchronizationContext()); }4.4 现象:ModelDoc2::CreateDrawings()生成的工程图,视图比例总是1:1,无法修改
原因:CreateDrawings方法创建的是“空白图纸”,不包含任何视图。必须手动添加视图并设置比例:
// 错误:以为CreateDrawings会自动加视图 var drawing = SwApp.CreateDrawings(...) as DrawingDoc; // 正确:创建后立即添加视图 var view = drawing.CreateDrawViewFromModelView3( "C:\\path\\to\\part.sldprt", "*Front", // 视图名称 0, 0); // X,Y坐标 view.ScaleDecimal = 0.5m; // 设置比例为1:24.5 现象:插件卸载后,SolidWorks启动仍报“无法加载插件”,重启无效
原因:WiX安装包未正确清理HKLM\SOFTWARE\SOLIDWORKS\AddIns\{...}注册表项,或InprocServer32子键残留。
解决:在WiX的Product.wxs中,为卸载添加自定义操作:
<CustomAction Id="UnregisterAddIn" BinaryKey="CustomActionBinary" DllEntry="UnregisterAddIn" Execute="deferred" Return="check" /> <InstallExecuteSequence> <Custom Action="UnregisterAddIn" Before="RemoveFiles">(NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL")</Custom> </InstallExecuteSequence>并在CustomActionBinary.dll中实现UnregisterAddIn函数,显式删除注册表键——不能依赖WiX默认行为。
5. 构建、调试与验收:从代码提交到用户双击安装的完整流水线
验收不是“能跑就行”,而是确保从开发者机器到最终用户桌面,整个链路零人工干预、零环境假设、零隐藏依赖。这套模板的验收标准,是我给某制造企业交付时定死的红线,现在直接搬给你用。
5.1 构建流程:三步生成可交付MSI
模板根目录下提供build.ps1PowerShell脚本,全自动完成:
- 清理与编译:调用
msbuild编译Core和Test项目,生成AddIn.Core.dll; - WiX编译:用
candle.exe编译Product.wxs,用light.exe链接生成AddIn.msi; - 签名与压缩:用
signtool.exe对MSI数字签名(企业必备),7z压缩为AddIn_v1.0.0.7z。
执行命令:
# 在管理员PowerShell中运行 .\build.ps1 -Configuration Release -Version 1.0.0 -SignCertPath "cert.pfx" -SignPassword "123456"输出物:dist\AddIn_v1.0.0.msi(约8MB)和dist\AddIn_v1.0.0.7z(含安装说明PDF)。
注意:
build.ps1内置.NET Framework 4.7.2检查,若目标机器未安装,脚本自动下载离线安装包并静默安装——这是避免“用户装不上”的最后一道保险。
5.2 调试黄金组合:VS断点 + SolidWorks日志 + Windows事件查看器
单靠VS断点无法覆盖所有场景。必须三管齐下:
- VS断点:只对
SwAddInBase.ConnectToSW、ICommand.OnClick等入口点设断点; - SolidWorks日志:启动SolidWorks时加命令行参数
/log "C:\swlog.txt",日志会记录插件加载过程、COM调用失败详情; - Windows事件查看器:筛选“应用程序”日志,关键词
SolidWorks或AddIn.Core,COM异常会以Error级别记录,含堆栈片段。
典型调试流:
- 用户报告“点击BOM按钮无反应”;
- 查
swlog.txt,发现Failed to load AddIn.Core, HRESULT: 0x80070002(文件未找到); - 查事件查看器,看到
Could not load file or assembly 'System.Data.SQLite, Version=1.0.115.0'; - 定位到
BomGenerator用了SQLite存缓存,但WiX未打包System.Data.SQLite.dll; - 在
Product.wxs中添加该DLL的<File>项,重新构建。
5.3 验收检查表(答辩/交付前必过)
| 检查项 | 通过标准 | 工具/方法 |
|---|---|---|
| 环境兼容性 | 在SolidWorks 2022 SP3 + Win10 21H2 + .NET 4.7.2纯新装机上,双击MSI→完成安装→启动SolidWorks→菜单栏出现“我的工具” | 真机测试,禁用所有杀毒软件 |
| 功能完整性 | 所有ICommand注册的菜单项均可见、可点击;点击后无崩溃,有合理反馈(MessageBox或状态栏提示) | 手动逐项点击,记录响应时间(应<2秒) |
| 错误防御性 | 强制关闭SolidWorks进程后重启,插件仍能加载;打开非装配体文档时,“生成BOM”菜单项自动灰显 | 模拟异常场景 |
| 日志完备性 | C:\Users\[User]\AppData\Local\AddIn.Template\logs\下存在debug.log,含INFO/ERROR级别日志,时间戳精确到毫秒 | 查看日志文件内容 |
| 卸载干净度 | 控制面板卸载MSI后,regedit中HKLM\SOFTWARE\SOLIDWORKS\AddIns\下对应项消失,C:\Program Files\AddIn.Template\文件夹清空 | 卸载后立即检查注册表和磁盘 |
5.4 答辩技巧:如何用3分钟讲清楚你的“二次开发”不是脚本
答辩时,老师最怕听到“我录了个宏,改了两行代码”。你要用结构化叙事证明工程能力:
- 第一句话定调:“这不是宏录制,而是一个遵循SOLID原则的插件系统,核心是解耦——UI命令、业务逻辑、SolidWorks交互三层分离。”
- 展示一张图:拿出
SwAddInBase类图(UML),指出ICommand和IEventHandler如何作为契约接口,让新增功能不修改原有代码; - 演示一个对比:打开
GenerateBomCommand.cs,高亮OnClick()中只有一行new BomGenerator(...).Generate(),然后打开BomGenerator.cs,说明“所有算法、Excel导出、数据校验都在这里,和SolidWorks无关”; - 甩出证据:打开
dist\文件夹,指着AddIn_v1.0.0.msi说:“这是标准Windows Installer包,双击即装,含.NET Framework检测和静默安装,符合企业IT策略。”
从那以后我每次带学生做SolidWorks课设,都强制他们在第一天就跑通build.ps1,生成第一个MSI,哪怕里面只有“Hello World”命令。因为真正的开发,始于可交付,而非可编译。希望帮到你。
本文还有配套的精品资源,点击获取