简介:这是一份UG NX外部开发模式的基础入门示例文档,面向机械设计自动化或NX二次开发初学者,通过一个在外部模式下新建零件并创建Block的完整程序,讲解UF_API环境初始化、零件创建、块建模、保存与关闭等关键步骤,帮助读者理解NX Open C/C++开发的基本流程。示例采用公制单位,利用UF_MODL_create_block1并设置原点坐标与边缘ID参数,清晰展示了Block特征构造时各参数的传值方式。资源共1个文件,为docx格式,压缩包约14KB,内容精简,适合快速查阅与对照练习。文档中直接给出可运行代码,并以分步输出说明每项操作的结果,对理解UF_PART_new、UF_MODL_create_block1、UF_PART_save等函数调用很有帮助。目前已有619人学习,适用于希望从零开始接触UG外部开发、了解API调用结构的入门读者。
1. 外部开发模式创建Block:批量出坯与自动建模的第一块敲门砖
如果只是想在NX(UG)界面里画个方块,谁也不会去写代码。真正需要“UG外部开发模式下创建Block”的,往往是这样一批人:做模具标准件库的,想用脚本把几百个垫块、镶块一次性生成;做工艺自动化的,想把产品模型转成加工毛坯再交给CAM;还有做产线集成的,希望调用三方系统里的长宽高参数,服务端自动出一个带特征的prt。Block在NX里不是普通草绘矩形,而是带历史记录的三维实体特征,参数一改,后续的倒角、布尔、坐标都要跟着动。外部开发模式意味着程序运行在NX进程之外,先写好逻辑,再去驱动NX建模。这条路值不值得走?只要你有批量、有规律、要交付可追溯的参数化模型,它比人肉画图靠谱得多。下面的内容,从环境搭建到代码复现,再到踩坑复盘,按工程落地顺序写。
2. 外部开发模式的环境搭建:先搞定进程连接,再谈Block特征
2.1 内部开发和外部开发,差在启动方式和职责边界
常见误解是把“外部开发”等同于“命令行里跑UG”。严格说,二者的代码很相似,区别全在进程关系上。内部开发是NX界面里执行,DLL被NX主进程加载,能直接用UI和当前Part的对象,调试方便,但NX没开就跑不了;外部开发是独立的exe或dll被你的服务调用,再用NXOpen API去连接已运行的NX会话,适合无人值守和批量任务。我一般推荐新项目优先做外部模式,因为它和内部模式的代码比只多了一句会话获取,却能摆脱界面对脚本的束缚。下面是两种模式的对比:
| 对比项 | 内部开发 | 外部开发 |
|---|---|---|
| 启动方式 | 在NX界面内执行 | 独立进程连接NX会话 |
| 界面操作 | 可以直接驱动UI | 不依赖UI,以建模和存取为主 |
| 适用场景 | 交互式工具、需要人工看结果 | 批量建模、服务端自动化 |
| 典型入口 | 直接用WorkPart | Session.GetSession() 或 UF_initialize() |
外部模式也不是万能的:它拿到的Part本身就是NX进程里的WorkPart,如果你的程序在服务器上连一个无人值守的NX,模型树、导航器这些交互能力会弱很多。所以外部模式的正确打开方式是“把建模当函数调”,入参出参都做好日志,别在UI上较劲。
2.2 准备NX Open引用与最小连接代码
NX Open对C++和.NET都有支持。如果你只用C++,核心头文件是uf.hpp和nxopen.hpp;用.NET(C#)更省事,直接引用NX安装目录下的NXOpen.dll、NXOpen.UF.dll即可。注意版本:外部程序的引用DLL要和你机器上的NX主版本对应,否则Session连接时会出现程序集版本冲突。运行程序前,确认当前NX许可证里包含NX Open模块,否则一调API就报许可错误。先跑通下面这段,再写Block:
using NXOpen; class ExternalConnectDemo { static void Main() { Session theSession = Session.GetSession(); // 连接外部NX进程 Part workPart = theSession.Parts.Work; // 当前活动工作部件 if (workPart == null) { throw new System.Exception("当前没有打开的工作部件"); } System.Console.WriteLine("连接成功:" + workPart.FullPath); } }Session.GetSession()是外部模式的一道分水岭:内部模式里,系统会直接给出Session对象,不需要你手动连接;外部模式则必须靠这一行去绑定NX进程。workPart必须是外部程序运行前已打开的部件;如果NX刚启动且没有加载任何prt,这里workPart会为空。参数上不要纠结Work是什么,它和NX界面左上角看到的Work Part是同一个。调试时,如果程序一闪而过,建议把Console.WriteLine改成写文本日志,或者用Debugger.Launch()强制挂上断点。这一行看起来简单,却是外部开发模式能不能真实跑起来的咽喉。
2.3 自动化启动NX与附加调试
外部程序可以要求NX已经打开,也可以在程序里自行启动NX进程。我习惯把第一步做成启动脚本:脚本先根据配置找到NX安装目录,再启动NX,并自动打开一个空模板prt,把路径传给程序。这能避免“连不上会话”的第一类翻车。
using System.Diagnostics; // 启动NX进程:外部程序先拉起一个NX实例并打开目标prt string nxUgraf = @"<NX安装目录>\UGII\ugraf.exe"; string targetPart = @"<绝对路径>\blank.prt"; Process nxProc = Process.Start(nxUgraf, targetPart); if (nxProc == null) throw new System.Exception("NX进程启动失败"); // 等NX初始化完成,再尝试连接会话 System.Threading.Thread.Sleep(20000); Session theSession = Session.GetSession();代码里我用尖括号占位,是因为真实路径在不同机器上不统一,建议从配置文件或环境变量读取。Thread.Sleep只是最简单粗暴的等待方式,生产环境更稳的做法是轮询Session.GetSession(),每隔两秒尝试一次,直到成功或超时。跑批量时没有人工看屏幕,图形刷新、报警对话框都会变成坑。后面第4章会专门说刷新和卡死问题。这里先记住:跑批量之前,先用一个最小程序验证连接、验证许可、验证日志可写,三步都过再上复杂任务。
3. 用NXOpen创建Block:两条代码路线与参数说明
3.1 NXOpen API的CreateBlock:最短可运行代码
创建Block常用API是workPart.Features.CreateBlock,返回Feature对象,再把特征转成Features.Block。下面是可直接改路径跑的完整程序:
using System; using NXOpen; using NXOpen.Features; public class BlockCreator { public static void Main(string[] args) { Session theSession = Session.GetSession(); // 连接外部NX进程 Part workPart = theSession.Parts.Work; // 当前工作部件 if (workPart == null) throw new Exception("请先打开一个prt"); Point3d origin = new Point3d(0.0, 0.0, 0.0); // 左下角原点,绝对坐标系 string[] edgeLengths = { "100", "60", "40" }; // X、Y、Z三条边的长度 bool isSolid = true; // true=实体,false=片体 Features.Feature feature = workPart.Features.CreateBlock(origin, edgeLengths, isSolid); Features.Block block = feature as Features.Block; if (block == null) throw new Exception("创建结果不是Block特征"); workPart.Update(); // 更新特征 Console.WriteLine("Block已创建:" + feature.FeatureName); } }代码逻辑顺序是:先拿会话,再拿工作部件;构造原点Point3d,边长用字符串数组;调用CreateBlock生成特征;用as做类型校验;最后Update让模型真正更新。不要把new Point3d和CreateBlock分开太远,原点、边长、实体标志这三个要素要保持在一个事务里,中途不要做会触发NX重算的操作。CreateBlock默认把块体加入模型历史记录,这一步不传布尔运算标志,所以它生成的是独立实体,不做减法和交集。需要做布尔时,第5章会讲Builder方式。
3.2 决定Block创建成败的关键参数与控制点
| 参数 | 类型 | 含义与建议 |
|---|---|---|
| origin | Point3d | 绝对坐标系下的左下角点,不是WCS坐标 |
| edgeLengths | string[] | X/Y/Z三条边长,支持"100"或"L*2"表达式 |
| isSolid | bool | true建实体,false建片体,建模直接用true |
| Update() | void | 让特征计算落库,漏了它模型树可能不刷新 |
最容易被忽略的是单位和参考系。edgeLengths是字符串数组,NX会把“100”按当前部件的单位解析:在毫米部件里是100mm,在英寸部件里就是100英寸。origin却是纯double坐标,不带单位,这一点和UF函数的double数组一致。如果你在英制部件里想建100mm的块,直接把"100"填进去会得到100英寸;想显式指定单位要写成"100mm"。另外,origin用的是绝对坐标系,不是WCS;很多工程师在WCS里做设计,程序里也照抄WCS坐标,结果Block飞到了天边。解决方法是先换算:以WCS原点加矢量偏置,换算到绝对坐标再传给API。
3.3 老代码兼容:UF_MODL_create_block1的写法
如果你维护的工程是多年前的C++二次开发,很可能用的是UF_MODL_create_block1。它和NXOpen API是两套体系:函数名带UF前缀,返回tag_t而不是Feature对象。C++外部模式入口固定要先UF_initialize(),结束再UF_terminate():
#include <uf.h> #include <uf_modl.h> int CreateBlockWithUF() { int status = UF_initialize(); /* 连接外部NX会话,标志外部模式开始 */ if (status != 0) return status; double origin[3] = { 0.0, 0.0, 0.0 }; /* 绝对坐标系原点 */ double lengths[3] = { 100.0, 60.0, 40.0 }; /* 三条边 */ tag_t block_tag = NULL_TAG; status = UF_MODL_create_block1(origin, lengths, &block_tag); if (status != 0) { char msg[256]; UF_get_fail_message(status, msg); /* 取错误文本 */ /* 写日志 */ } UF_terminate(); /* 结束外部会话,释放资源 */ return status; }UF_MODL_create_block1的入参是double数组,默认就建实体;想建片体要走另一组builder接口。它也不带表达式能力,lengths写的是数值而不是"100mm"这类字符串,所以单位完全跟随部件。新项目我建议优先用NXOpen API:对象模型更规整,C#写起来省心,特征命名、表达式、图层信息都挂在同一套对象树上;老工程保留UF调用没问题,但不要混用两套接口去建同一个特征,否则对象句柄维护起来非常痛苦。
4. 外部模式创建Block的高频踩坑与排查清单
4.1 现象:Session连接失败,程序秒退
现象:外部exe运行后控制台一闪而过,或者抛出连接NX会话失败。原因:NX进程没启动;或者NX Open许可证不完整;再或者引用DLL版本与NX版本不一致。解决:先手动打开NX和目标prt,再跑程序;用日志把异常堆栈写进txt;检查exe同目录DLL和NX安装目录里的DLL是否版本匹配。最难查的是版本冲突,现象千奇百怪,“创建Block成功但Update崩溃”也常由它引起。这种问题不要凭感觉改代码,先写一个最小连接案例对照跑,能过再跑业务逻辑。
4.2 现象:尺寸明明是100,测量变成2540
现象:用"100"做边长,模型里看着正常,CAM编程时一量,怎么大了这么多?原因:当前prt单位是英寸,字符串"100"被解析成100英寸;如果你后续又把它导出成毫米格式,数字会按换算系数放大约25.4倍。解决:创建前读Part单位做分支;或者强制带单位写表达式,如"100mm"。这也是UF路径和NXOpen路径行为不一致的地方:UF_MODL_create_block1的double数组不带单位,永远按当前部件单位解析。很多人升级到NXOpen后沿用double思维,反而掉进表达式字符串的坑。
4.3 现象:重复运行,模型树里Block越叠越多
现象:程序跑一次成功,跑第二次时旧Block还在,新Block又加进来了。原因:CreateBlock是纯增量操作,不会自动替换同名特征,代码里没有清理旧特征。解决:创建前先按特征名遍历当前部件,找到旧Block先删除或禁用;删除时要小心特征依赖,如果后续倒角、布尔都挂在它上面,删除会连带报错。我一般会在Block特征名里带上批次号,例如BLK_PART_20250211_01,重复运行时删除旧批次、新建新批次,这样既避免堆积,也为出问题留了后悔药。
4.4 现象:外部模式下Update后视图不刷新,像卡死
现象:程序没报错,日志说特征已创建,但屏幕上就是看不到新的Block。原因:外部模式不依赖界面消息循环,模型更新后视图不会主动重绘;不是创建失败,是没刷新。解决:创建后主动刷新视图:
theSession.Views.Work.ViewRefresh(); // 强制刷新当前工作视图如果是无人值守远程跑批,界面都可能不开,刷新也看不到,那就别依赖视图验证,改为读取feature和part的attribute属性汇总到日志。远程或后台运行时,不要弹NX对话框去确认,一旦弹窗没有人工点,整个任务就停在那里。有对话框需求时一律改成自己的日志和错误码。
4.5 现象:Block建出来后和预期位置差一个WCS
现象:原点明明传的是(0,0,0),Block却出现在离模型很远的位置。原因:API的Point3d是绝对坐标系,设计时用的WCS可能已经旋转或平移过了。解决:换算坐标时统一走绝对坐标系;如果需要对齐WCS,用Matrix4x4或UF_CSYS相关函数做变换。不要“看着差不多就行”,Block原点是后续所有布尔和装配的基准,错一点,后面孔位、开槽全乱。
5. 从单个Block到批量自动化:命名、图层与回滚
5.1 用特征名和图层管理几百个Block
创建单个Block不是目的,批量才是。批量时我会把Block放进指定图层,并用有规则的FeatureName,方便后续筛选、排序和清理:
for (int i = 0; i < paramList.Count; i++) { Point3d origin = new Point3d(baseX + i * 120, 0, 0); Features.Feature f = workPart.Features.CreateBlock(origin, edgeLengths, true); f.FeatureName = string.Format("BLK_{0:D4}", i); // 统一命名规则 workPart.Layers.SetLayerForFeature(f, 1); // 放进1层 } workPart.Update();命名规则一固定,后面按名称删除旧批次、按图层批量设置颜色或属性就都有了抓手。工业自动化里交付的prt不只问“有没有Block”,还要问“哪个是毛坯、哪个是余量”。
5.2 参数化回滚:用表达式而不是死数值
CreateBlock的边长接受表达式字符串,这是它比UF好用太多的地方。“100/2”、“20+5”都能解析。想批量调整时,不需要重新创建Block,改表达式即可:
Expression expr = workPart.Expressions.FindObject("BLK_L_X"); expr.SetRightHandSideString("150"); workPart.Update();表达式名字要预先规划,我习惯在FeatureName外再存一套特征参数属性。这样回滚和复用都简单,遇到供应商模型批次差异,改一个表达式比重新跑一遍生成脚本安全得多。
5.3 给自动化脚本加事务边界
外部模式跑批最容易出的事故是跑到一半失败,留下一堆半成品。我现在给每批任务加两个固定节点:任务开始前记录清理锚点,任务结束后统一校验数量与尺寸;中途失败就整批删除新建特征。外部开发模式下没有界面给你按撤销,删除操作本身要找对对象,所以锚点加命名规则就是无人值守的后悔药。
有一次我图省事,没做清理锚点,直接在一个批次的Block上追加了下一个批次的Block,结果特征依赖全缠在一起,返工花了比写代码更长的时间。从那以后,我养成了一个习惯:凡是批量创建Block的程序,第一步永远是“删干净上一批”,第二步才是“建这一批”,日志里补全参数快照。希望帮到你。
本文还有配套的精品资源,点击获取