ARTICLE DETAIL

资讯详情

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

Teamcenter ITK开发环境搭建:从DLL编译到动作注册全流程

Teamcenter ITK开发环境搭建:从DLL编译到动作注册全流程 简介面向Teamcenter二次开发的工程师这份以PDF格式呈现的文档重点讲解如何使用Visual Studio搭建ITK开发环境内容适合刚接触Teamcenter集成工具包的技术人员。压缩包内仅包含一个PDF文件大小约966KB加载快捷、便于查阅。目前已有1188人学习下载口碑实用。文档详细叙述了项目创建的完整流程包括选择Win32类型、配置编译器的包含目录与预处理器定义、设置链接器输出文件名和附加依赖项等并针对常见错误给出了解决思路。在演示环节文档通过编写一个输出hello world的处理函数说明了如何建立公共头文件、实现动作回调以及注册初始化模块并展示了运用Teamcenter接口完成动作注册的过程。此外还给出了常见编译警告及避坑提示有助于减少环境配置中的摸索时间可帮助读者顺利迈出二次开发的第一步。1. Teamcenter ITK开发环境搭建从零把第一个DLL跑起来第一次接到Teamcenter ITK二次开发任务时我花了大半天卡在环境搭建上头文件路径配了又报错库文件链接时一堆警告DLL好不容易编译出来了却不知道怎么让Teamcenter加载。后来把这套流程理顺发现ITK开发环境搭建的核心就三件事——让编译器找得到头文件、让链接器找得到库文件、让Teamcenter认你的DLL。这篇文章不绕弯子直接按一个可复现的hello_world示例讲完整流程适合刚接手Teamcenter实施、需要在业务系统里做集成的工程师。看完你能独立搭好环境并且知道每个配置项为什么这么设、哪些坑可以提前躲开。2. VS项目属性配置包含目录、预处理定义和链接器设置一次讲透2.1 创建空白项目为什么选Win32而不是其他模板打开Visual Studio先新建项目。按原文档的流程在新建项目里选Win32项目输入项目名比如firstITKProject然后一路下一步。这里有个常见翻车点很多人会顺手选中预编译头或者空项目之外的选项导致项目创建后右键打开属性页看不到C/C和链接器这两个配置节点。原因是VS根据项目类型和文件类型决定了属性页显示的配置类别——如果没有添加任何源文件、或者项目模板选的不是Win32控制台属性页就不会出现C/C这一项。我的做法是创建项目时直接选Win32控制台应用程序然后在向导的应用程序设置里勾选空项目这样属性页最干净也不会带入一堆向导生成的骨架代码。空项目创建好之后第一件事是配置解决方案平台。默认创建出来是Win32平台但Teamcenter 13的ITK库是x64的。如果平台不匹配后面链接时会出现LNK2019无法解析的外部符号这类经典报错。在工具栏上把平台切到x64或者到配置管理器里新建一个x64平台。这一条建议在项目一开始就做不然后面库目录配好了也会栽在位数不匹配上。属性页的位置也要说清楚。右键项目不是解决方案选择属性弹出的对话框左上角有配置下拉框务必要把配置切到Release x64再改下面的每一项。很多人默认停在Debug改完路径切到Release一看配置全没了又怀疑人生。这个配置项是分平台、分Debug/Release独立保存的。2.2 C/C设置包含目录与IPLIB预处理定义进入属性页后先看C/C - 常规 - 附加包含目录。这里要填两个路径用分号分隔D:\Siemens\Teamcenter13\include;D:\Siemens\Teamcenter13\include_cpp。注意第一个include目录是Teamcenter ITK的C接口头文件第二个include_cpp是C接口的。两个都要填缺一个就可能出现在代码里#include tccore/item.h能找到、#include dft/dft_toolkit.h却找不到的诡异情况。接着看C/C - 预处理器 - 预处理器定义。原始文档给的值是IPLIBnone。这里的作用是让ITK的头文件体系跳过对IPLIB相关符号的引用避免和业务代码里的第三方库或标准库发生宏冲突。实际项目里如果你不打算在ITK里调用Teamcenter的成像/打印相关服务这个值保持IPLIBnone就不会有编译问题。还有一个易漏的细节VS默认会帮你带上WIN32、_WINDOWS、_DEBUG等宏这些不用删保留即可只在那一行末尾加上IPLIBnone。下面这个表可以直接抄作业配置的时候对着逐行核对配置位置属性项值C/C - 常规附加包含目录D:\Siemens\Teamcenter13\include;D:\Siemens\Teamcenter13\include_cppC/C - 预处理器预处理器定义IPLIBnone链接器 - 常规输出文件D:\Siemens\Teamcenter13\bin$(TargetName)$(TargetExt)链接器 - 常规附加库目录D:\Siemens\Teamcenter13\lib链接器 - 输入附加依赖项D:\Siemens\Teamcenter13\lib*.lib2.3 链接器设置输出路径、库目录与通配符依赖链接器这部分是新手最容易敷衍、老鸟也容易忽略配置顺序的地方。先说链接器 - 常规 - 输出文件。原始文档里的值是D:\Siemens\Teamcenter13\bin\$(TargetName)$(TargetExt)意思是编译出来的DLL直接输出到Teamcenter的bin目录。这样做的好处很直接ITK的DLL最终必须放进Teamcenter安装目录的bin下才能被加载与其编译完再手动拷贝不如让链接器自己把DLL写到bin里。但这里同时埋下了一个坑——MSB8012警告后面避坑章节专门讲。然后是链接器 - 常规 - 附加库目录填D:\Siemens\Teamcenter13\lib。Teamcenter的lib目录下放着一大堆以lib结尾的静态库和导入库文件ITK程序链接时需要把这一堆库全部绑定进去。最后是链接器 - 输入 - 附加依赖项。原始文档给的值是D:\Siemens\Teamcenter13\lib\*.lib。这个写法和常规的逐个添加xxx.lib不一样它是用通配符把lib目录下所有.lib全量加进来。好处是快、省事坏处是你不知道具体链了哪些库后期如果遇到符号冲突排查起来麻烦。我个人的习惯是初学阶段按通配符来先把环境跑通等到了正式项目里再用#pragma comment(lib, tccore.lib)或者逐项添加的方式把真正用到的库显式列出来。注意附加依赖项里如果同时写了路径和通配符格式要写成完整路径形式不要只写*.lib否则VS找不到这些库文件。到这里项目的编译环境就配置完了。下面可以开始写代码。3. 编写第一个动作Handlerhello_world的完整代码拆解3.1 common.h头文件的组织与包含顺序ITK项目的常规做法是搞一个公共头文件把Teamcenter的核心头文件全部集中进来业务代码里统一#include。原始文档里的common.h内容比较全我按原始内容整理并加注释#include ict\ict_userservice.h #include tccore\custom.h #include epm\epm_toolkit_tc_utils.h #include tc/preferences.h #include property/prop_msg.h #include vector #include set #include tccore\aom_prop.h #include tccore\aom.h #include tccore/project_msg.h #include tccore/item_msg.h using namespace std; #ifdef __cplusplus extern C { #endif extern DLLAPI int firstITKProject_register_callbacks(); extern DLLAPI int firstITKProject_main_register_init_module(int *decision, va_list args); extern DLLAPI int hello_world(EPM_action_message_t msg); #ifdef __cplusplus } #endif这里有几个地方需要说明。头文件的包含顺序Teamcenter的ITK头文件对包含顺序比较敏感ict_userservice.h这类基础服务头文件要放在前面后面再跟tccore、epm、bom这些功能模块的头文件。如果你发现某个头文件编译时报未定义的类型先检查一下是不是它依赖的头文件还没被包含。extern C这一段是必须的。ITK的DLL对外暴露的接口遵循C调用约定而我们的代码是用C写的如果不包一层extern CC编译器会对函数名做name mangling生成出的导出符号和Teamcenter期望的就不一致了注册时会直接失败。DLLAPI是Teamcenter头文件里定义的导出宏如果没有这个宏函数默认不会被导出到DLL的导出表里。Teamcenter加载你的DLL时找firstITKProject_register_callbacks这个导出函数找不到就会在启动时报错。3.2 hello_world.cpp动作处理函数的实现原始文档里的hello_world.cpp非常简短核心就是一个EPM action handler#include common.h int hello_world(EPM_action_message_t msg) { int ifail ITK_ok; printf(输出hello world\n); return ifail; }这个函数是动作处理器action handler当Teamcenter里的某个EPM动作被触发时ITK会调用它。msg参数携带了动作上下文比如当前操作的对象、触发动作的用户、动作类型等这是后面做真实业务逻辑的入口。ITK_ok是ITK定义的成功返回值所有ITK API函数返回0表示成功非0值对应不同的错误码。handler函数必须显式返回ITK_ok否则Teamcenter会认为这次动作处理失败可能会回滚操作。当前这个实现没有使用msg参数里的内容只打印了一行文本。不过在实际运行时printf的输出不会直接显示在Teamcenter的界面上需要看日志或者用写文件的方式才能确认它执行了。从hello_world这个函数的结构能看出ITK action handler的通用形态一个返回int、接收EPM_action_message_t参数的函数函数体里写业务逻辑最终返回状态码。后面进阶章节会在这个基础上读取对象属性让它真正有用。3.3 注册回调文件三处名称必须一致这是整个ITK项目里最容易被忽略、也最容易出问题的一个文件。原始文档给出的firstITKProject_register_callbacks.cpp代码如下#include ai\sample_err.h #include ict\ict_userservice.h #include ae\dataset_msg.h #include tccore\grm_msg.h #include tccore\tc_msg.h #include common.h #include bom/bom_msg.h #include tccore/wso_msg.h extern DLLAPI int firstITKProject_register_callbacks() { int stat ITK_ok; CUSTOM_register_exit(firstITKProject, USER_gs_shell_init_module, (CUSTOM_EXIT_ftn_t)firstITKProject_main_register_init_module); printf(\n ******************************************************\n); printf(\n firstITKProject loaded! %s %s \n, __DATE__, __TIME__); return stat; } extern DLLAPI int firstITKProject_main_register_init_module(int *decision, va_list args) { *decision ALL_CUSTOMIZATIONS; int status ITK_ok; METHOD_id_t method1; /* 注册hello_world动作handler */ ITKCALL(EPM_register_action_handler(hello_world, , (EPM_action_handler_t)hello_world)); printf(\nEntering hello_world register\n); return status; }代码逻辑拆开来看firstITKProject_register_callbacks是整个DLL的入口回调Teamcenter在加载这个DLL时会调用它。函数内部通过CUSTOM_register_exit注册了一个退出回调第一个参数是模块名第三个参数是模块初始化函数指针。firstITKProject_main_register_init_module是这个初始化函数它先把*decision置为ALL_CUSTOMIZATIONS表示这个模块完全接管自身的定制逻辑然后调用EPM_register_action_handler把hello_world函数注册为动作处理器。EPM_register_action_handler的第一个参数hello_world就是动作名第三个参数是处理函数指针中间的空字符串表示不限定具体类或对象类型。这里要特别注意原文提到的一个硬性要求三处名称保持一致。具体说是项目名、回调函数名firstITKProject_register_callbacks、以及CUSTOM_register_exit里的模块名firstITKProject必须对应。比如你的项目叫my_itk_test那么回调函数和模块名都应该叫my_itk_test开头。Teamcenter加载DLL时按项目名称去找这个导出函数对不上就直接跳过加载而且不会给你任何明确的错误提示只会在系统日志里留一行不起眼的无法加载自定义库。我在这里翻过车一个字母写错整整排查了半天。4. 编译输出DLL与Teamcenter端注册让插件被系统加载4.1 Release x64编译DLL该生成到哪里代码写完之后直接按Release x64生成解决方案。编译输出里能看到DLL就落在D:\Siemens\Teamcenter13\bin\firstITKProject.dll。原始的编译日志里有一堆警告但最终是成功的1------ 已启动全部重新生成: 项目: firstITKProject, 配置: Release x64 ------ 1 firstITKProject_register_callbacks.cpp 1D:\Siemens\Teamcenter13\include\pom/pom/pom.h(801): warning C4819: ... 1D:\Siemens\Teamcenter13\include\pom/pom/pom.h(5415): warning C4819: ... 1firstITKProject_register_callbacks.cpp(23): warning C4101: method1: 未引用的局部变量 1 hello_world.cpp 1D:\Siemens\Teamcenter13\include\pom/pom/pom.h(801): warning C4819: ... 1D:\Siemens\Teamcenter13\include\pom/pom/pom.h(5415): warning C4819: ... 1C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V140\Microsoft.CppBuild.targets (1189,5): warning MSB8012: ... 1 正在创建库 D:\ITKProjects\firstITKProject\x64\Release\firstITKProject.lib 1 firstITKProject.vcxproj - D:\Siemens\Teamcenter13\bin\firstITKProject.dll 全部重新生成: 成功 1 个失败 0 个跳过 0 个 这里有个细节值得注意编译成功时同时生成了.lib和.exp文件但这两个文件在D:\ITKProjects\firstITKProject\x64\Release\下而DLL直接输出到了Teamcenter的bin目录。这也解释了后面会遇到的MSB8012警告——链接器的输出路径和VS默认的TargetPath不一致。很多人在这一步会困惑DLL到底被复制到bin目录了还是链接器直接把DLL写到bin目录了答案是后者。链接器的输出文件属性指定的完整路径就是DLL的最终位置VS不会去拷贝它只是按这个路径写文件。所以如果你改了输出文件路径但又把$(OutDir)保持默认就会出现编译日志里TargetPath和OutputFile各指一个地方的情况。MSB8012警告一般不影响DLL的生成但如果你同时依赖VS的部署功能或者后续的custom build step务必把这两个路径对齐。4.2 登录Teamcenter时的DLL名称检查编译好DLL之后还有一个关键动作在Teamcenter的首选项里注册DLL。打开Teamcenter客户端用管理员账号登录在编辑-选项或直接搜索首选项名称找到TC_customization_libraries。这个首选项的值是DLL文件名的列表多个DLL之间用逗号或分号分隔。注意填的是不带.dll后缀的文件名也就是firstITKProject不是firstITKProject.dll。这个首选项控制的是Teamcenter启动时自动加载哪些自定义库。如果漏掉这一步你编译得再成功DLL也永远不会被加载所有的回调注册都不会生效。这是整个ITK开发环境搭建里最反直觉的地方——明明是编译层面的工作最后却要在业务系统里配一个选项。4.3 验证动作是否注册成功重新登录Teamcenter看系统日志或者用日志工具观察。如果DLL被正常加载firstITKProject_register_callbacks.cpp里的两行printf会输出出来包括这行firstITKProject loaded! 某日期 某时间再往下看应该还能看到Entering hello_world register。这说明EPM_register_action_handler已经执行成功。这时在Teamcenter里配置一个业务流程在某个节点上挂上hello_world这个动作触发它就能看到handler里的输出hello world打出来。这一步是整个搭建流程的验证节点环境配好、DLL编译好、注册成功、动作触发成功四段全部打通才说明环境是真的搭好了。5. ITK开发环境搭建避坑指南五个典型翻车现场5.1 C4819代码页警告源文件编码问题现象编译时满屏warning C4819: 该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止数据丢失。原因VS2015V140工具集默认把源文件按当前系统代码页读取简体中文系统代码页是936GBK。Teamcenter的头文件里有一部分字符通常是注释和版权声明不在GBK的映射范围内编译器读出来就报C4819。这个警告本质上来自第三方头文件不是你写的代码有问题。解决如果是别人提供的头文件不要随便改动它的编码。我的做法是不管这个警告让它报着只要不影响编译和运行。如果你自己写的源文件里带有中文字符串且出现C4819就把这个源文件另存为UnicodeUTF-8带签名格式这样中文不会变成乱码也不会触发警告。注意一个连带问题如果某个文件里有中文字符串你又忽略了C4819生成的DLL在运行时打印出来的中文可能是一堆乱码——因为编译器读到的是错误解码后的字节序列。这个今天不处理迟早要在日志里还的。5.2 MSB8012 TargetPath不匹配输出位置错乱现象编译日志里出现warning MSB8012: TargetPath(...x64\Release\firstITKProject.dll) does not match the Linkers OutputFile property value(...\Teamcenter13\bin\firstITKProject.dll)。原因链接器的输出文件直接指定到了Teamcenter的bin目录但VS的项目常规页里的输出目录还是默认的$(SolutionDir)$(Configuration)\。MSBuild在编译前会对这两个路径做一致性校验不一致就警告。有时这个警告还会连带导致后续的部署/复制步骤把错误的DLL当作产物。解决最简单的办法是把项目的输出目录也改成D:\Siemens\Teamcenter13\bin\让$(OutDir)和链接器输出文件对齐。如果不想改输出目录就把链接器输出文件改成$(OutDir)$(TargetName)$(TargetExt)然后依赖VS的生成后事件把DLL复制到bin目录copy /Y $(OutDir)$(TargetName)$(TargetExt) D:\Siemens\Teamcenter13\bin\我习惯用前一种——直接统一路径源码项目里少一个生成后事件看起来清爽。5.3 method1未引用变量C4101告警的成因现象编译输出warning C4101: method1: 未引用的局部变量。原因firstITKProject_main_register_init_module里声明了METHOD_id_t method1但从头到尾没有使用它。这通常是照着模板写代码时留下的残留可能是某个版本示例里要用method1去注册method后来改成用EPM_register_action_handler后忘记删了。解决直接删掉METHOD_id_t method1;这一行。不删也不影响DLL生成但对代码洁癖来说每次编译都带一条警告不难受吗。更关键的是删掉它之后如果以后还需要注册method比如新建一个自定义的ITK方法再把这行加回来就好。这个警告本身暴露了一个问题模板代码粘贴后没有完整的走查这也是ITK开发里最危险的习惯——从网上复制代码不逐行理解就编译。5.4 IPLIBnone的真正用途避免宏重定义冲突现象设置IPLIBnone之前编译时会出现一堆关于IPLIB宏重定义的警告甚至有些情况下直接报错错误信息指向某个第三方头文件。原因Teamcenter的ITK头文件里IPLIB相关的代码会根据这个宏的定义去引用一组和知识产权保护/权限管理相关的符号。如果其他第三方库也定义了同名的宏或者类型就会冲突。IPLIBnone是告诉预处理器当前编译场景不启用IPLIB的服务,这样ITK头文件里关联的代码段会被跳过冲突就不会发生。解决在预处理器定义里加上IPLIBnone同时检查项目的高级属性里忽略特定默认库是不是干净。不要试图去改Teamcenter的头文件来规避冲突头文件升级一次你的修改就全没了维护成本极高。5.5 编译成功但Teamcenter完全不加载DLL现象DLL在bin目录里首选项TC_customization_libraries也加了firstITKProject重新登录Teamcenter后日志里找不到firstITKProject loaded!业务动作也没有任何反应。原因我排查过这个问题的几种典型情况。第一种是x64/x86不匹配——VS里配置管理器切到了x64但实际编译的还是Win32产物或者反过来第二种是首选项里填了firstITKProject.dll带了扩展名Teamcenter按不带后缀的文件名去bin目录找对不上第三种是DLL有依赖库缺失ITK的DLL往往还依赖Teamcenter bin目录下的其他DLL如果你手动拷贝DLL到别的机器上测试最容易遇到这个。解决按顺序检查确认bin目录下DLL的位数和Teamcenter客户端一致确认首选项配置值不带.dll后缀用Dependency Walker或dumpbin /dependents查看DLL的依赖项把缺失的DLL补齐。另外还可以打开Teamcenter的部署日志搜customization关键字看有没有加载失败的明确记录。这条排查思路对后来遇到的每个ITK模块都适用我每次部署新环境都强制走一遍这个顺序。6. 进阶技巧在handler里读取对象属性把hello_world变成真的业务逻辑6.1 从动作消息里拿对象Tag并读取属性hello_world的演示价值是打通整条链路但真正的ITK开发里handler一定要能拿到触发动作的业务对象。EPM_action_message_t里携带了object_tag字段它指向当前动作关联的对象。拿到对象tag之后可以用AOM_ask_value_string读取它的属性。下面这个例子读取对象的名称并打印出来#include common.h #include tccore/aom.h #include tccore/aom_prop.h int hello_world(EPM_action_message_t msg) { int ifail ITK_ok; if (msg.object_tag ! NULL_TAG) { char *value NULL; if (AOM_ask_value_string(msg.object_tag, object_name, value) ITK_ok) { printf(触发动作的对象名称: %s\n, value); MEM_free(value); /* 释放ITK分配的内存 */ } } else { printf(hello_world: object_tag为空, 动作没有关联业务对象\n); } return ifail; }这个函数说明了两件事ITK API返回的字符串内存需要调用方用MEM_free释放否则内存泄漏会随着系统运行时间增长而积少成多在handler里做任何操作之前一定要先检查传入的tag是否为NULL_TAG。实际工作中同一个handler可能被多个不同类型的动作复用有些动作并不携带业务对象不检查空指针直接调用AOM函数结果是崩溃。6.2 写在最后的两个习惯我做了几年ITK开发最大的教训是每次新建项目都强制走一遍固定检查先确认x64平台再核对include路径里有没有include_cpp再看预处理器定义有没有IPLIBnone最后检查链接器的附加依赖项和输出路径。四五个配置点检查完再开始写代码。依赖这些配置项的系统很多Teamcenter升级时配置流程基本不变但路径和库名会变直接复制旧工程配置去适配新版本一定会翻车。另外建议把一个成熟的hello_world工程整个保存下来作为基础模板新项目直接从这份模板复制只改项目名和回调函数名。这样能避开大部分环境配置的重复劳动也避免了从零配置时漏项。希望帮到你。本文还有配套的精品资源点击获取
返回列表