CATIA CAA二次开发实战:利用CATMathBox自动测量零件长宽高 简介面向CATIA二次开发学习者的第06讲CAA编程代码包适合已有基本CAD建模经验、希望进入三维设计自动化领域的技术人员。内容围绕CAA架构认知、工程创建、对象操作与API接口调用等主题准备了可直接研读的示例代码。rar压缩包内共收录2000个文件以h头文件、cpp源文件、obj编译产物、catnls本地化资源和bmp界面位图为主体并附带dll动态库、lib静态库、sln/vcproj工程文件及bat构建脚本等整体大小31.74MB目录结构贴近真实CAA开发工程便于对照学习。已有501人学习下载。通过研读这些源码与资源配置可以完整看到从环境配置、模块创建、对象操作到插件部署的落地实现理解TopoDS_Shape几何处理与文档对象管理等关键接口的具体用法稍加改动即可迁移到自己的CATIA插件项目中。1. 第06讲这套CATIA CAA代码资源到底解决谁的什么问题当你拿到一个标注着“第06讲代码资源_CATIACAA_”的压缩包时里面少则几百行、多则几十个.cpp和.h文件第一反应往往不是兴奋而是不知道从哪开始看。这套资源讲的是CATIA CAA二次开发核心价值是提供一组能编译、能挂进CATIA菜单栏、能直接改来用的可复用代码框架覆盖命令注册、几何遍历、特征识别、批处理输出这些CAA开发的高频场景。它适合三类人刚接触CAA、连Workspace都建不顺的新手需要在具体版本上做自动测量、批量导出等改造的老手以及手上有资源但不确定哪些API能复用、怕抽错边界的开发者。别急着全编先看它到底能解决什么问题再动手。2. 先把CAA环境立起来从版本选型到第一个能跑的编译工程2.1 CAA开发的第一步是“版本对齐”不是写代码很多第一次接触CAA的人会先打开Visual Studio去找“新建CAA项目”按钮然后发现根本没有。这不是因为你装错了而是CATIA CAA从来不是一个独立的IDE它是在Visual Studio基础上叠加了一套工具链和运行时库。真正拦在大家面前的第一道门槛不是语法而是版本对齐。CAA的全称是Component Application Architecture它的开发依赖三样东西CATIA本体、CAA RADE可以理解为CAA开发环境插件、以及特定版本的Visual Studio。这三者的版本必须严格匹配差一点都会导致后面编译时出现一堆“找不到CATIA头文件”或者“无法启动调试器”。常见组合里V5R21那一代大多配VS2008V5-6R2016那代配VS2013V5-6R2017之后基本是VS2015起步。具体配什么以你安装盘里自带的releasenote为准不能拿百度上随便搜到的组合硬套。版本对齐为什么很重要因为CAA的底层DLL是区分编译器和运行库的同一个功能VS2008编译出来的模块和VS2013编译出来的模块二进制格式不一样。你用VS2013打开一个老资源项目如果没有先做“代码迁移”它会在预处理阶段就挂掉。我见过最快的一个翻车案例环境装好、工程打开、一条命令没写第一板就崩在mkmk上。所以拿到第06讲这套资源先别急着研究代码老老实实确认三件事你的CATIA版本是多少、对应CAA RADE装没装、VS版本对不对得上。这三件事任何一件错了后边全是白干。版本对齐的一个容易忽略的细节是“补丁版本”。CAA对CATIA的HotFix补丁也很敏感比如V5R21的GA版本和SP2版本头文件路径里都带版本号。如果资源里的路径参数写死在SP2而你的是GA编译时就会找不到CATI3DShape.h一类的东西。这种问题最坑因为它不会报“路径不存在”而是报“无法打开包括文件”。遇到这种先去看mkcat生成的env文件路径里有没有跟你系统里对不上的补丁号。2.2 用命令验证环境编译一个范例模块的三个动作版本对好之后验证环境是否真的可用标准做法是先把资源自带的一个空Module编译出来。CAA不推崇鼠标右键编译它的编译动作都在命令行里完成。CAA装好以后开始菜单里会多出一个“CAA Command Prompt”入口点击进去就是一个带CAA环境变量的命令提示符。在这个命令行里最常做的三个动作是创建Workspace、进入Framework目录、编译Module。对应命令大概是这样的# 1. 在工作区目录下创建Workspace指向你的资源目录 mkcat -ws MyWorkspace -p D:\CAA_Code\Chapter06 # 2. 进入Workspace下的Framework并编译所有模块 cd MyWorkspace\YourFramework mkmk -m YourFramework -f这里-ws后面跟的是Workspace名称-p是指向现有代码资源的路径mkmk是整个CAA构建系统的核心命令-m是指定要编译的Module名-f是强制重新编译关键文件。注意不同版本CAA的mkcat参数略有差别但-ws、-p、-m这几个几乎没变过你只需要把YourFramework换资源包里的真实名字。如果编译成功最后会提示生成一个.dll位置一般在WorkSpace\Generated\YourFramework\intel_a\code\bin下。这个intel_a是平台目录对应64位Windows。有位同学在这卡了两天因为他把dll拷到CATIA的同一个intel_a\code\bin目录里覆盖了同名DLL。这是另一个坑CAA模块要部署到CATIA安装目录\intel_a\code\bin但覆盖前最好先备份否则会导致CATIA启动直接崩。编译过的第一个Module能跑起来环境就算站住了。后续你再看第06讲的代码资源时至少不会因为“连编译都过不了”而怀疑人生。注意如果编译过程中看到大量warning C4251不用管那是DLL导出模板的常见警告不影响功能。2.3 字体库与JT文件打不开多半是环境变量和通道没配好很多人在B站或百度上搜“catia字体库下载”“JT文件怎么用catia打开”然后下载了一堆字体或插件装进去依然报错。在CAA开发视角下这类问题十有八九不是缺文件而是环境变量或通道没配对。CATIA的资源体系里字体库、命令图标、消息目录都放在resources目录下CAA通过CATStartPath这类环境变量去定位它们。如果你自己建了一个Workspace但没把资源目录加进CATICPath那么哪怕你已经把字体文件放到CATIA安装目录里界面依然会显示成方框。JT文件打不开也是一样。CATIA读JT不是靠“直接打开”而是通过DataExchange模块的导入通道。CAA代码里要触发这个通道得调用CATIExchangeAddin一类的接口并且要确保你有相应的格式许可证。没有许可证的情况下你用宏录制的Documents.Add(JT, ...)代码大概率报“没有可用的事务”。这时候很多人以为是CAA代码问题其实是CATIA的安装组件没装或者许可类型不对。所以在你用这套代码资源做二次开发前先顺手解决这两件事把CAA的resources目录挂到环境变量里把DataExchange相关的组件装齐。别小看这一步资源包里凡是涉及界面命令和模型导入的功能都会间接依赖这两个基础环境。你不想在调试代码时突然发现字体变成豆腐块也不想在测导入模块时被JT文件卡住这都属于“环境债”早还不亏。3. 看懂代码资源的“骨架”从Framework到命令注册3.1 一个CAA资源包里的三层结构Workspace、Framework、ModuleCAA的代码组织跟普通的C工程不一样它是强迫症级别的分层。一个Workspace就是一个工作区对应你从压缩包里解出来的那一大堆文件夹Workspace下面有多个FrameworkFramework可以理解为“功能包”每个Framework里又分成若干ModuleModule才是真正参与编译的单元每个Module有自己的src、include、resources目录。第06讲这套资源如果命名规范你会在根目录看到类似Chapter06Framework这样的文件夹进去后有Module1、Module2。每个Module里的src/放.cpp和部分.hinclude/放对外发布的头文件resources/下还有CNext/commands目录定义命令ID、图标、简短提示。这些目录结构不是摆设编译脚本会按这个约定去收集源文件。所以你拿到资源后不要新建一个“全拼一体”的.cpp放入任意目录那样工程根本识别不了。理解这一层结构的意义在于你要定位“自动测量长宽高”这类功能直接去src目录搜Measure或Box关键字比在CATIA界面里找快得多。Framework和Module之间的关系决定了依赖方向你要在一个新Module里调用资源包里的几何函数就必须在mkcat或Imakefile里声明对那个Framework的依赖否则编译器找不到头文件。这种依赖关系是CAA把“代码复用”做成工程级约束的体现也是很多人从普通C转过来最不适应的地方。有一个快速判断“这个Module是干什么的”的方法看它的Imakefile。CAA每个Module根目录都有一个Imakefile没有扩展名但里面写了这个模块依赖哪些其他Framework。如果一个Module的Imakefile里出现了CATApplicationFrame、CATInteractiveInterfaces那它多半跟UI命令相关如果出现CATGMOperators、CATTopologicalOperators那它是几何运算模块。第06讲资源里如果既有UI又有几何你会发现Framework被拆得很干净这是故意做的目的是让你能只抽走几何部分而不带上鬼一样的界面代码。3.2 先找“入口文件”CreateCommands和InitInstance是暗号代码资源里代码文件很多但真正作为“入口”的就那么两三个。CAA命令模块的入口通常藏在src目录下的CreateCommands.cpp或CreateCommands.h里。CreateCommands函数是CAA框架规定的一个全局函数CATIA主程序启动后会遍历各个Framework调用这个函数来完成命令注册。你在这个函数里看到的每条AddCommand调用都相当于把资源里的一个按钮放到CATIA命令列表里。另一类入口文件是Init.cpp或InitInstance。它负责初始化模块里的资源比如加载消息目录、设置资源字符串。如果你的代码资源包里有CATMsgCatalog资源那么启动时如果这条初始化路径断了命令名会直接显示成ID号。这两个入口文件就是“暗号”定位到它们你就能快速画出这个资源包的功能地图。比如CreateCommands里有三个AddCommand那这个包大概率有三个独立功能每个命令后面关联的类名就是你接下来要看的核心类。我第一次拿到别人写的CAA资源时花了整天去读一个500行的.cpp后来发现那个类根本没在CreateCommands里注册。CAA的机制决定了函数的调用链是“向导式”的命令类必须经过注册才能被CATIA实例化。你单独把命令构造函数翻出花来也没用入口才是关键。正确的阅读顺序是先CreateCommands再顺着它看到的命令类名去找BuildGraphic、Activate这些虚函数实现。3.3 资源包里的可复用API把“几何遍历”抽出来单独存代码资源里最有价值的不是某个完整命令而是那些可以脱离UI独立调用的函数。比如“遍历一个CATPart里所有Body的所有Face”这种基础能力。CAA的UI命令类往往混杂着消息循环和状态管理直接复制会带进来一堆用不上的依赖。但底层几何函数通常写在src里的几何工具类中或者以Free Function形式暴露在头文件里。你把它抽出来做成独立的小工具函数后续写批处理、写自动测量都会很爽。怎么判断哪些可以抽标准很简单看函数是不是只依赖“输入数据接口返回”不依赖窗口句柄、命令上下文。比如一个函数接收CATIPart_var返回CATMathBox这就是典型的可复用API。而一个函数接收CATCommand在内部弹出对话框这种就得整块搬走或者直接放弃。在第06讲这种情况下我建议先在资源包里搜“Box”“Boundary”“Extent”这些词定位到几何求值代码然后单独Copy到一个GeomUtils.cpp文件里。不要小看这一步操作后面你要做“自动测量零件长宽高”时你会发现CATIA自带的Measure Between只能一个人手工点三个面根本没提供CAA直调接口而资源里的几何工具函数正好能补上这个缺口。4. 把代码资源改造成“自动测量长宽高”功能可直接抄的零件样例4.1 测量长宽高的核心用CATMathBox算三个方向极值自动测量零件长宽高产品需求大概是这样打开任意一个CATPart程序自己算出这个零件在X、Y、Z三个方向上的最大包络尺寸分别作为长、宽、高输出不需要用户手工去点面。这个需求在CAA里最直接的实现方式不是调用交互测量命令而是计算包围盒Bounding Box。CAA里描述包围盒的类是CATMathBox它本质上是一个数学空间盒子记录了三个坐标轴方向上的最小值和最大值。你只要拿到零件所有几何元素的包围盒再对它做一遍“合并取并集”最后用最大值减最小值就得到长宽高。注意这个“所有几何元素”必须包含实体的所有面、片体、以及可能的外形基准面否则算出来的是残缺尺寸。很多新手会跳进一个坑试图去遍历每个面的CATSurface用自己的方式求极值。这能做但代码量会爆炸。CAA里更稳的做法是通过CATIGeometricalElement接口去拿Get3DBox把这个结果不断合并到全局CATMathBox中。如果你的CATIA版本较老Get3DBox不一定存在那就需要在资源包里找旧版CATMathBox::Union方法。第06讲资源里通常已经把这些版本差异封装好了希望你拿到的不是那种纯裸指针对砸的原始版本。4.2 完整可跑的CAA代码块遍历Part并输出尺寸到控制台下面这段代码是核心逻辑的可读版本。它已经把资源包里的“遍历几何元素”封装成了一个名为CollectGeometries的辅助函数实际代码里你直接把那个辅助函数换成资源包里的同名实现即可。// MeasurePartDim.cpp // 功能输入CATIA Part对象输出长宽高数组单位mm #include CATIPart.h #include CATIGeometricalElement.h #include CATMathBox.h #include CATMathVector.h #include iostream #include vector // 辅助函数从Part中收集所有需要参与尺寸计算的几何元素 // 在资源包里可能有现成的同名或近名函数直接改一下类型即可 static std::vectorCATIGeometricalElement_var CollectGeometries(const CATIPart_var iPart); bool MeasurePartDimensions(const CATIPart_var iPart, double oLWH[3]) { if (NULL_var iPart) return false; // 1. 收集几何元素 std::vectorCATIGeometricalElement_var geometries CollectGeometries(iPart); if (geometries.empty()) return false; // 2. 用全局Box保存合并后的极值 CATMathBox globalBox; bool hasInitialBox false; for (size_t idx 0; idx geometries.size(); idx) { CATMathBox box; HRESULT hr geometries[idx]-Get3DBox(box); if (SUCCEEDED(hr)) { if (!hasInitialBox) { globalBox box; hasInitialBox true; } else { // 把当前面/体的Box合并进全局Box globalBox.Union(box); } } } if (!hasInitialBox) return false; // 3. 取全局Box的角点差值 // GetInfPt/GetSupPt是CATMathBox的两个端点接口 // 这里写成GetLowerPoint/GetUpperPoint是为了直观资源包里常做这种包装 CATMathVector lower globalBox.GetLowerPoint(); CATMathVector upper globalBox.GetUpperPoint(); oLWH[0] fabs(upper.GetX() - lower.GetX()); oLWH[1] fabs(upper.GetY() - lower.GetY()); oLWH[2] fabs(upper.GetZ() - lower.GetZ()); return true; }代码的逻辑分三步先收集几何元素再逐个求CATMathBox并合并最后用全局盒子的两个角点算差值。第一段中CollectGeometries在第06讲资源里通常已经有实现因为它属于“读取模型”的基础能力。你只需要确认它返回的元素类型是不是也支持CATIGeometricalElement如果不支持就把它返回的元素挨个QueryInterface一下。参数方面oLWH是输出数组顺序固定为[X方向尺寸, Y方向尺寸, Z方向尺寸]。调用者可以根据需要把第一个当成“长”第二个当成“宽”。要注意CATMathBox的默认坐标参考是全局坐标系GSD如果你的零件有用户坐标系UCS必须先做坐标变换这会在下一节说。4.3 参数和边界条件单位、坐标系、非实体几何的注意事项这节是真正会翻车的地方。先说单位。CAA内部几何运算的长度单位是mm角度是弧度。听起来简单但如果你在某个模块里提前把数据从mm换算成了m再丢给这个函数长度会直接缩小1000倍。更隐蔽的是CATIA模型里有些参数的内部存储单位可能是m比如某些API返回的变体值你需要认真看头文件注释不能假设所有返回都是mm。坐标系的问题是最常见的“差之毫厘谬以千里”来源。前面说的全局Box是在“模型绝对坐标系”下算的。但用户实际要求的长宽高往往希望是“平行于设计坐标系”或“平行于某个轴系”。这时候直接拿上下点做减法得到的是绝对坐标系下的包络如果模型是斜着摆的计算出的长宽高会明显大于用户拿测量工具沿一条边量出来的值。解决思路是先把所有几何点投影到目标坐标系或者你在调用Get3DBox之前对几何元素做一个坐标系旋转。CAA里可以做CATMathTransform乘法资源包里也可能已经封装了TransformBoxToAxisSystem优先用现成的。非实体几何也是个隐蔽坑。CATIA里面有很多“构造线”“参考平面”“外部参考”等元素它们属于几何但不应该参与零件真实长宽高的计算。如果资源包的CollectGeometries不加过滤计算出的尺寸会把那些离实体很远的参考线也算进来数值大得离谱。常见的过滤条件有两个一个是特征类型排除Construction属性为True的元素另一个是排除Datum基准元素。在第06讲这种教学资源里可能故意塞了一个不过滤的版本目的就是让你调试时发现不对然后自己加过滤条件。遇到这种情况别慌在CollectGeometries里加一条if (IsConstruction(elem)) continue;就能解决。5. 避坑/常见问题CAA开发最容易翻车的5个地方5.1 现象编译通过一启动就崩溃这是一个特别典型的“编译过了不代表环境可用”的案例。你按照命令把DLL编译部署好打开CATIA点击命令按钮CATIA直接闪退事件查看器里能看到0xc0000005内存访问冲突。原因有两种第一种你的Module没有正确初始化CATSession就试图访问模型对象。第二种更常见命令类里使用了未初始化的智能指针比如CATIPart_var spPart;用了空指针直接调用方法。在CAA里COM接口的返回失败不是靠异常而是靠HRESULT很多初学者忘了检查SUCCEEDED(hr)。解决方式第一步先加日志。在MeasurePartDimensions函数开头写一个std::ofstream输出日志文件把每一步的返回值和指针打印出来定位到具体崩溃行。第二步检查所有CATIPart_var的赋值语句是否都判断了NULL_var。CAA里的NULL_var跟普通C空指针不一样直接等于比较可能带宏转换建议用if (NULL_var spPart)这种写法。我见过有人因为这个崩溃查了三天最后发现是某接口返回了一个无效指针但他没有检查该接口的HRESULT。5.2 现象测量结果和CATIA自带工具差了0.01mm这可能是“单位”或“边界表示”的差异。CATIA的测量工具默认使用“曲面上最远两点”的欧几里得距离而包围盒算法用的是“绝对坐标极值”。当模型的面是斜面时包围盒尺寸会比沿面测量的距离偏大当模型有曲面时包围盒又可能比最小包络略小。所以0.01mm级别的偏差对尺寸要求严格的场景是正常的但如果你要求完全一致就得改用CAA的CATMeasureGeometry接口而不是包围盒。解决方式先确认你的场景是要“设计包络”还是“测量精确距离”。如果是后者把代码换成调用CATIA的测量接口它能做到跟UI一致。CAA里对应的是CATIMeasureGeometry初始化时需要传递测量对象然后返回距离值。需要注意的是这个接口的调用逻辑比Get3DBox复杂且在不同版本里对“可见性”有要求测量隐藏体可能返回0。5.3 现象想用宏改CAA结果两边打架有老工程师习惯用CATIA的VBA宏做自动化拿到CAA资源后想当然地把宏代码贴进CAA类里。宏和CAA是两个完全不同的体系宏运行在进程内的脚本引擎里能使用的对象是CATIA.Application这种自动化接口CAA是原生C COM对象是CATIPart_var这类指针。你可以在CAA里调用宏封装好的可执行文件但不能直接把宏语句塞进C函数。解决方式明确边界CAA负责性能和深层次几何操作宏负责快速UI操作或批处理。如果你要自动化测量建议保留CAA写的测量DLL再写一个VBA脚本在CATIA里调用它。怎么调用通过CATIA.ActiveDocument拿到Part然后调用一个“异步命令”触发CAA模块。注意这个命令必须被注册为Visible且在宏环境里能触发这又回到第3章的CreateCommands注册环节了。5.4 现象DLL生成后CATIA加载不了报Manifest错误Windows下C动态库附带一个manifest文件里面定义了依赖的VC运行库版本。CAA编译出来的DLL用的是特定版本VC运行库如果你的系统或CATIA安装目录下没有对应运行库加载时会报“组件清单引用未找到”。这不是CAA独有的问题但很多CAA刚入门的人容易忽略因为它常常在部署到别人机器时才暴露出来。解决方式在目标机器上安装对应版本的Visual C Redistributable。比如你是VS2013编译的就装VS2013的运行库。另外确认你部署DLL的目录和CATIA主程序的可执行文件在同一个“受信任的加载路径”下CAA默认只从..\intel_a\code\bin加载DLL如果你放在别的目录还需要修改PATH环境变量。提示先用dumpbin /dependents查看DLL依赖确认缺了哪些动态链接库再对症下药。5.5 现象换一台机器编译报一堆“找不到CATIA头文件”这几乎是CAA资源被拷贝到新电脑后必然会出现的问题。原因很简单旧电脑上CAA的Workspace里有很多mkcat生成的文件路径是写死的绝对值比如C:\Program Files\Dassault Systemes\B28\intel_a\code\headers。新电脑的CATIA版本、安装盘符不一样这些路径全废了。解决方式不要直接打开他人拷贝来的Workspace里的工程文件而是重新用自己的mkcat生成一个新的Workspace然后把资源包的src和include目录复制进来。如果你确定CATIA安装目录结构一致也可以用文本编辑器打开.CAA工程配置文件把里面所有旧路径替换成新路径。替换时注意保留intel_a这种平台目录名不能只换盘符。还有一个常见做法是使用CAACommandPrompt里自带的SetupEnv.bat脚本它会根据当前CATIA安装目录动态生成环境变量再配合mkmk编译可以规避大部分路径问题。6. 最后一招把编译和验证揉进一个批处理脚本里6.1 用build_and_deploy.bat把“编译-部署-重启”串成一条命令调试CAA时最费精力的不是写代码而是“改一行、编译、复制DLL、重启CATIA、手动点按钮验证”这套循环。每轮起码五分钟改十轮就是五十分钟。所以我在第06讲资源之后都会补一个批处理脚本把机械动作自动化。echo off setlocal set WORKSPACED:\CAA_Code\Chapter06_Workspace set FRAMEWORKYourFrameworkName set CATIA_BINC:\Program Files\Dassault Systemes\B28\intel_a\code\bin call C:\Program Files\Dassault Systemes\B28\intel_a\code\bin\mkcat.exe -ws %WORKSPACE% if errorlevel 1 goto :error call mkmk -m %FRAMEWORK% -f if errorlevel 1 goto :error copy /Y %WORKSPACE%\Generated\%FRAMEWORK%\intel_a\code\bin\*.dll %CATIA_BIN%\ if errorlevel 1 goto :error echo Compile and deploy OK endlocal exit /b 0 :error echo Failed with error code %errorlevel% endlocal exit /b 1这个脚本把mkcat、mkmk、copy三个动作串起来任何一步失败都会退出并给错误码。你可以在VS里配一个“外部工具”指向这个.bat每次改完代码按一下快捷键几秒后DLL就更新到位。注意脚本里的CATIA_BIN路径要根据实际版本改B28只是示例。6.2 用日志关键值对比改造前后的行为编译部署只是手段真正要验证的是“改造后的代码资源行为没变”。我的习惯是在公用的测量函数里加一行日志输出格式固定为MEASURE_RESULT X Y Z。改造前跑一遍记录原始值改造后再跑一遍对比差值在阈值内就视为回归通过。这样比肉眼去看CATIA界面里的数字可靠得多。日志文件放在C:\Temp\caa_log.txt用std::ofstream追加写。注意写入时加进程ID和线程ID因为CATIA可能同时开多个文档线程安全问题别忽视。批量跑多个零件时脚本可以循环调用你的批处理入口把每个零件的测量结果追加到日志里。有这个习惯之后我再也没有因为“改错一个返回码”而在CATIA界面里点一下午鼠标。这也是我想留给你的一句话CAA资源不怕旧不怕乱真正怕的是没有“可验证”的习惯。第06讲这种代码资源本质是给你一个可复现的起点而不是现成的最终答案。拿到它之后先把编译、部署、日志这三件事做成肌肉记忆再谈改代码。希望帮到你。本文还有配套的精品资源点击获取