简介:面向UG NX二次开发人员的C++编程模板,基于NX2406版本制作,适配VS2022环境。模板的定位是帮助开发者跳过繁琐的项目初始化,快速搭建NX二次开发框架。压缩包共含7个文件,包括程序入口源文件、数字签名资源文件、VS项目文件、VSTemplate模板、两个图标文件及ReadMe说明文档,整体仅15KB,结构凝练。文件中含程序入口源文件与数字签名资源文件,图标文件用于界面识别,说明文档交代使用须知,各类文件职责明确。该模板可配合博主发布的NX2406二次开发配置系列文章使用,覆盖从环境配置到编译运行的常见问题,适合刚接触NX二次开发或希望统一项目结构的工程师。无论是入门学习还是日常项目开发,都能有效降低配置成本与上手难度。目前已有1737人学习浏览,是了解NX二次开发工程结构、缩短前期准备时间的实用参考。 我做NX二次开发也有小十年了,手机和网盘里攒了一堆从NX6时代传下来的老模板,基本都是VS2008编译的,文件打开一大半是兼容性报错。直到用NX2406重新整理了一套编程模板,才从“每次换版本就返工”里彻底解脱。这套模板不是简单把示例堆一起,而是把工程结构、菜单加载、回调分发、常用工具函数一次性铺好,拿到新项目改改就能跑。我决定把模板怎么搭、为什么这么搭、踩过哪些坑一次性讲清楚,适合刚入坑UG NX二次开发的工程师,也适合正在被NX2406版本兼容问题折腾的老手。
1. 为什么我坚持在NX2406上做一套二次开发编程模板
1.1 从每次从零搭环境到拿来即用
老开发应该都有这个经历:拿到一台新电脑,或者公司统一升级了NX版本,第一件事就是翻旧代码。结果发现头文件路径全变了,Unicode字符集跟老项目对不上,菜单加载出来是乱码,好不容易编译通过,NX一点加载就崩。我最早做二次开发时,光是配置VS环境就折腾了两天,后来学聪明了,每次做完一个项目,就把当时的工程目录打包成“母版”,下次直接复制改名。但这个方法有个问题,NX版本一升级,母版就过时了,尤其是NX2306之后的版本,API变化明显加快,老母版基本等于废纸。
做NX2406模板的初衷,就是想彻底解决这个“每次从零开始”的痛点。NX2406是Siemens的新版本序列之一,底层内核和UI框架相比老版本有不少调整,最直观的就是对话框控件、选择逻辑和某些NXOpen类被重构。与其每次踩版本坑,不如基于当前主力版本做一套干净、稳定、自带最小可运行示例的模板。这套模板我把工程配置、菜单注册、回调分发、日志系统都固化进去,新项目只要复制目录,改项目名和业务代码,剩下的交给模板自动加载。
另外一点,模板这东西不只是给个人用。团队协作时,每个人本地环境不一样,有的用VS2019,有的用VS2022,有的装了多个NX版本。如果模板里做了版本适配和路径自动探测,后面接手的人会少很多抱怨。我做这套NX2406模板时,特意把所有外部依赖路径都改成可配置变量,不强绑某台机器的绝对路径,这样团队里谁拉下来都能直接编译。
1.2 这套模板到底解决了什么问题
先说结论:它解决的是“环境、工程、交互”三层问题,不是给你写业务逻辑的。
环境层,模板带完整的VS项目文件,包含头文件目录、库目录、附加依赖项、输出路径,以及NX2406对应版本的调试环境变量。你不需要再记住一条条路径配置,直接打包里的说明文档也能搭出来。
工程层,模板把NX二次开发常用的两种入口都做了统一封装:一个是经典的UFUN入口(ufusr),一个是NXOpen/BlockStyler对话框入口。菜单文件、工具条文件、对话框DLL的部署目录结构全部按NX官方推荐方式摆好,拷到客户机器上不会出现找不到资源的情况。
交互层,这是我自己项目里最常踩的地方。选择对象、获取点坐标、鼠标中键行为、绘图区窗口句柄、PMI遍历、刻字等等,这些功能每个项目都要写,但每次写都要翻文档重新试。模板里直接把高频能力封装成函数,我只需要调用方法名,不用关心内部是UF还是NXOpen实现。所以我常说,模板不是框架,是“拐杖”,把容易摔的坑先填平,让你把精力放在真正的业务上。
2. 模板的整体结构设计:工程目录、启动流程与功能划分
2.1 项目目录与VS工程文件的组织方式
NX二次开发的部署目录有讲究,不能把DLL随便丢。模板一开始就把目录结构固定下来,符合NX的搜索约定,也方便打包发布。我用的目录结构如下:
NxTemplate2406/ ├── startup/ │ ├── NxTemplate.men │ ├── NxTemplate.tbr │ └── NxTemplate.dlg ├── application/ │ ├── NxTemplate.dll │ ├── NxTemplate.txt │ └── resources/ ├── code/ │ ├── NxTemplate.vcxproj │ ├── src/ │ ├── include/ │ └── external/ ├── bin/ └── doc/startup目录存放菜单文件(.men)、工具条文件(.tbr)和对话框脚本(.dlg),NX启动时会自动扫描这个目录。application目录放编译产物DLL和对话框所需的图片、样式资源。code目录是源码主目录,VS工程文件放在最外层,方便整个目录直接打开编译。bin目录是中间产物,doc目录放配置说明和环境变量说明。
这套结构的关键点在于:startup和application是运行时目录,code是开发目录,两者分离。编译时我通过VS的输出事件,把生成的DLL自动拷贝到application目录,菜单文件里的命令路径也相对固定,这样无论开发机还是客户机,只要把整个模板目录放在任意盘符,不需要改环境变量就能运行。我一开始试过把DLL输出到system32,后来发现NX加载时对路径很敏感,绝对路径很容易造成“在本机好好的,拷到别人电脑就加载不了”的现象。现在模板里全部使用相对路径加环境变量UGII_USER_DIR来定位,部署省心很多。
2.2 菜单、工具条与对话框的自动装载机制
NX二次开发的菜单分为两类:一种是修改用户菜单(.men文件),另一种是用Ribbon UI(.rtb文件)。NX2406主界面已经全面Ribbon化,但我做模板时依然保留了传统.men格式,原因是传统菜单文件在脚本化测试、客户定制和老项目迁移时更通用。启动时,NX会按UGII_USER_DIR指向的目录寻找startup下的文件,自动装载。这里有一个容易被忽略的细节:菜单文件必须保存为UTF-8 with BOM,否则NX在读取中文标题时会乱码。我踩过这个坑,折腾了一个下午,最后发现是VS默认保存成了无BOM的UTF-8,NX根本不认。
菜单文件里,我定义了一个顶级菜单和三个命令按钮。每个命令对应一个回调函数,回调函数在DLL里导出。NX加载菜单后,点击按钮会调用DLL里的对应入口。这个机制说起来简单,实际开发中经常遇到“菜单出来了,按钮点了没反应”的情况,多半是菜单文件里的回调函数签名和DLL导出函数不一致,或者DLL没被加载进来。模板里我把回调函数统一注册在同一个文件里,并打印日志,排查时就快很多。
BlockStyler对话框的装载也是同理。使用BlockStyler创建对话框后,会生成一个.dlg文件和对应的C++文件,模板里保留了这套机制,并额外封装了一个对话框管理器。当用户在界面点击按钮时,不会直接操作NX对象,而是先把数据收集到结构体里,再调用业务层接口。这样做的原因是NXOpen的UI线程和业务线程有时需要切换,直接在UI回调里跑重逻辑,容易让NX界面卡死。
2.3 高频API的功能封装清单
模板里最有价值的部分,是我把所有项目里反复用的API封装成了一个个工具类。这些不是网上抄的Demo级代码,而是经过线上业务验证的稳定函数。下面这张表是模板里的核心封装模块:
| 模块 | 核心函数 | 解决的问题 |
|---|---|---|
| Environment | Initialize() / GetNxVersion() | 环境检测、路径解析、版本识别 |
| MenuCallback | RegisterMenuAction() | 菜单命令到函数指针的统一映射 |
| Selection | SelectObject() / SelectPoint() | 封装UF与NXOpen选择逻辑 |
| Coordinate | WcsToAbs() / MapPoint() | 坐标系转换,避免点坐标算错 |
| Graphics | GetGraphicsWindow() | 获取绘图区窗口句柄 |
| PMI | TraverseAnnotations() | 遍历PMI标注,提取文本与属性 |
| TextCurve | CreateTextCurve() | 草图和曲线文字生成 |
| Journal | WriteLog() / DumpError() | 日志记录与异常跟踪 |
表格里这些模块,我在下面章节会挑几个重点展开。封装的原则是“外部只看到简单参数,内部自己处理NX差异”。比如SelectPoint()函数,外部传一个起始点和一个过滤器类型,内部自动判断是走UF_UI_select_point还是NXOpen的Selection,这一步在NX2406里其实已经比较复杂了,因为高版本对选择过滤器的类型系统改了多次。
3. 核心实操:环境配置、菜单注册、交互与点坐标获取
3.1 UG NX二次开发环境配置vs2019:三个必须对齐的路径
网上关于NX二次开发环境配置的教程大多停留在NX6到NX12,到了NX2406版本,最坑的是Visual Studio版本与NX内置编译环境的匹配。我在这套模板里默认使用VS2019,因为NX官方在2406版本中明确支持VS2019的C++工具集。如果你非要用VS2022,也不是不行,但需要额外装一个v142工具集让VS2022兼容VS2019的库格式,否则链接阶段会报一堆“无法解析的外部符号”。
配置时要对齐三个路径:头文件目录、库文件目录、输出目录。头文件目录需要包含两块:一块是$UGII_BASE_DIR\UGOPEN,另一块是$UGII_BASE_DIR\NXOpenCPP。库文件目录则要指向$UGII_BASE_DIR\UGOPEN,附加依赖项里加上libugopen.lib、libnxopencpp.lib、libnxopenuicpp.lib。这三个lib是最常用的,如果用到NXOpen的某些功能模块,比如PMI、几何建模,还需要额外加对应的lib,比如libnxopen_annotations.lib。
还有一个细节:输出目录不要直接指向application目录,而是先输出到本地bin目录,再通过VS的后期生成事件直接复制到application目录。这样做的原因是NX运行时会锁定正在使用的DLL,如果你直接在源码目录下生成DLL,下次重编译时会提示文件被占用,非常烦人。后期生成事件里加一行copy命令,既不会被锁,还能在编译失败时自动清理旧DLL,避免踩到“改了代码但NX加载的还是老DLL”这种坑。
最后是调试环境。模板里建议把NX的安装目录下的UGII设置为系统环境变量,同时在VS调试属性里设置“命令”为$UGII_BASE_DIR\UGII\ugraf.exe。这样按F5时VC会自动启动NX并进入调试模式,DLL里的断点可以命中。不要在NX已经运行时再手动附加进程,虽然也可以,但容易出现断点不生效的情况,尤其是菜单命令是动态加载的DLL。
3.2 添加命令按钮下拉菜单项:从men文件到回调函数
标题里提到的“添加命令按钮下拉菜单项”是NX二次开发最基础也最容易出错的需求。模板里的.men文件写法如下:
VERSION 2406 EDIT UG_GATEWAY_MAIN_MENUBAR BEFORE UG_HELP CASCADE_BUTTON TPL_TOOLS LABEL 我的工具 END_OF_BEFORE MENU TPL_TOOLS BUTTON TPL_BTN_1 LABEL 获取点坐标 ACTIONS TPL_GetPoint BUTTON TPL_BTN_2 LABEL 遍历PMI ACTIONS TPL_TraversePmi SEPARATOR BUTTON TPL_BTN_3 LABEL 刻字 ACTIONS TPL_CreateText END_OF_MENU这个文件的意思是:在主菜单栏的Help菜单之前插入一个“我的工具”级联菜单,下面挂三个按钮。ACTIONS后面的名字就是DLL里需要导出的函数名。注意,这里不是随便取名,DLL里必须有对应的导出函数,且函数签名必须是extern "C" DllExport void TPL_GetPoint( int response, UF_MB_data_t* data )这种形式。模板里的回调注册模块会自动扫描这些导出函数并绑定到菜单事件上。
如果要做下拉菜单项,就是CASCADE_BUTTON里再加MENU,菜单层级最多别超过三层,否则在高分辨率屏下显示会出问题。另外一个容易踩的坑是:菜单文件里的BUTTON名字(比如TPL_BTN_1)不能和别的菜单项重名,NX对菜单ID唯一性检查很严格,重名时它会静默忽略后面的按钮,不报错也不提示,你只会在界面上发现按钮消失。这种问题排查起来很痛苦,所以模板里我按项目缩写统一命名,降低重名概率。
3.3 鼠标中键、获取点坐标与绘图区窗口句柄的封装细节
这三个交互需求是搜索热词里出现最多的,也是我在实际项目里被问得最多的。
先说说鼠标中键。NX的鼠标中键默认代表“确定”,很多新人在BlockStyler对话框里想用中键触发某个按钮,结果发现按了中键没反应,或者整个对话框被关掉。原因是NX把中键消息在更上层就拦截了。模板里我的建议是:如果你要响应用户的中键单击,不要在对话框控件层面做,而是给绘图区窗口注册一个原生消息钩子,监听WM_MBUTTONDOWN。但千万注意,不能把中键的默认行为完全吃掉,尤其是旋转视角功能,你要是把中键消息全拦截了,用户会发现模型不能旋转,那就严重影响操作了。我自己的做法是:只在特定的自定义命令激活状态下启用中键钩子,其余时间全部放行,这样既不会干扰默认交互,又能拿到中键事件。
再来说获取点坐标。这是很多需求的基础:用户点一下绘图区,你拿到一个点坐标,然后用这个点去做后续建模。UF和NXOpen两套API都有对应的选择函数,但我推荐直接封装一个通用选择点函数,内部优先走UF_UI_select_point,因为它返回的是double[3]数组,类型简单,不容易和NXOpen的Point3d混淆。这里有个细节:UF_UI_select_point返回的坐标是屏幕点的投影坐标,如果你想拿到的点是在某个工作平面或某条边上,就必须先用选择过滤器限定范围。模板里的SelectPoint函数支持传入过滤类型,比如只允许选点位或只允许选平面上的点,大大减少用户误点的概率。
最后一个就是获取绘图区窗口句柄。这个需求通常出现在你想做自定义绘图、窗格分割或鼠标跟踪的时候。NX的绘图区窗口不是普通的单窗口,它由多个子窗口嵌套组成。模板里我用UF_UI_get_window拿到NX主窗口句柄,然后通过Win32的FindWindowEx逐级下查,找到类名为“UGSGraphicsWindow”的子窗口。这个类名我在NX2406上实测过,是稳定的。但要注意,这个类名在不同版本间有变化,模板里我加了一个缓存机制,第一次找到后记录窗口句柄,下次不再重复查找,因为FindWindowEx在每次调用时消耗不小,频繁调用会导致界面卡顿。
4. 踩坑实录:典型问题与排查技巧
4.1 运行时加载失败和无响应类问题
只要长期做NX二次开发,就一定见过各种“加载失败”的弹窗。我把高频问题整理成了一张速查表,方便直接对照。
| 现象 | 常见原因 | 排查方法 |
|---|---|---|
| 菜单里点了没反应 | 回调函数未导出或DLL没复制到application | 用Dependency Walker查导出表,确认ACTIONS名字一致 |
| 启动时NX报入口库错误 | lib库版本与NX版本不符 | 检查UGII_BASE_DIR指向的是不是NX2406 |
| 对话框中文乱码 | .dlg文件编码不是UTF-8 with BOM | 用notepad++重新保存菜单和dlg文件 |
| DLL加载后NX崩溃 | 使用了老版本UF函数,NX2406里已废弃 | 打开日志文件,查看崩溃前的最后一条写出的日志 |
| 调试断点不命中 | VS输出目录设置错误,编译产物没被实际加载 | 确认应用代码目录下DLL是build后最新复制版 |
排查加载类问题,我的经验是:先看菜单和DLL文件是否存在,再看文件名和函数名是否匹配,最后才去怀疑代码逻辑。80%的“没反应”都是文件层面的问题,真正代码写错的反而少。
我在模板里专门放了一个“自检模式”。启动模板命令时按下Ctrl+Shift,DLL会打开一个调试窗口,显示当前环境变量、UGII_BASE_DIR、加载的库列表和最近一次错误日志。这个自检模式帮我在客户现场省了很多时间,很多客户说“你的命令没加载”,其实是他自己把startup目录删了。
4.2 进阶功能需求:遍历PMI、刻字等
搜索热词里出现了“nx二次开发遍历pmi”和“nx二次开发刻字”,这两个都属于典型的进阶需求,我单独说一下模板里怎么处理。
遍历PMI,传统做法是用UF函数遍历标注对象,但NX2406里我强烈建议改用NXOpen的Annotations集合,因为这些老UF接口在遍历视图关联的PMI时经常漏对象。我用NXOpen的View对象拿到当前视图,再调用GetAnnotations方法,然后按AnnotationType过滤出PMI类型的数组。注意,PMI可能挂在隐藏视图上,直接遍历看不到,需要先把视图设为可见,再强制刷新一次模型视图。
刻字这个需求,一般出现在模具刻标识、零件打标码的场景。NX2406里我用的方案是创建曲线文本,通过CurveTextBuilder设置内容、字高、字体和位置,然后生成文本曲线。如果要把字刻到圆柱面,还要配合缠绕曲线或投影功能。模板里封装了CreateTextCurve函数,输入一句话和位置姿态矩阵,自动生成文本曲线。但有个坑:中文字体在NXOpen里默认路径不对,如果不指定字体,生成的曲线可能是乱码轮廓。我在模板里把可选字体列表做成了配置文件,默认使用NX安装目录下的ugcls字体,中文则切换到中文字体,这样导出给下游时不会丢字。
高级一点的需求,比如在PMI上关联属性或自定义标签,模板里也留了扩展接口。PMI本身是带属性的,你可以通过SetAttribute方法把零件号、工序号绑定到PMI上,后续在装配导航器里可以直接检索。这块在实际生产环境中很有用,比如质检部门用NV查看PMI时,可以直接显示工序信息。
4.3 这套模板如何持续维护
最后说点维护经验。模板不是做完就完了,你需要跟着NX版本小版本升级去调整。我维护NX2406模板的方式是每季度做一次全量回归:在NX2406的最新MR版本上跑一遍所有示例命令,确认菜单加载、选择交互和结果建模都正常。
维护还有一个重点:区分“项目代码”和“模板代码”。模板里我划了一条清晰的边界,公共模块全部放在code/include下面,项目专属逻辑放在code/src/business下面。每次开发新项目时只改业务目录,公共模块尽量不要动。如果确实发现了公共模块的bug,修好后要同步回模板,这样才能形成正向积累。
我还给模板写了版本号管理和变更记录。每次修改模板后,在doc文件夹里添加一条记录,写明改了哪个类、为什么改、哪些项目已经用过这个版本。这个习惯听起来麻烦,但半年后回头看,真的能救命。很多坑是你第五次遇到时已经忘了前四次怎么解决的,写下来才能真正沉淀经验。
我做这套模板最大的体会是:二次开发的大部分工作量不是写功能,而是对抗环境的不确定性和版本差异。你提前封装的每一个函数,未来都会帮你省下好几个小时的排查时间。如果你也准备在NX2406上开始二次开发,不要急着写业务,先把环境、菜单、回调、日志这套“地基”打好。这套模板的思路完全可以直接复制,哪怕你只用其中一小部分,至少不会在环境配置上继续被折磨了。
本文还有配套的精品资源,点击获取