做NX二次开发的朋友应该都遇到过这个需求插件弹一个文件选择框让用户从硬盘上挑一个文件回来处理。最简单的做法就是用UFUN里的UF_UI_create_filebox函数它的调用方式非常直白一两分钟就能跑通。但我在实际项目里真正遇到麻烦的是这个函数能不能支持多个文件过滤器——就是Windows文件对话框里那个文件类型下拉框能同时列出.prt、.stp、*.igs几类文件。我第一次的直觉是不支持查了一圈资料才搞明白UFUN在这里留了口子filter参数其实可以塞进多组过滤规则。这篇文章就把这个口子彻底讲清楚顺便把我踩过的坑都摊开来说。1. 从选型说起UFUN文件框函数的前世今生1.1 UF_UI_create_filebox的基本签名在NX二次开发的C接口体系里文件选择框最经典、最容易被新手找到的函数就是UF_UI_create_filebox。它属于UFUNUser Function工具集声明在uf_ui.h头文件里负责弹出一个标准文件选择对话框。基本签名是这样的int UF_UI_create_filebox( const char *title, // 对话框标题 const char *initial_directory,// 初始目录 const char *filter, // 文件过滤器 const char *extension, // 默认扩展名 int mode, // UF_UI_OPEN / UF_UI_SAVE char *file_name, // 返回的文件名 int *response // UF_UI_ACCEPT / UF_UI_CANCEL );参数不多但每一个都值得细看。title就是弹窗标题栏上显示的文字比如请选择要导入的模型文件initial_directory是对话框打开时定位到的文件夹路径filter是文件过滤规则比如*.prtextension是当用户没输入扩展名时自动补齐的后缀比如prtmode区分你是让用户打开文件还是保存文件最后两个是输出参数file_name带回用户选择的完整路径response告诉你用户到底点的是确定还是取消。对刚入门NX二次开发的人来说这个函数最大的优点是简单不需要你去创建复杂的UI界面一个函数调用就能拿到结果。它的内部封装了NX自身的文件对话框逻辑无论是老版本NX还是新版NX外观风格都能跟系统保持统一不会出现一个四不像的自绘窗口。1.2 它和UF_UI_fileselection、NXOpen FileDialog是什么关系很多人在群里问为什么我调用UF_UI_create_filebox只能选一个文件其实这里有个概念要区分清楚。UFUN体系里还有一个稍微进阶的函数叫UF_UI_fileselection它支持多文件选择也支持更丰富的过滤器配置但结构体参数比UF_UI_create_filebox复杂一些后面我会单独讲。另外在NXOpenC/C#体系里还有更现代的NXOpen::UI::FileDialog类这个类几乎能覆盖所有文件对话框需求甚至支持分页签、自定义控件。函数/类所属技术栈多文件选择多过滤器支持代码复杂度维护状态UF_UI_create_fileboxUFUNC不支持支持低经典稳定UF_UI_fileselectionUFUNC支持支持中可用NXOpen::UI::FileDialogNXOpenC支持支持中高官方推荐我这几年做插件有个经验如果是做内部小工具、快速验证逻辑、或者需要在老代码里补一个文件框别犹豫直接用UFUN的UF_UI_create_filebox。但如果是要做面向大量用户的正式功能跟NX其他界面有大量交互我一般会直接上NXOpen的FileDialog因为它后续扩展空间大。这篇文章的标题既然点名了UFUN我下面的重点就放在UFUN这条线上NXOpen只做对比和备选。2. 多过滤器是怎么实现的filter参数的拼装规则2.1 先看单过滤器的写法最开始我写文件框时filter参数是这么写的char filter_buf[256] *.prt; UF_UI_create_filebox( 选择PRT文件, D:\\work, filter_buf, prt, UF_UI_OPEN, file_name, response );这样对话框打开后文件列表里只会显示.prt结尾的文件。Windows系统自带的文件对话框这个位置通常是一个文件类型下拉框下拉框里每一项就是一个过滤器规则。问题是UFUN这层接口到底能不能让下拉框出现多项2.2 多过滤器的拼装方式答案是可以。UF_UI_create_filebox的filter参数解析规则比表面看起来要灵活它允许你用竖线字符|分隔多组过滤器我在NX2206系列上实测是稳定生效的。组合格式大概是这样char filter_buf[1024] NX Part File (*.prt)|*.prt |STEP File (*.stp)|*.stp |Parasolid (*.x_t)|*.x_t |All Files (*.*)|*.*;每一组过滤器由两部分组成前面是给用户看的描述文字写在括号里展示扩展名后面紧跟|加实际匹配规则。组和组之间再用|分隔。这样在对话框的文件类型下拉框里就会出现四项NX Part File (*.prt)STEP File (*.stp)Parasolid (*.x_t)All Files (.)把这段filter字符串传进去初始显示的下拉框第一项会被默认选中文件列表自动按*.prt过滤。用户切换下拉框选项时文件列表会跟着变化。有一个细节要注意UF_UI_create_filebox还接收一个独立的extension参数。很多人不理解为什么有了filter还非要写extension其实这两个角色的分工完全不同filter决定的是文件列表里能看见什么而extension决定的是用户直接敲了一个不带扩展名的文件名进去之后系统自动接什么后缀。比如用户想保存文件输入了part_model没有输入扩展名这时extension参数是prt最终保存路径就会变成part_model.prt。2.3 默认过滤器和顺序的细节多过滤器组合好之后默认选中的是第一个。如果想默认选中某个后期项一个变通做法是调整组合顺序把那项提到最前面。UFUN这个老接口没有提供指定默认选中第几项的参数这个限制我在实际项目里确认过。另外不同NX版本的解析规则有细微差别。NX 5之前的老版本还在用Motif风格对话框filter识别比较粗对描述|模式这种带描述的写法支持不完整NX 5之后换成标准Windows风格文件对话框这种写法就稳定了。如果代码要在NX 5以前的环境跑建议老老实实用简单模式串*.prt|*.stp不要带描述部分避免出现下拉框空白。早期的NX文档里还有一种写法是用分号把多个扩展名合并成一个过滤器规则比如*.prt;*.stp这种写法是让一个过滤器项同时匹配多种扩展名跟多过滤器是完全不同的两个概念。一个是一个选项选多种文件一个是多个选项分别选一种文件。设计功能时要想清楚用户需要哪种别写混了。3. 实战写一个支持多过滤器的文件选择框3.1 环境准备与头文件用C/C做UFUN插件最基础的环境是NX的Open C开发包。项目里需要包含的头文件主要是#include uf.h #include uf_ui.h #include uf_ui_types.h链接的阶段需要把NX的库目录加到工程里通常是NX安装目录下的UGII和NXOPEN相关lib路径。具体配置跟编译环境有关Visual Studio就直接在附加依赖项里把libnxopen_ui.lib之类的库加进去不同的NX版本库名稍有差异最稳妥的做法是在NX安装目录里搜索libufun.lib这个文件它是UFUN函数库的核心。还需要注意一点UFUN的很多函数必须在NX环境中运行不能脱离NX进程直接跑控制台程序。我最早犯过一个低级错误想写个独立小程序测试UF_UI_create_filebox结果一调用就报错后来才明白UFUN是寄居在NX进程里的插件必须以NX内部加载的方式执行比如用File - Execute或者做成Menu脚本调用。3.2 完整代码实现直接给一个我项目里实际用过的函数这个函数封装的比较完整包括初始化、目录处理、结果解析和错误清理#include uf.h #include uf_ui.h #include uf_ui_types.h #include string.h #include stdio.h int SelectModelFile(char *selected_path, int path_buf_size) { int rc 0; int response 0; char title[128] 请选择模型文件; char init_dir[512] ; char filter_buf[1024] NX Part File (*.prt)|*.prt |STEP File (*.stp)|*.stp |Parasolid (*.x_t)|*.x_t |All Files (*.*)|*.*; char ext[32] prt; char filename[1024] ; UF_initialize(); // 如果没有指定初始目录就去取当前工作部件所在的路径 // 没有工作部件时回退到用户默认目录 char cur_dir[512] ; UF_get_current_directory(cur_dir); if (strlen(cur_dir) 0) { strcpy(init_dir, cur_dir); } rc UF_UI_create_filebox( title, init_dir, filter_buf, ext, UF_UI_OPEN, filename, response ); if (rc 0 response UF_UI_ACCEPT) { // 检查路径长度避免把缓冲区写爆 if ((int)strlen(filename) path_buf_size) { strcpy(selected_path, filename); rc 0; } else { rc 1; } } else { // 用户点了取消 rc 1; } UF_terminate(); return rc; }这个函数不是我随手写的里面几个处理点都踩过坑UF_initialize和UF_terminate这对调用是UFUN的安全锁。UFUN的很多内部逻辑依赖一个全局初始化状态如果你跳过了UF_initializeUF_UI_create_filebox本身也许能弹出来但后续跟NX内核交互的其他调用很可能会莫名其妙出错而且这种错误往往是偶发的极难排查。所以养成好习惯每个入口函数开头UF_initialize结尾UF_terminate。3.3 关键参数解释与调用约定再看几个容易忽略的参数mode参数UF_UI_OPEN是打开文件UF_UI_SAVE是保存文件。如果是保存场景filter和extension的语义依然起作用但用户看到的是文件名输入框而不是文件列表。这个模式同时影响返回结果用户也可能输入一个不存在的文件名这在保存场景下是合法的。initial_directory如果传空字符串某些NX版本会回退到上次访问的目录有的版本会打开用户默认目录。最好在调用前主动查一下当前工作部件路径尽量给用户一个合理的初始位置。file_name缓冲区函数对缓冲区大小没有显式保护如果你传了一个很小的char file_name[64]用户选到深路径文件时直接溢出。我在实际项目里统一用char filename[1024]目前够用。更好的做法是把缓冲区定在MAX_PATH 256以上毕竟Windows扩展路径可能很长。编译运行后对话框会出现在NX主窗口的顶层文件类型下拉框可以切四种过滤规则。选择文件后函数内部会把完整路径拷贝到filename我们只需要做字符串拷贝和结果判断。4. 实际使用中的五个坑与对应排查方案4.1 过滤器下拉框空白或只有所有文件这是多过滤器写法最常见的翻车现场。现象是对话框弹出来了但文件类型下拉框里什么都没有或者只有默认的All Files。根因filter字符串里混入了空格、空组或者非法字符。比如你写NX Part (*.prt) | *.prt描述和模式之间多了一个空格或者模式前面多了一个空格解析器一旦读出了不匹配的模式整个过滤器串都有可能被丢弃回退到默认的所有文件。排查方法把filter用十六进制打印出来逐个字符检查。我有一次是从配置文件读取过滤规则文件里行尾带了\r\n直接拼到字符串里结果弹出来的对话框过滤器就是乱的。后来在读取后主动去掉\r和\n问题消失。标准做法过滤器字符串里空格只允许出现在描述文字中间描述括号前后、分隔符周围不要有空格。模式部分更不要有任何空白字符。4.2 返回文件名的字符集与路径问题NX的C接口字符串底层走的是多字节字符集还是宽字符集跟编译选项和NX版本都有关系。我遇到过最典型的场景是用了VS编译NX插件默认是Unicode字符集结果UF_UI_create_filebox返回的路径在中文路径下乱码。排查方法不要想当然地直接strcpy可以先把返回的filename逐字节打印成Hex观察。如果发现中文路径字节被截断或者字符错位基本可以判断是字符集转换问题。解决方案把工程的字符集临时切到使用多字节字符集再试一次或者干脆在代码里做一个UTF-8到本机编码的转换。如果你的NX版本底层是UTF-8而你的工程默认走Unicode可以考虑用WideCharToMultiByte做显式转换。这个问题的本质是NX对本地化字符集的支持一直是历史包袱不同版本行为不一致唯一可靠的解决思路是在目标NX版本上实测并固定一种字符集方案。4.3 点取消后程序直接崩了这是一个很低级但很多人会踩的坑用户点了取消response返回UF_UI_CANCEL代码没判断就直接用了file_name结果崩溃。因为取消时file_name的内容是不确定的可能是空字符串也可能是残留的旧值。标准做法判断逻辑严格一点先判断rc 0再判断response UF_UI_ACCEPT两个条件都满足才去处理file_name。我在文章前面给的示例函数里就是这么写的。别只看rcrc只能说明函数本身执行没有异常不能代表用户点了确定。4.4 默认目录没生效有朋友反映initial_directory传了D:\\work但对话框打开后还是跑到别的地方去了。常见原因字符串里的反斜杠被转义吃掉了。C语言里D:\work会被解析成D:work看起来好像没什么区别但系统去访问这个不存在的路径失败后会回退到默认目录。正确写法是D:\\work或者用正斜杠D:/workWindows API对正斜杠兼容性OKNX内部也能识别。另一个原因路径根本不存在。系统不会给你创建一个新目录目录不存在时对话框会直接落到默认位置。所以调用前最好做一个目录存在性检查不存在就回退到.。4.5 非UI线程调用时的卡顿与异常UFUN不属于严格的线程安全接口UF_UI_create_filebox这种UI相关函数尤其敏感。如果你在后台计算线程里直接调用它轻则对话框弹不出来重则NX进程直接崩溃。原因NX的UI系统运行在主线程的消息循环里跨线程调用UI函数等于绕过了消息泵系统无法正确处理窗口事件。方案把文件选择框调用放到UI线程或主线程。NXOpen C里可以用theSession-setUndoMark配合UI线程调度也可以用NXOpen::UI::UI::GetUI()-BlockPoption这类机制规避。反正记住一条文件选择框是UI操作不要放到后台计算线程里去弹。如果确实需要在后台逻辑中触发先跑到UI线程再弹。5. 如果想多选文件UFUN与NXOpen两条路线对比5.1 UFUN的UF_UI_fileselection多选方案文章开头我提过UF_UI_fileselection它才是UFUN体系里真正支持多文件选择的接口。这个函数不通过一堆散参数传值而是让你填一个UF_UI_file_selection_t结构体。核心字段大概是这样不同NX版本的字段名可能略有差异以本机头文件为准UF_UI_file_selection_t sel; memset(sel, 0, sizeof(sel)); strcpy(sel.title, 选择多个文件); strcpy(sel.directory, D:\\work); strcpy(sel.filter, *.prt|*.stp|*.igs); // 多过滤器规则 sel.mode UF_UI_OPEN; sel.multi_select 1; // 关键开启多选 void *response NULL; int rc UF_UI_fileselection(sel, response);开启多选后文件列表允许用Ctrl和Shift选择多个文件选择结果会存在结构体或返回参数里。这个方案比UF_UI_create_filebox复杂在你要处理多个文件名结果而且response返回值在旧版NX里是一个内存指针需要自己管理释放稍不留神就造成内存泄漏。所以我的观点是除非你的项目严格锁死在C接口上否则多选场景我更推荐NXOpen方案。5.2 NXOpen推荐做法与代码示例NXOpen C的FileDialog是我现在的主力方案。它不但支持多选和多过滤器还能在对话框里加自定义控件、设置回调函数。多文件选择的典型写法如下NXOpen::UI::FileDialog *fileDialog theUI-FileDialog(); fileDialog-SetTitle(选择多个模型文件); fileDialog-SetFilterIndex(0); fileDialog-AddFilter(NX Part File (*.prt), *.prt); fileDialog-AddFilter(STEP File (*.stp), *.stp); fileDialog-AddFilter(Parasolid (*.x_t), *.x_t); fileDialog-AddFilter(All Files (*.*), *.*); NXOpen::Session::UndoMarkId markId; markId theSession-SetUndoMark(NXOpen::Session::MarkVisibility::Visible, FileDialog); NXOpen::UI::FileDialog::ResponseType resp fileDialog-Show(); if (resp NXOpen::UI::FileDialog::ResponseType::Ok) { std::vectorNXOpen::NXString selectedFiles; selectedFiles fileDialog-GetSelectedFiles(); // 遍历 selectedFiles 处理每个路径 } fileDialog-Dispose();NXOpen的代码写起来确实比UFUN长但缓冲区和内存管理都更安全。GetSelectedFiles()返回一个字符串数组怎么处理都不会溢出了。而且AddFilter接口是真正的多过滤器接口每个过滤器都有明确的描述和模式不会再出现像UFUN那样拼一个长字符串然后担心解析不稳定的问题。5.3 什么时候该放弃UFUNUFUN毕竟是NX二次开发的老前辈在很多存量代码里依然健在。但如果你做的是新项目我真心建议优先考虑NXOpen。理由很实际UFUN的对话框函数在界面风格上跟新版NX有代差走的是兼容层路线有些新版主题下的显示效果会发灰发暗。UFUN函数对中文路径、多字节字符集的支持一直不稳定每升级一个NX版本都可能翻车。NX官方在持续维护NXOpen体系新功能基本都优先覆盖NXOpenUFUN基本处于能跑就不修的状态。当然如果只是为一个内部小工具快速加一个文件框或者你的项目已经有一堆UFUN调用、改造成本很高那继续用UF_UI_create_filebox完全没问题。这篇博客的核心目标就是帮你把UFUN这个老接口用得明明白白——就算未来要换路线至少现在能把活儿干完。我在实际项目里的体会是文件选择框这个功能看起来很不起眼但它处在用户交互的第一线弹窗逻辑、过滤规则、返回结果任何一个环节出现问题用户的直观感受就是这个插件不好用。拿UF_UI_create_filebox来说很多人以为它只是一个简单封装实际上filter参数留的口子足够应对绝大多数文件类型切换场景。多过滤器字符串的拼装规则说白了就是三句话描述和模式用竖线隔开、多个过滤项之间也用竖线隔开、字符串里不要出现多余空格。把这三点记住基本就不会再在过滤器上翻车了。最后再补充一个小技巧如果你的插件经常需要处理带后缀的模型文件比如公司内部定的.mprt、.cae这种自定义格式完全可以用同样的写法加到filter里UFUN不会因为扩展名不常见就嫌弃它只要模式字符串写得对解析器一视同仁。