? 相关文章
-
?CANoe DLL编程(一)—— Visual Studio 创建DLL以及动态调用
-
?CANoe DLL编程(三)——DLL和回调函数
-
?CANoe DLL编程(四)——SendKey DLL的CANoe应用
-
?CANoe DLL编程(五)——通过VS 生成 SendKey.dll
-
?CANoe DLL编程(六)—— DLL 的二次封装
-
?本章节内容演示源码下载,点击跳转?
?前言
-
上一节我们已经掌握了怎么用VS创建一个DLL以及在VS中调用使用。
-
本节我们通过
Vector
的官方Demo
来学习下CANoe中DLL的规则和语法.
-
软件环境:
win10 x64
visual studio 2019
CANoe 11 x64
文章目录
- ? 相关文章
- ?前言
- ? Vector 官方DLL Demo结构理解
- ? Vector 官方DLL Demo源码解读
-
- ? 代码初步理解
- ? C++函数到CAPL可识别函数的映射表 `CAPL_DLL_INFO4`
- ? C++数据类型和CAPL数据类型的映射规则
- ?总结
? Vector 官方DLL Demo结构理解
1?? Vector Demo
的路径如下C:\Users\Public\Documents\Vector\CANoe\Sample Configurations 11.0.55\Programming
,因为我们在学习时有可能会对源码造成破坏,所以我们先copy一份
2?? 打开CANoe工程之后,什么都不做,先Run下,看下打印信息,明白这个工程干什么的,大致意思就是DLL里面定义了很多函数,然后按不同按键,调用不同函数。
3?? 打开capldll.can
看下源码,在includes
中它有个编译预处理,判断canoe
是 x64 位还是 32位,然后选择不同的DLL
这就很离谱的是:我的window系统是64位的 ,CANoe软件是64的,但是根据打印结果,可以看出脚本打印的却是32位的,不知什么原因,所以我们用VS生成DLL,也应该选择32位平台
4?? 我想尝试下我自己生成的DLL,能不能正常使用;因为我用的是VS2019
,我就打开VS 2017 Project
,不添加任何代码,然后将输出平台改成X86平台的
5?? 直接点击build
有可能报错如下图,如果报了这个错误,那就按下图设置下。
然后再点击build
生成 Debug\capldll.dll
,将这个dll copy到 canoe工程 CAPLdll - Copy\EXAMPLE\EXEC32
路径下替换掉原来的capldll.dll,然后关闭重启CANoe,RUN下如果不报错就说明我们自己生成的DLL也是没问题。
? Vector 官方DLL Demo源码解读
? 代码初步理解
1?? 我们先了解下 on preStart
和 on start
中的代码,除了一些打印,就有两个重要函数
registerCAPLDLL
这个函数的作用是初始化CAPL DLL;并返回一个引用dword了类型的句柄,- 它还说了,这是考虑到你有可能再其它的node脚本中也引用了相同的DLL,为了不干扰,就分配了不同的句柄
- 而且这个句柄值还将作为回调函数的参数(下章将细说回调函数)
dllInit(gHandle)
初始化
2?? 下图是源码中最为关键,为什么VS创建的DLL,CANoe不能直接用,因为CANoe 有自己的一套规则
- C++中定义的函数要经过
CAPL_DLL_INFO4
配置成CANoe可以识别的类型,这个映射关系这就是CANoe创建DLL的核心点 CAPL_DLL_INFO4
是一个结构体类型,有九个参数CAPL_DLL_INFO4 table[]
结构体数组,table中配置了多少函数,那么再CANoe中引用这个DLL时就可以使用多少函数;也许你在C++脚本中写了100个函数,但是你在table中就定义了10个函数,那么你在CANoe中就只能使用这10个
3?? 下图这九个参数的具体含义解释
typedef struct CAPL_DLL_INFO4{
char cdlName[MAX_CDL_NAME2]; // 在CAPL中调用时显示的函数名CAPL_FARCALL adr; // 函数地址,即vs工程中函数定义时的函数名const char* categoryName; // 函数在CAPL中的所属目录,可以用于对函数的分类const char* hintText; // 对该函数功能的描述介绍(在CAPL中显示)char resultType; // 函数的返回值类型,用CAPL中的类型的首字母大写去表示int parCount; // 函数的参数个数char parTypes[MAXCAPLFUNCPARS_8_1]; // 函数的参数类型,用CAPL中的类型的首字母大写去表示unsigned char array[MAXCAPLFUNCPARS_8_1]; // 函数的参数是否是数组:\001表示是1维数组;\002表示2维数组。默认则为\000,可省略,表示常量const char* parNames[MAXCAPLFUNCPARS_8_1]; // 函数显示的参数名(在CAPL中的声明介绍时)
} CAPL_DLL_INFO4;
? C++函数到CAPL可识别函数的映射表 CAPL_DLL_INFO4
4?? 我们在table中新增一行,让它仍然指向appAdd
这个函数,但是我们改了三个参数,待会重新build一下,去CANoe工程中看下有什么区别
- 参数1: // 在CAPL中调用时显示的函数名
- 参数2: // 函数地址,即vs工程中函数定义时的函数名
- 参数3: // 函数在CAPL中的所属目录,可以用于对函数的分类
- 参数4: // 对该函数功能的描述介绍(在CAPL中显示)
{
"dllAdd", (CAPL_FARCALL)appAdd, "CAPL_DLL","This function will add two values. The return value is the result",'L', 2, "LL", "", {
"x","y"}},{
"dll_test_Add", (CAPL_FARCALL)appAdd, "TEST_DLL","just for test",'L', 2, "LL", "", {
"x","y"}},
5?? 我们把刚才重新build的dll从新copy到 CANoe工程 CAPLdll - Copy\EXAMPLE\EXEC32
路径下替换掉原来的capldll.dll,然后关闭重启CANoe
和CAPL Browser
- 在
layout
菜单栏中调出CAPL Function
,可以看到我们刚才添加的一行代码,和之前有什么不同 - 函数的分类多了一个新建的
TEST_DLL
- 多了一个函数
dll_test_Add
,可以在capl脚本中尝试下,和dllAdd
功能是一样的 - 函数的解释说明
- 请仔细的和上一步骤我们的设置惊醒对比,加强理解
? C++数据类型和CAPL数据类型的映射规则
6?? 涉及到C++数据类型和CAPL数据类型的差异性, CANoe通过参数5-9
定义了一套数据类型的映射规则
- 参数5: //函数的返回值类型,用CAPL中的类型的首字母大写去表示
- 参数6: // 函数的参数个数
- 参数7: // 函数的参数类型,用CAPL中的类型的首字母大写去表示
- 参数8: // 函数的参数是否是数组:\001表示是1维数组;\002表示2维数组。默认则为\000,可省略,表示常量
- 参数9: // 函数显示的参数名(在CAPL中的声明介绍时)
- 下图是CAPL的数据类型和C++的数据类型对比,然后再
table
中用大写首字母表示 - 函数参数最多支持64个参数,没人能用那么多吧。。。
7?? 下面的代码很重要,很重要,很重要,基本包括了参数的所有类型。
/***************************参数是常量的情况***********************************/
long CAPLEXPORT far CAPLPASCAL appAdd(long x, long y)
{
long z = x + y;return z;
} 'L', 2, "LL", "", {
"x","y"} //参数都是常量,可以省略\000/***************************参数是一维数组的情况***********************************/void CAPLEXPORT far CAPLPASCAL appPutDataTwoPars( unsigned long numberBytes,const unsigned char dataBlock[] )
{
unsigned int i;for (i = 0; i < numberBytes; i++) {
dlldata[i] = dataBlock[i];}
}'V', 2, "DB", "\000\001", {
"noOfBytes","datablock"}/***************************参数是二维数组的情况***********************************/void CAPLEXPORT far CAPLPASCAL appPutDataTwoPars_2( unsigned long numberBytes,const unsigned char dataBlock[],const unsigned char dataBlock_2[][] )
{
unsigned int i;for (i = 0; i < numberBytes; i++) {
dlldata[i] = dataBlock[i];}
}'V', 3, "DBB", "\000\001\002", {
"noOfBytes","datablock",dataBlock_2}/***************************参数是指针的情况***********************************/void CAPLEXPORT far CAPLPASCAL appSum(long i, long j, long* s)
{
*s = i + j;
}'V', 3, {
'L', 'L', 'L' - 128}, "", {
"i", "j", "s"}
8?? 因为官方demo中没有传递指针的情况,下面我们添加一个实例函数
//添加一个 求和函数
void CAPLEXPORT far CAPLPASCAL appSum(long i, long j, long* s)
{
*s = i + j;
}//table中定义
{
"dll_test_Sum", (CAPL_FARCALL)appSum, "TEST_DLL","Sum via reference parameter",'V', 3, {
'L', 'L', 'L' - 128}, "", {
"i", "j", "s"} },
//重新build之后,再capl中添加代码,可以打印sum的值。
on key '2'{
long sum ;writeLineEx(1,1,"");writeLineEx(1,1,"<2> Sum via reference parameter");dll_test_Sum(10,20,sum);writeLineEx(1,1,"Call CAPL DLL Function dll_test_Sum() Result = %d ",sum);
}
End |
?总结
? 有需要演示中所用demo工程的,可以关注下方公众号网盘自取啦,感谢阅读。
- ?要有最朴素的生活,最遥远的梦想,即使明天天寒地冻,路遥马亡!
- ? 有手机的小伙伴可以加下交流群,在车载诊断领域的一个小小圈子,群里有
网盘资料
,源码
,可能有你需要的呢,平时可以交流技术,聊聊工作机会啥的。
- ?如果这篇博客对你有帮助,请 “点赞” “评论”“收藏”一键三连 哦!码字不易,大家的支持就是我坚持下去的动力。