
做过EPLAN二次开发的朋友应该都有同感真正劝退你的往往不是API那点事而是你连一个插件都跑不起来。Visual Studio 2019装好了EPLAN也开着了可项目一建就是一堆引用错误好不容易编译通过插件加载进EPLAN却一点反应都没有想调试又不知道断点该怎么挂最后只能在代码里堆MessageBox碰运气。这篇文章就是围绕这个痛点来的。我会从EPLAN二次开发的整体思路说起重点讲Visual Studio 2019环境配置、插件项目结构、把插件跑进EPLAN的完整流程以及最常见的调试手段和排障套路。目标很直接让一个从没碰过EPLAN API的工程师花一个下午就能把第一个插件跑起来并且知道下一步该往哪使劲。1. 二次开发到底在开发什么先搞懂EPLAN的API结构1.1 EPLAN开放了哪几层能力接触EPLAN二次开发前我先花点时间把它的API体系理清楚。EPLAN的API是基于.NET的一套程序集集合用C#写插件是主流方式。从功能角度看这些API大致可以分成三层第一层是应用框架层命名空间类似Eplan.EplApi.ApplicationFramework。它管的是EPLAN这个程序本身的骨架比如菜单怎么挂、命令怎么注册、窗口怎么弹。你做插件的第一步基本都会碰到它因为你要告诉EPLAN“我有个功能我想放到某个菜单下”这就要通过应用框架层的接口去注册。第二层是数据模型层命名空间Eplan.EplApi.DataModel。这是EPLAN二次开发里最值钱的部分图纸里的页、符号、部件、连接、端子、PLC变量、线号等信息都是通过这里的数据对象暴露出来的。我们常说的“批量改线号”“批量生成报表”“自动检查端子连接”本质上都是在操作这一层的对象。第三层是基础服务层比如Eplan.EplApi.HEServices、Eplan.EplApi.Base。它更像一个工具箱提供了项目交互、设置读写、查找替换、日志、进程通信这些能力。写插件时很多“绕不开的活”都在这一层比如你要遍历当前项目里的所有页面要么通过HEServices拿项目对象要么通过Base里的工具类去操作。理解这个分层你就知道学EPLAN二次开发的大致路线先学会用应用框架层把功能挂到界面上再学数据模型层去读写图纸对象最后用基础服务层解决项目交互的细节。环境配置出问题的根源也往往是我们没有搞清楚自己写的代码到底依赖了哪个程序集、哪个版本。1.2 三种常见的开发形态怎么选EPLAN二次开发不像很多软件只有一种套路它至少有三条路可以走。第一种是脚本。EPLAN自带脚本环境你可以在EPLAN里直接写C#脚本常用在临时处理一批数据、做一个一次性的小工具。优点是上手极快不需要编译、不需要配置VS缺点是工程化能力弱没法做复杂的UI也没法方便地调试。适合场景领导扔给你一个下午要搞定的小批量操作。第二种是插件Addin也就是本文要讲的主线。插件是一个类库工程编译成DLL后由EPLAN进程加载。它和EPLAN跑在同一个进程里可以直接操作菜单、弹窗、数据模型能力最完整。日常使用的效率工具、自动化功能基本都是用插件实现的。第三种是外部程序。它独立于EPLAN进程运行通过EPLAN提供的接口去操作项目。优点是不占用EPLAN授权、可以做服务化部署缺点是开发和调试门槛高和EPLAN进程的通信也比插件复杂。适合场景后台批量处理、定时任务、Web服务集成。新人入门我强烈建议从插件开始。原因很简单插件是进程内运行调试时断点可以直接命中你能亲眼看到代码执行过程里每个变量的值这对理解API对象模型帮助巨大。很多人一上来就做外部程序结果连进程通信都调不通最后连API的学习热情都搭进去了。2. Visual Studio 2019环境配置从建项目到引用EPLAN程序集2.1 版本搭配与目标框架怎么选Visual Studio 2019一共分Community、Professional、Enterprise三个大版本做EPLAN插件用Community社区版就够了免费且功能不缩水。安装的时候记得勾选“使用C的桌面开发”旁边的“.NET桌面开发”工作负载因为我们要写的是C#类库这个工作负载会带上C#编译器、项目模板和调试器。真正容易踩坑的是目标框架的选择。EPLAN的API程序集是基于.NET Framework开发的不是.NET Core、不是.NET 5/6/7/8。所以新建项目时一定要选“类库(.NET Framework)”而不是“类库(.NET Core)”这种模板。目标框架一般选.NET Framework 4.7.2或4.8具体以你安装的EPLAN版本要求为准。我见过不少人在这里选成了.NET 6结果引用EPLAN的DLL时直接提示“版本不兼容”项目还没开始就已经结束了。顺带说一句EPLAN的二次开发文档里通常会写明它支持的.NET Framework版本装完EPLAN后在安装目录下的“帮助”或“文档”文件夹里能找到动手前先翻一翻比你瞎猜目标框架靠谱得多。2.2 引用EPLAN程序集的具体步骤新建好类库项目后下一步就是把EPLAN的API程序集引用进来。EPLAN安装完成后API DLL默认在安装目录下的Bin文件夹里典型路径像这样C:\Program Files\EPLAN\Platform\2.9.4\Bin不同版本路径会略有差异你可以打开EPLAN的安装目录按版本号找到Bin子目录。进入Bin后能看到一大堆和EPLAN相关的DLL但新手不需要全部引用核心的就这几个程序集文件命名空间主要用途Eplan.EplApi.ApplicationFramework.dllEplan.EplApi.ApplicationFramework菜单、命令、Addin生命周期Eplan.EplApi.Base.dllEplan.EplApi.Base日志、设置、文件操作等基础工具Eplan.EplApi.DataModel.dllEplan.EplApi.DataModel图纸对象、符号、部件、连接等Eplan.EplApi.HEServices.dllEplan.EplApi.HEServices项目、查找、服务类操作Eplan.EplApi.Gui.dllEplan.EplApi.Gui界面控件、进度条、弹窗在Visual Studio里右键项目选择“添加-引用”在弹出的管理器左下角点击“浏览”定位到上面的Bin目录把这几个DLL选进去。这里有一个很关键的设置在引用列表里选中某个EPLAN程序集在属性面板把“复制本地”改成False。如果不改编译时VS会把几百MB的EPLAN DLL原封不动拷到你输出目录里不仅浪费磁盘空间还容易导致运行时加载到错误版本出现一堆莫名其妙的冲突。还有一个更方便的做法在VS的“选项-项目和解决方案-引用路径”里把EPLAN的Bin目录添加进去。以后建新工程、重新添加引用的时候就不需要每次去文件系统里翻路径了直接在“引用”管理器里搜索就能看到EPLAN的DLL。2.3 工程配置里容易被忽略的两个小细节第一是平台目标。EPLAN本身是64位程序所以你的插件项目建议把平台目标设为x64。右键项目选择“属性”在“生成”选项卡里找到“平台目标”下拉选择x64。如果你保留默认的AnyCPU在部分机器上可能会因为位数不匹配导致程序集加载失败这个坑虽然不一定每次都遇到但碰到了就很烦先设置好省得后面排查。第二是强签名。EPLAN在加载插件时有些版本对程序集安全性要求比较敏感。我建议在项目属性-签名选项卡里“为程序集签名”随便选一个.snk文件就行这样程序集有了强名称后续部署到用户机器上时能减少很多安全校验相关的幺蛾子也让插件在EPLAN启动时更容易被信任。除了这两项我还会顺手创建一个Log目录用来放日志文件。在项目里新建一个文本文件或者直接在代码里判断目录存在与否后面调试会方便得多。这个细节看起来不起眼但真到插件跑不起来的时候日志能帮你少掉一半头发。3. 第一个插件长什么样从IEplAddin到菜单按钮3.1 插件的最小结构当你新建完项目、配好引用和平台目标之后就可以写代码了。一个最小可用的EPLAN插件结构上只需要三块内容一个实现IEplAddin接口的入口类、一个用特性声明的命令类、一段把命令挂到菜单上的注册代码。项目文件结构可以参考这样MyEplanPlugin/ ├─ MyAddin.cs ├─ Commands/ │ └─ FirstCommand.cs └─ Properties/ └─ AssemblyInfo.cs编译之前别忘了一个关键步骤在项目里加上程序集特性。EPLAN扫描插件时会识别带有特定特性的程序集。在Properties/AssemblyInfo.cs里加上一行using Eplan.EplApi.ApplicationFramework; [assembly: Eplan.EplApi.ApplicationFramework.EplApi]加上这个特性后EPLAN启动加载程序集时才知道“这个DLL是给EPLAN用的”才会去扫描里面的IEplAddin实现。漏掉这行插件编译一百次都白搭EPLAN压根不会理你。3.2 用IEplAddin入口类注册菜单在MyAddin.cs里写一个类实现IEplAddin接口。这个接口里有两个方法OnRegister和OnUnregister。从名字就能看出来前者是EPLAN加载插件时回调的后者是卸载插件时回调的。using Eplan.EplApi.ApplicationFramework; using Eplan.EplApi.Gui; namespace MyEplanPlugin { public class MyAddin : IEplAddin { public void OnRegister(AddIn addIn) { // 1. 注册命令 CommandRegistry.RegisterMyCommands(); // 2. 把命令挂到菜单上 MenuRegistry.AddMenuItem( 我的菜单, // 菜单文字 FirstCommand, // 命令名称 功能描述, // 状态栏提示 MyEplanPlugin.FirstCommand); // 唯一标识 } public void OnUnregister(AddIn addIn) { CommandRegistry.UnregisterMyCommands(); } } }这里借用了两个静态类CommandRegistry负责命令注册MenuRegistry负责菜单挂载。命令名称就是下面命令类里声明的名字保持字符串一致才能把按钮和动作关联起来。3.3 用特性声明第一个命令命令类需要一个特性标注EPLAN通过反射扫描带这个特性的类自动把命令注册进命令表。好处是命令ID不用你手动去维护一个全局列表框架帮你做了。using Eplan.EplApi.ApplicationFramework; namespace MyEplanPlugin.Commands { [DeclareCommand(FirstCommand)] public class FirstCommand : ICommand { public void Execute() { // 你的业务逻辑先弹个框验证流程通了没有 Eplan.EplApi.Base.CommandInterpreter.ExecuteWithCommandInterpreter( XMA_MESSAGEBOX 我的第一个EPLAN插件跑通了); } } }Execute方法就是按钮被点击后真正执行的那段代码。上面的示例用EPLAN自带的命令解释器弹了一个消息框用来验证从菜单到命令的链路是通的。这套“特性接口”的机制就是EPLAN插件的核心骨架后面你写任何复杂功能都是在这个骨架上不断往Execute里填充业务代码。3.4 生成DLL后怎么让EPLAN加载它编译完成后项目bin目录下会生成MyEplanPlugin.dll以及一堆依赖文件。你可以手动把MyEplanPlugin.dll复制到EPLAN能扫描到的目录也可以通过项目生成事件自动复制。我用的是很省事的方式在项目属性-生成事件-后期生成事件命令行里写一条copy命令copy /Y $(TargetPath) C:\Program Files\EPLAN\Platform\2.9.4\Bin注意两点一是EPLAN的Bin目录通常需要管理员权限才能写入Visual Studio最好以管理员身份运行否则copy会失败二是不同机器EPLAN版本和安装路径不一样这个路径要按你的实际环境去改。复制完DLL后打开EPLAN在“选项-设置-用户-接口-附加模块”里点击“新增”把刚才的DLL添加进去保存后重启EPLAN。如果一切正常菜单栏里就能看到“我的菜单”点下去就能弹窗。提示添加附加模块后一定要重启EPLAN不会热加载。不少新手在这里反复尝试以为是代码问题其实只是没重启而已。4. 插件调试实战让断点真正命中你的代码4.1 附加到进程的标准操作流程写EPLAN插件最常用的调试方式就是附加到进程。流程不复杂但每一步都有讲究。第一步用管理员身份启动Visual Studio。右键VS图标选“以管理员身份运行”这一步很关键。如果VS权限太低附加到进程时可能找不到EPLAN进程或者即使附加上了调试器也没有权限读取内存信息断点表现会很诡异。第二步打开你的插件源码在Execute方法里打一个断点。第三步启动EPLAN并确保插件已经被加载菜单里能看到你的按钮。第四步回到Visual Studio菜单栏选“调试-附加到进程”。在进程列表里找到Eplan.exe点击“附加”。第五步回到EPLAN点击你的菜单按钮。这时候VS会弹到前台断点应该命中然后就可以用F10/F11单步调试了。这套流程本身不复杂但有个顺序问题值得注意先开VS附加再回EPLAN点按钮。一旦你反过来先点了按钮再附加那断点是永远等不到的因为代码已经执行完了。我见过不少同事在这里卡了很久总觉得是自己逻辑写错了其实是调试时序没搞对。4.2 断点为什么不命中查“仅我的代码”如果你严格按上面的顺序操作断点还是不命中那大概率是VS的“仅我的代码”搞的鬼。这是VS默认开启的一个功能调试时它会自动跳过“非用户代码”而EPLAN插件代码在他看来可能是第三方代码所以断点直接被忽略了。解决办法很简单工具-选项-调试-常规在右侧取消勾选“启用仅我的代码”然后重新附加一次。还有一个排查断点的辅助工具调试-窗口-模块。附加上去之后在这个窗口里搜“MyEplanPlugin”看有没有出现你的DLL以及它的“符号状态”是不是“已加载”。如果显示“跳过加载符号”右键选择“加载符号”即可。这种手段能帮你快速定位是程序集没加载还是断点设置本身有误。4.3 日志调试法没有断点时靠什么定位问题断点调试虽好但有些场景它派不上用场。比如插件在EPLAN启动阶段就崩了你的附加操作根本没机会做又比如某个功能要在特定项目条件下才能触发你不好复现。这种时候日志就是最靠谱的调试手段。我会在插件里放一个极简的日志类不引入第三方库省得还要管NuGet依赖。写一个静态Log类输出到用户临时目录下一个固定文件using System; using System.IO; namespace MyEplanPlugin { public static class Log { private static readonly string LogPath Path.Combine(Path.GetTempPath(), MyEplanPlugin, plugin.log); public static void Info(string message) { Write(INFO, message); } public static void Error(string message, Exception ex) { Write(ERROR, message | ex); } private static void Write(string level, string message) { try { Directory.CreateDirectory(Path.GetDirectoryName(LogPath)); File.AppendAllText(LogPath, ${DateTime.Now:yyyy-MM-dd HH:mm:ss} [{level}] {message}{Environment.NewLine}); } catch { // 日志写入失败时不能再抛异常否则会影响插件主流程 } } } }写日志的调用时机也有讲究。一般我会在插件的OnRegister、命令Execute入口、以及每个catch块里记一条日志。这样一旦插件加载失败或者运行报错打开plugin.log就能看到最后一次执行到了哪一行再配合断点做二次定位。这比在EPLAN里疯狂弹窗高效得多也能避免把用户环境搞得一团糟。4.4 DLL被占用导致编译失败怎么办这是每个EPLAN插件开发者都会遇到的经典问题EPLAN开着的时候你改了代码想重新编译结果VS报错“无法将文件复制到xxx因为文件正由另一个进程使用”。原因是EPLAN进程已经加载了你的DLLWindows的文件锁机制不允许覆盖被占用的文件。解决办法不复杂但需要一个好习惯如果你正在调试代码先在EPLAN里把涉及插件的窗口全部关掉然后关掉EPLAN再回VS编译。还有一种灵活的做法把VS的项目输出路径改到一个独立的调试目录比如D:\Temp\MyEplanPluginDebug然后在后期生成事件里用bat脚本先杀掉EPLAN进程再copy文件。参考命令行taskkill /IM Eplan.exe /F 2nul timeout /t 2 /nobreak nul copy /Y $(TargetPath) C:\Program Files\EPLAN\Platform\2.9.4\Bin这个方案能让你在编译时自动关掉EPLAN并部署最新DLL省去手动操作的步骤。但注意taskkill是强杀进程如果有未保存的图纸会丢失所以我通常会在电脑上设个提醒调试前养成随手CtrlS的习惯。5. 常见问题与排查技巧实录做了几年EPLAN二次开发我把新手问得最多的几个问题整理成一张表配合解决方案说清楚现象可能原因解决方案插件菜单在EPLAN里完全看不到DLL没被EPLAN加载缺少AssemblyInfo特性附加模块没添加检查“选项-设置-附加模块”确认[assembly: Eplan.EplApi.ApplicationFramework.EplApi]存在附加到进程时找不到Eplan.exeVS权限不足EPLAN没启动用管理员身份运行VS确认EPLAN已经打开断点设置后不命中VS“仅我的代码”开启调试符号未加载关闭“仅我的代码”用“模块”窗口手动加载符号编译报错文件被EPLAN占用EPLAN进程锁定了DLL关闭EPLAN再编译或用taskkill脚本自动杀进程插件能加载但点击没反应命令名称字符串不匹配命令类没声明特性核对MenuRegistry里的命令字符串与DeclareCommand里的名称是否一致引用的EPLAN DLL版本和当前安装版本不一致参考了其他机器的DLL或者缓存里的旧版本引用Bin目录里的DLL时用“浏览”直接定位当前安装目录清理bin/obj后重新编译这里挑两个特别典型的展开说一下。第一个是“插件能加载但点击没反应”。这个问题十有八九出在命令名称字符串不匹配上。MenurRegistry.AddMenuItem里的第二个参数是命令名称而命令类上的DeclareCommand特性里的名称必须和它完全一致。字符串多一个空格、大小写不一致EPLAN都找不到对应的命令点击时自然没有反应。排查时我会在Execute里先打一个日志看看方法到底有没有被调用再把两个字符串逐个字符比对基本都能定位。第二个是“引用的DLL版本不一致”。EPLAN对程序集版本比较敏感如果你在环境A上引用了EPLAN 2.9的DLL然后把项目拷到EPLAN 2022的环境上编译运行经常会出现类型加载异常或者方法找不到。解决办法很简单引用的EPLAN DLL一定是从当前环境安装目录的Bin下选择的不要盲从网上教程复制DLL到自己的工程目录更不要把别人项目里的引用路径直接搬过来。6. 经验交流给刚入门的你几条实在建议踩过这些坑之后有几个习惯我想特别留给你它们不算什么高深技巧但能实实在在地减少你在调试上消耗的时间。第一给插件建一个独立的命名空间和一型唯一的前缀。EPLAN插件是以程序集为边界加载的如果两个插件定义了同名的命令或者菜单ID后加载的那个会默默覆盖前一个查起来非常头疼。我在给命令取名字的时候习惯用公司名插件名作为前缀比如AcmeWireTool.FirstCommand别人不会撞自己也好认。第二坚持“日志断点”的双轨调试。断点能帮你看到代码现场日志能帮你还原用户环境里出现的问题两者缺一不可。插件发布给客户后他们那边的异常你是没法断点调试的这时候一条清晰的日志比任何远程工具都管用。所以从项目一开始就要把日志类放进去别等出了问题再来补。第三发布前换台干净机器做一次冒烟测试。EPLAN二次开发的版本兼容问题比想象中多我吃过亏在自己机器上编译好的插件到客户那边加载就报错最后发现是客户机器上EPLAN版本的小版本号不一样API行为有细微差异。所以有条件的话至少准备一个安装了目标版本EPLAN的虚拟机专门用来验证插件能不能正常加载、菜单能不能点开、核心功能能不能跑通。最后再分享一个小技巧每次动手改代码前先把EPLAN安装目录下的帮助文档翻一翻。EPLAN的二次开发文档虽然有点旧但API的类结构、方法说明基本都在。你卡住的那个问题大概率文档里已经写过答案了。