ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

NX Open C++实战:从fam_test.rar跑通NX二次开发

NX Open C++实战:从fam_test.rar跑通NX二次开发 简介本资源是面向NX二次开发初学者与工程技术人员的UG Open C实战示例包聚焦家族表Family Table的程序化创建与管理解决企业中参数化变体设计自动化程度低、重复操作多等实际问题。压缩包共19个文件含3个核心头文件.h用于接口声明、3个C源文件.c实现主逻辑与对话框功能、3个目标文件.obj及1个动态链接库.dll辅以VC6工程配置文件.dsw/.dsp、调试符号.pdb和库文件.lib/.exp整体仅61KB轻量易导入学习。已有230人下载学习适合在NX 12环境下结合SDK快速编译调试。读者可直接获取完整可运行的fam_test工程结构包含家族表定义函数、UI交互逻辑、模型加载与特征操作等关键代码模块并通过.h/.c文件分离清晰掌握UG Open C的会话管理、部件操作与数据驱动建模流程。1. 拿到fam_test.rar之后先把NX Open C这件事想清楚打开压缩包那一刻大多数人跟我刚入行时一样里面躺着一堆.cpp、.h、.dll、.prt还有一个不知道哪年写的README。文件名fam_test.rar带NX Open C、NX、UG Open C这些标签这就是典型的NX二次开发练习包。我当年第一反应是赶紧编译跑起来结果卡在环境配置上整整两天。后来才明白这类项目包的核心价值不在于那几个测试文件而在于它帮你把NX Open C的开发链路跑通了——从VS工程配置、NX内部加载、UI交互到几何操作一整套东西。NX Open C是什么简单说西门子NX以前叫UG提供了一套C接口让你能在NX进程里跑自己的代码。这跟外部脚本不一样你的代码编译成DLL后NX在启动时加载它你就能在NX菜单里看到自己的按钮点击后执行你的逻辑。区别于NX Open Python和NX Open .NETC的版本性能最好适合做大量几何运算、批量处理这种重活。而且很多老工厂的NX二次开发代码都是C写的你接手维护的时候绕不开。这个fam_test.rar解决的核心问题我总结下来是三个第一演示了NX Open C工程的标准结构让你知道头文件、源文件、DLL输出怎么组织第二跑通了一个最小闭环从VS编译到NX加载到功能执行第三提供了一组基础测试函数比如获取光标位置、获取点坐标这类高频操作改一改就能用在真实项目里。适合谁看想入NX二次开发坑的新人或者已经用NX Open Python写了不少脚本、想转C提性能的老人。如果你连NX都没装过建议先装好NX 12.0或NX 12.0.2.9再回来看这篇。下面所有内容都围绕实际开发场景展开不整虚的。2. 开发环境的坑九成新人踩在版本匹配上2.1 VS版本和NX版本的对应关系别想当然NX Open C的编译环境有个硬性要求NX版本和Visual Studio版本必须匹配。我见过太多人拿着VS2019去编NX 10的代码折腾一晚上编出来的DLL加载就崩还以为是代码问题其实是编译器版本不对。原因是NX本身是用特定版本的编译器构建的它加载的扩展DLL必须用相同或兼容的运行时库。具体说几个常见组合NX版本推荐的VS版本平台工具集NX 10.0VS2012v110NX 11.0VS2013v120NX 12.0VS2015/VS2017v140/v141NX 12.0.2.9VS2017v141NX 2206系列VS2019v142NX 2306系列VS2022v143你可能会问我VS2019装的是v142工具集能不能编译NX 12的代码能编但运行时大概率出问题。NX 12.0.2.9这个版本很多人在用因为它是NX 12的最终维护版本稳定配套的MP14补丁包装上之后API行为更完善。我建议新项目直接上NX 12.0.2.9 MP14加VS2017或者NX 2206系列加VS2019这两个组合社区资料最多踩坑了也有人能帮你。还有个细节容易被忽略如果你机器上装了多个VS版本编译NX代码前一定要确认当前使用的是对的平台工具集。在VS里右键项目→属性→常规→平台工具集手动切到对应版本。我习惯把VS2017和VS2019都装上每个NX项目单独指定工具集互不干扰。2.2 include和lib目录配置照着抄就行环境配置里最核心的就是把NX的头文件和库文件路径告诉VS。路径其实很固定以NX 12.0为例安装目录假设是D:\Program Files\Siemens\NX 12.0.0include目录D:\Program Files\Siemens\NX 12.0.0\UGII\NXOpen这里面有NXOpen、NXOpenCPP等子目录所有头文件都在这里lib目录D:\Program Files\Siemens\NX 12.0.0\UGII\NXOpen\lib或者D:\Program Files\Siemens\NX 12.0.0\UGII某些版本的lib在UGII根目录在VS项目属性里这样配C/C → 常规 → 附加包含目录填入NXOpen头文件所在路径 链接器 → 常规 → 附加库目录填lib所在路径 链接器 → 输入 → 附加依赖项libnxopencpp.lib、libnxopencpp_annotations.lib、libnxopencpp_features.lib等具体看你用到哪些模块。新手先加libnxopencpp.lib和libnxopencpp_ufun.lib这两个就够这些路径里的NGOpenCPP和UFUN两个模块是NX二次开发的基石。NXOpen是面向对象的C封装UFUN是历史悠久的C风格函数库。实际开发中两者经常混用NXOpen做对象操作流畅UFUN做底层几何查询方便。fam_test.rar里的代码大概率两者都用到了后面我会细化。3. 从压缩包到能跑的DLLfam_test.rar完整编译流程3.1 解压之后的目录结构先看明白再动手一个标准的NX Open C练习包解压后一般长这样fam_test/ ├── fam_test.cpp # 主入口定义入口函数 ├── fam_test.h # 头文件声明函数和类 ├── fam_test.dlx # NX对话框文件NX 12及以上用.dlx ├── fam_test.prt # 测试用的部件文件 ├── startup/ # 这个目录很关键NX启动时自动扫描 │ └── fam_test.men # 菜单文件定义你的菜单项 ├── application/ # 生成的DLL通常放这里 └── README.txtstartup目录是NX二次开发的标准约定。NX启动时会去用户目录或环境变量指定的目录下找startup文件夹加载里面的.men菜单文件和.tbr工具条文件。你的DLL可能放在application目录菜单文件通过.men里的BUTTON指令指向DLL里的入口函数。这个机制理解了后面部署到别的机器就不慌。fam_test.cpp这种单文件工程是最简单的形态。入口函数通常长这样extern C DllExport void fam_test_entry( char *parm ) { // 这里是你的业务逻辑 // 菜单点击后NX调用这个函数 }注意extern C和DllExport这两个缺一不可。前者告诉C编译器用C方式导出符号避免名字修饰name mangling否则NX按函数名找入口时找不到后者是告诉链接器这个函数要导出。UG/NX的老项目里也见过用ufusr( char *parm, int *returnCode, int rlen )这种老式入口的那就是纯UFUN风格的程序。两种入口NX都支持但新项目建议用NXOpen风格的入口。3.2 编译时常见的三个低级错误看到了直接改我让几个新人分别编译fam_test.rar收集到的错误高度集中列出来省得你们再踩错误一C2065 unresolved external symbol一堆找不到符号的链接错误。原因基本是附加依赖项没加全。比如代码里调用了NXOpen::Session::GetSession()但libnxopencpp.lib没加进去。解决办法是把NXOpen目录下的所有.lib都加进去吗不是lib加多了反而可能冲突。我一般只加用到的先跑起来再加。错误二C2664 cannot convert argument类型不匹配。NXOpen C的API大量使用智能指针比如TaggedObject *、NXOpen::Point *这些指针类型新手经常拿UFUN的tag_t直接往里传。两个模块的类型体系不同混用前要做转换tag_t转NXOpen对象用NXOpen::NXObjectManager::Get( tag )NXOpen对象转tag_t用obj-Tag()。错误三C3646 unknown override specifier头文件里用了NXOpen命名空间但没加using namespace NXOpen或者类定义里引用了未前置声明的类型。这种在编译阶段就能发现处理方式是在头文件顶部补上using namespace NXOpen; using namespace NXOpen::Features; using namespace NXOpen::GeometricUtilities;3.3 代码怎么进NX菜单注册和手动加载两条路代码编译成DLL后怎么让它跑起来两条路。第一条路通过菜单文件注册适合正式功能。在startup文件夹里写一个.men文件VERSION 120 EDIT UG_GATEWAY_MAIN_MENUBAR BEFORE UG_HELP CASCADE_BUTTON FAM_TEST_MENU LABEL 尺寸测试 END_OF_BEFORE MENU FAM_TEST_MENU BUTTON FAM_TEST_BTN LABEL 获取光标坐标 ACTIONS fam_test_entry END_OF_MENU意思是在帮助菜单前加一个“尺寸测试”菜单里面有一个“获取光标坐标”按钮点击后执行fam_test_entry这个导出函数。ACTIONS后面的名字必须和你代码里DllExport的函数名完全一致。第二条路手动加载适合调试。在NX里按CtrlU打开“执行NX Open”对话框选中你的DLL直接执行。也可以输入File→Execute→NX Open效果一样。这个方式不需要菜单文件编译完立刻验证逻辑我调试阶段90%都用它。4. 核心代码怎么写从外部特征识别到参数获取4.1 UFUN和NXOpen老代码和新代码怎么选fam_test.rar这个包名里的fam是什么看代码才明白是feature and measurement的缩写就是特征和测量。这类代码常见的功能是选中一个面或孔判断它是孔面还是轴面然后测出直径、坐标。在NX二次开发里这就是最典型的特征识别测量需求。UFUNUser Function是UG时代传下来的C接口用tag_t表示对象函数名前缀是UF_比如UF_CURVE_ask_point_data、UF_MODL_ask_feat_type。好处是稳定、文档全、老项目多但写起来啰嗦而且不面向对象逻辑一复杂代码就乱。NXOpen是后来主推的面向对象接口用类、方法、属性组织写起来思路清晰配合C的智能指针还能自动管理内存。但NXOpen的API覆盖不如UFUN全有些老功能只有UFUN有。实际项目中我的原则是新功能优先用NXOpen代码可读性好后续维护的人感谢你老功能迁移或UFUN独有的直接用UFUN两者混用时做好类型转换就拿“判断是孔面还是轴面”这个需求举例。NX里一个圆柱面可能是孔的内表面也可能是轴的圆柱外表面。怎么区分看面的法向方向孔面的法向量指向圆柱轴线指向圆心轴面的法向量背离轴线指向外。用UFUN的UF_MODL_ask_face_type先确认是圆柱面再用UF_MODL_ask_face_data取到面的参数通过几何分析得到轴线方向最后判断法向和轴线的关系。用NXOpen的话先拿到Face对象转成CylindricalFace用GetDirection()拿方向再用GetAxis()拿轴线比较两个向量的点积正负。4.2 代码示例获取光标位置和点坐标热搜词里反复出现“NX二次开发代码获取光标位置”“获取点的坐标”这说明很多人做的第一个功能就是让用户在NX里点一个位置然后程序拿坐标。这个功能不难但UFUN和NXOpen的写法不太一样。UFUN的写法#include uf.h #include uf_ui.h extern C DllExport void get_cursor_pos_entry( char *parm ) { UF_initialize(); double origin[3]; double direction[3]; // 获取当前光标位置 UF_UI_point_construct(指定点, origin, direction); char msg[256]; sprintf(msg, 光标坐标: X%f, Y%f, Z%f, origin[0], origin[1], origin[2]); uc1601(msg, 1); UF_terminate(); }这里UF_initialize()和UF_terminate()必须成对出现功能是初始化UFUN环境不调用的话UF_函数会直接报错。UF_UI_point_construct会弹出“点构造器”对话框用户点一下坐标就进origin数组了。NXOpen的写法思路类似但对象模型更清晰#include NXOpen/Session.hxx #include NXOpen/Point.hxx #include NXOpen/UI.hxx #include NXOpen/SelectObjectList.hxx using namespace NXOpen; extern C DllExport void get_point_entry( char *parm ) { Session *theSession Session::GetSession(); UI *theUI UI::GetUI(); // 让用户选择一个点对象 Point *selectedPoint NULL; // 这里简化了选择逻辑实际用Selection::SelectObject等接口 // 拿到selectedPoint后 Point3d coords selectedPoint-Coordinates(); char msg[256]; sprintf(msg, 点坐标: X%f, Y%f, Z%f, coords.X, coords.Y, coords.Z); theUI-NXMessageBox()-Show(提示, MessageBox::TypeQuestion, msg); }看明白区别了吗UFUN用的是C风格的数组和函数NXOpen用的是Point3d这样的结构体和对象方法。性能上两者差不多但NXOpen的代码读起来更像“在操作NX对象”而不是“在调一堆函数”。4.3 参数计算孔轴判断的核心逻辑回到“判断是孔面还是轴面”这个经典需求我把判断逻辑完整拆一遍这也是fam_test这类包里最有价值的部分。首先拿到一个面Face判断类型。UFUN的代码#include uf_modl.h // 返回1表示孔面0表示轴面-1表示不是圆柱面 int judge_hole_or_shaft( tag_t face_tag ) { int type 0; UF_MODL_ask_face_type(face_tag, type); // 圆柱面的type是UF_MODL_CYLINDRICAL_FACE if (type ! UF_MODL_CYLINDRICAL_FACE) return -1; // 获取面的几何数据 double point[3], dir[3], box[6]; double radius 0.0; double rad_data 0.0; int type1 0; UF_MODL_ask_face_data(face_tag, type1, point, dir, radius, box, rad_data); // point是轴线上一点dir是轴线方向单位向量radius是半径 // 在面上取一个点比如面的参数中心点 double param[2] {0.5, 0.5}; double uf_point[3]; UF_MODL_ask_face_uv_point(face_tag, param, uf_point); // 计算面上点到轴线的垂足 double to_axis[3]; to_axis[0] uf_point[0] - point[0]; to_axis[1] uf_point[1] - point[1]; to_axis[2] uf_point[2] - point[2]; // 点到轴线垂足 面上点 - 轴线方向 * 投影距离 double proj to_axis[0]*dir[0] to_axis[1]*dir[1] to_axis[2]*dir[2]; double foot[3]; foot[0] point[0] proj * dir[0]; foot[1] point[1] proj * dir[1]; foot[2] point[2] proj * dir[2]; // 法向向量 面上点 - 垂足指向远离轴线的方向 double normal[3]; normal[0] uf_point[0] - foot[0]; normal[1] uf_point[1] - foot[1]; normal[2] uf_point[2] - foot[2]; // 判断法向方向如果面的法向和normal同向是轴面反向是孔面 double face_normal[3]; UF_MODL_ask_face_uv_normal(face_tag, param, face_normal); double dot face_normal[0]*normal[0] face_normal[1]*normal[1] face_normal[2]*normal[2]; return (dot 0) ? 0 : 1; // dot0同向轴面dot0反向孔面 }这个逻辑的核心就一句话圆柱面的法向量如果指向外是轴面指向内指向轴线是孔面。实际工程中孔的圆柱面法向是朝里的轴的圆柱面法向是朝外的这是NX建模的默认约定。注意UF_MODL_ask_face_uv_point拿到的是面的参数坐标点UV值取0.5, 0.5一般就在面中心附近。但对复杂面或修剪过的大圆柱面UV中心可能不在实际面上这时要做容差判断或者改用UF_MODL_ask_face_props在面上取多个点做统计。这个细节是新手容易忽略的等你在真实零件上判断出错就明白为什么要多取点了。5. 调试技巧VS附加线程日志输出还有那些说不清的坑5.1 用VS调试NX进程里的DLLNX Open C调试比普通C麻烦因为你的代码跑在NX进程里。常规的办法是在VS里打开你的工程设置断点然后Debug→Attach to Process选nx.exe进程。这样NX执行你的DLL时断点就会命中。有个细节附加进程时注意VS版本和NX的位数一致。NX12是64位的VS里必须用x64的Debug配置编译附加到的也是64位进程。如果你编译的是Win32配置附加后断点不会命中因为NX进程根本没加载你那个位数的DLL。调试时建议用Debug配置编译不要用Release。Debug生成的DLL带了调试符号.pdb断点、变量查看、调用栈都正常。Release也不是不能调试但优化后的代码跳来跳去看起来非常痛苦。5.2 日志输出比弹窗好用一百倍NX二次开发有个反人类的设定printf和std::cout在NX进程里看不到输出。你printf了一堆调试信息控制台啥都不显示。新手最容易在这上面卡住以为代码没执行。靠谱的做法是写日志文件#include fstream void write_log( const char *msg ) { std::ofstream logfile; logfile.open(D:\\nx_dev_log.txt, std::ios::app); if (logfile.is_open()) { logfile msg std::endl; logfile.close(); } }然后在每个关键步骤后面打一条日志write_log(Step 1: entry called); UF_initialize(); write_log(Step 2: UF_initialize done); // ... 业务逻辑 write_log(Step 3: feature type std::to_string(type));等你程序跑挂了打开D盘的日志文件从上往下看到哪一步没打出来问题范围就缩小了。如果日志显示UF_initialize()没执行完就崩了多半是NX环境问题如果UF_initialize()之后崩了那就是你自己的代码逻辑问题。我实际开发中还会在日志里记录每个函数的执行耗时用std::chrono。批量处理几百个面时哪个函数慢一眼就能看出来。这种性能分析的日志线上处理大模型时尤其有用。5.3 退出NX时DLL崩溃多半是入口函数的问题一个祖传老坑功能正常但每次关闭NX时崩溃。原因是DLL卸载时的清理逻辑写错了。NX加载你的DLL后退出时会调你的清理函数比如UF_terminate()之前的资源释放。如果你在入口函数里new了对象没delete或者没有成对调用像UF_initialize/UF_terminate、UF_free/UF_alloc这样的APINX关闭时就可能访问到已释放的内存。排查方法把入口函数里的所有资源分配和释放列个表逐个检查是否成对。特别注意UFUN的C接口很多函数返回的内存需要你手动UF_free比如UF_MODL_ask_face_data返回的数组。NXOpen这边用智能指针管理一般不涉及但如果你用UF函数必须留意内存。还有个常见问题是DLL里用了静态变量或全局对象它们的析构函数在DLL卸载时执行如果此时NX内部某些子系统已经关闭析构函数里调用NX API就会崩。解决办法是全局变量尽量用指针并在入口函数里new出口函数里delete。6. 从fam_test到完整项目项目管理与发布配置6.1 团队协作时环境变量怎么统一你一个人在自己机器上开发没问题但如果要团队协作或者部署到客户机器环境变量就必须统一。NX二次开发常用的环境变量是UGII_USER_DIR和UGII_SITE_DIRUGII_USER_DIR用户自定义目录NX启动时加载这个目录下的startupUGII_SITE_DIR站点目录多用户共享的二次开发文件放这里环境变量指向你的开发目录后NX启动时会自动加载里面的菜单和DLL。注意DLL依赖的其他文件比如.dlx对话框文件也要放在NX能找到的路径下。一个稳妥的做法是UGII_USER_DIRD:\nx_dev\fam_test D:\nx_dev\fam_test\ ├── startup\ │ ├── fam_test.men │ └── fam_test.tbr └── application\ ├── fam_test.dll ├── fam_test.dlx └── fam_test_doc.pdf这样设置后NX启动就自动加载fam_test菜单无需手动CtrlU。发布给客户时只需要把整个fam_test目录拷贝到客户机器设置相同的环境变量即可。6.2 编译脚本和版本管理工程化必备手工在VS里点编译不是不行但每次都要打开VS、选配置、点生成效率低。我习惯写一个build脚本用命令行或批处理调用MSBuildmsbuild fam_test.vcxproj /p:ConfigurationRelease /p:Platformx64 /m配合Jenkins或GitLab CI可以实现提交代码后自动编译、自动拷贝DLL到部署目录、自动跑冒烟测试。这种工程化配置在单体项目里可以不做但当你维护十几个NX插件时没有自动化会疯掉。版本管理同样重要尤其NX的API在不同版本间有差异。我项目里每个DLL都会带版本信息代码里用宏定义#define FAM_TEST_VERSION 1.3.2 #define FAM_TEST_NX_MIN NX 12.0 #define FAM_TEST_NX_MAX NX 2206启动时打印版本号到日志客户报问题的时候一句话就能定位到他跑的哪个版本。6.3 V142工具集和NX版本升级兼容性向前看前面提到NX 2206系列用VS2019的v142工具集。如果你现在跑NX 12升到NX 2206之后旧代码可能编译不过。NX的API升级虽然保持了大部分的向后兼容但有些函数签名会变尤其是NXOpen里一些返回类型从int变成了enum class。我升级时的做法是先编一遍把编译错误全部拉出来分类函数签名变了的一律照着新API改不要试图绕过去行为变化的部分比如某些默认参数的值变了仔细看Release Notes回归测试用同一套测试零件跑一遍老功能和升级后的功能对比输出最关键的是别一次性跨越太多版本。NX 12直接跳到NX 2306中间API变了好几次工作量巨大。稳妥的路径是NX 12 → NX 2206 → NX 2306每一步只处理一小批差异。7. 实操总结fam_test.rar给我的三个教训项目虽小但fam_test.rar里隐含的开发思路这三年帮了我不少忙写下来分享给大家第一先把环境跑通再动手改代码。我见过太多人拿到练习包第一件事是读代码读了一上午结果编译都没过。正确顺序是解压→建工程→配置路径→编译→让它在NX里跑起来→然后才开始改逻辑。环境通了后面每一步都有反馈改起来心里有底。第二UFUN和NXOpen不是敌人是战友。别听人说UFUN是老技术就完全不用也别迷信NXOpen全都能搞定。我做在NX里做批量孔表提取功能用了NXOpen做界面和选择用UFUN做底层几何遍历取长补短两天写完效果很好。fam_test.rar里也是两种API混用的说明这种混合模式就是NX二次开发的主流玩法。第三日志和调试习惯决定开发效率。现在我做任何NX这个领域的定制功能上来先把日志函数写好再写业务逻辑。不是说我代码写得比别人好而是出了问题能快速定位不用靠猜。猜在NX二次开发这个领域是最贵的。把fam_test.rar跑通你就入了NX Open C的门了。接下来可以去看西门子自带的Sample目录里面有几十个官方示例从简单的实体创建到复杂的CAM后处理全是好资料。结合fam_test.rar里的基础操作你会发现一个完整的NX二次开发体系正慢慢展开。本文还有配套的精品资源点击获取
返回列表