跳到主要内容

调用动态链接库

· 阅读需 29 分钟

新版本请查看: https://lv.qizhen.xyz

动态链接库导入工具

    在 Windows 系统上,动态链接库就是DLL文件。调用DLL是 LabVIEW 与其它语言混合使用是最重要的一种方法。比如,在一个大的项目中,用户可以用 C++ 语言实现软件的运算部分,并把这些功能构建在DLL文件中;再使用 LabVIEW 编写程序的界面部分,并通过调用编写好的DLL来调用运算部分的功能。
    现在轻易就可以找到完成各种功能的DLL,LabVIEW 通过调用它们,也几乎无所不能。

    在 LabVIEW 中,通过 Connectivity -> Libraries & Executables -> Call Library Function Node(CLN)节点来调用DLL中的函数。
    在函数选板上,它旁边的一个节点是 Code Interface Node,这个 CIN 节点也是用来与C语言混合编程用的。这是在 CLN 节点出现以前,LabVIEW 调用C函数的方法。现在有了CLN,可以不再考虑它了。(我个人强烈建议:不要使用CIN!因为那样会遇到很多问题,但没人能帮你解决。)

    在 LabVIEW 中调用 DLL 中的函数,最大的难度就在于把函数参数的数据类型映射为相应的 LabVIEW 中的数据类型。在准备研究 CLN 中的参数如何配置之前,可以先考虑一下这个工具:Tools -> Import -> Shared Library。这个工具专门用作把 DLL 中的函数包装成 VI,升成的每个 VI 中最主要的部分就是一个 CLN 节点。它自动帮你把函数的参数都已经设置好了,有了这个工具你就不需要再费脑筋去考虑数据类型匹配的问题了。
    这个工具的第一个版本是我开发的。它虽然不算完美,但大多数情况下也够用了。如果你有现成的DLL,打算在LabVIEW 中使用,首先应该考虑的就是用这个工具,把DLL中所有函数都包装成 VI。再使用起来,就方便多了。

    某些特别复杂的情况下,Import Shared Library 这个工具可能无法处理,或者包装出来的 VI 不令人满意。这时就需要编程人员手工做一些设置或修改了。因此,高级用户了解这些数据类型的匹配也还是有必要的。我会在后续文章里对此做一详细的解释。

CLN 的配置选项

    双击一个 CLN(Call Library Function Node)节点,就会出现它的配置对话框。这个对话框有四页。

    第一页是被调用函数的信息。
    Library name or path 是DLL文件名和路径。在系统路径下的DLL,直接输入文件名即可,否则需要全路径。在这里知名的DLL是被静态加载的程序中的。当调用了这个DLL的VI被装入内存时,DLL也同时被装入内存。
    LabVIEW 也可动态加载DLL。只要把 Specify path on diagram 选上就可以了。选择了这个选项,在 Library name or path 中输入的内容就无效了。取而代之的是,CLN 节点多出一对输入输出,用于指明你需要使用的DLL的路径。这样,当VI被打开时,DLL不会被装入内存,只用程序运行到需要使用这个DLL中的函数时,才把其装入内存。
    Function name 及需要调用的函数的名称。LabVIEW 会把DLL中所有的暴露出来的函数都列出,用户只要在下拉框中选取即可。
    Thread 栏用于设定哪个线程里运行被调用的函数。它的具体含义可以参考《LabVIEW 程序中的线程 4 - 动态连接库函数的线程》。
    Calling convention 用于指明被调用函数的调用约定。这里只支持两种约定:stdcall 和 C call。它们之间的区别在于,stdcall 由被调用者负责清理堆栈;C call 由调用者清理堆栈。这个设置错误时,可能会引起 LabVIEW 崩溃,所以一定要小心。反过来说,如果 LabVIEW 调用 DLL 函数时出现异常,首先就可以考虑这个设置是否正确。
    (Windows API 一般使用的都是 stdcall;标准C的库函数大多使用 C call。如果函数声明中有类似 __stdcall 这样的关键字,它就是 stdcall 的。)

    第二页是函数参数的配置,这是最复杂的部分,留待下次详细分析。

    第三页用于为DLL设置一些回调函数,可以使用这些回调函数在特定的情形下完成初始化、清理资源等工作。
    如果为 Reserve 选择了一个回调函数,那么当一个新的线程开始调用这个DLL时,这个回调函数首先被调用。可以利用这个函数为新线程使用到的数据做初始化工作。
    如果一个线程使用了这个DLL,在线程结束时,它会去调用 Unreserve 中指定的回调函数。
    Abort 中指定的函数用在 VI 非正常结束时被调用。比如按 Abort 按钮让一个VI停止,而不是让他运行完。
    这里的几个回调函数必须要由DLL的开发者按照特定的格式实现。它的原型就是 Prototype for these procedures 中列出的那个。如果你使用的DLL不是专为 LabVIEW 设计的,一般不会包含这样的回调函数。

    第四页是错误处理方式,这上面说明写得已经够详细了,我也在补充不了什么了。不过,像我在《用户界面设计 4 - 帮助和反馈信息》里提到的,把帮助文档直接写在界面上的地方,都是极不常用的设置。所以,我们基本上可以不关心这页的设置。

简单数据类型参数的设置

复杂问题先从简单地说起,在DLL和LabVIEW之间传递参数,最常用的三种数据类型是:数值类型、字符串、数值型数组。这几种类型的参数配置起来还是比较简单的。

1. 数值类型
    LabVIEW 多种不同精度的数值类型与C语言中的数值类型的匹配是相当直观的,比如 4-byte Single 对应 C 语言中的 float。LabVIEW 自带的例子“LabVIEW 8.5\examples\dll\data passing\Call Native Code.llb”中详细的列出了简单数据类型在 LabVIEW 与 C 之间的对应关系。
    C 语言中经常把指针,或者数据的地址在函数间传递。在32位操作系统中,可以使用int32数值来表示指针。因此,当需要在LabVIEW中传递指针数据时,可以使用I32或U32数值类型来表示这个地址类型的数据。但是,64位的程序中,数据的地址只能使用I64或U64来表示。这样,如果一个调用了DLL函数的VI,并且函数参数中有地址型数据,使用固定数据类型的数值来表示地址,就要准备两份代码。解决方法就是使用 LabVIEW 中的新的数据类型 Pointer-sized Integer。这个数据类型的长度在不同的平台上会自动使用32位或64位长度。
    如果在C语言函数参数声明中有const关键字,可以选中Constant选项。

<table style="border-collapse:collapse;" border="0"><colgroup><col style="width:84px;"> <col style="width:214px;"> <col style="width:230px;"></colgroup><tbody valign="top"><tr style="height:22px;"><td style="border-right:silver .5pt solid;border-top:silver .5pt solid;border-left:silver .5pt solid;border-bottom:silver .5pt solid;" valign="center"><p style="text-align:center;"><span style="font-size:9pt;"><strong><span style="font-family:宋体;">输入</span><span style="font-family:times new roman;">/</span><span style="font-family:宋体;">输出</span></strong></span></p></td><td style="border-right:silver .5pt solid;border-top:silver .5pt solid;border-bottom:silver .5pt solid;border-left-style:none;"><p style="text-align:center;"><span style="font-size:9pt;color:black;font-family:宋体;">输入</span></p></td><td style="border-right:silver .5pt solid;border-top:silver .5pt solid;border-bottom:silver .5pt solid;border-left-style:none;"><p style="text-align:center;"><span style="font-size:9pt;color:black;font-family:宋体;">输出或兼作输入输出</span></p></td></tr><tr style="height:22px;"><td style="border-right:silver .5pt solid;border-left:silver .5pt solid;border-top-style:none;border-bottom:silver .5pt solid;" valign="center"><p style="text-align:center;"><span style="font-size:9pt;"><strong><span style="font-family:times new roman;">C</span><span style="font-family:宋体;">语言声明</span></strong></span></p></td><td style="border-right:silver .5pt solid;border-top-style:none;border-bottom:silver .5pt solid;border-left-style:none;"><p style="text-align:center;">float red;</p></td><td style="border-right:silver .5pt solid;border-top-style:none;border-bottom:silver .5pt solid;border-left-style:none;"><p style="text-align:center;">float* red;</p></td></tr><tr style="height:21px;"><td style="border-right:silver .5pt solid;border-left:silver .5pt solid;border-top-style:none;border-bottom:silver .5pt solid;" valign="center"><p style="text-align:center;"><span style="font-size:9pt;"><strong><span style="font-family:times new roman;">LabVIEW<br></span><span style="font-family:宋体;">中的配置</span></strong></span></p></td><td style="border-right:silver .5pt solid;border-top-style:none;border-bottom:silver .5pt solid;border-left-style:none;"><p style="text-align:center;"><a href="http://by1.storage.msn.com/y1pH5Es9Ox64kaizaxO3J63rPpUjiPxjo3bilYkPzc1GDaBPxyaniqyouXHckJYk0wIgUqXDvRv78ZeZbB-a3wdcO03gc0BHIRz?PARTNER=WRITER"><img src="http://byfiles.storage.msn.com/y1pIcO_924THoc_0AHueZlgd3SVIP2cyvdcajv66iWUfAbyAeemS2lst2nNmR876dfpLw_Q2KIDbuE?PARTNER=WRITER" border="0"></a></p></td><td style="border-right:silver .5pt solid;border-top-style:none;border-bottom:silver .5pt solid;border-left-style:none;"><p style="text-align:center;"><a href="http://byfiles.storage.msn.com/y1pIcO_924THofTRMTB7DoGNR09muDvJnhZ92FnjUOXCvDIXVQ94Ukdp9Oo0i3wij0ZeMdUSZG6maU?PARTNER=WRITER"><img src="http://byfiles.storage.msn.com/y1pIcO_924THocOEphCBLw5DYMkdZUuCixzPYLXJjzFxh4Iwn6rNbBffyNZ6eAsZwQXqxajrVqB1Ys?PARTNER=WRITER" border="0"></a></p></td></tr><tr style="height:28px;"><td style="border-right:silver .5pt solid;border-left:silver .5pt solid;border-top-style:none;border-bottom:silver .5pt solid;" valign="center"><p style="text-align:center;"><span style="font-size:9pt;"><strong><span style="font-family:times new roman;">LabVIEW<br></span><span style="font-family:宋体;">的使用</span></strong></span></p></td><td style="border-right:silver .5pt solid;border-top-style:none;border-bottom:silver .5pt solid;border-left-style:none;"><p style="text-align:center;"><a href="http://byfiles.storage.msn.com/y1pIcO_924THodK9p5lIvmhD3tjwe1S6c5D2JOaysS3MNerSkuljqiWOGT_vQdZLpmhEvmjlSImMDI?PARTNER=WRITER"><img src="http://by1.storage.msn.com/y1pH5Es9Ox64kYaxyqpaQ9wQuGZaNgQAQ1COsIFT3ltXxLdcUh0Oxt72rlqPQSyDlE46MEWF-PcjbNfAbyyhtMuaepB4s7fhgUw?PARTNER=WRITER" border="0"></a></p></td><td style="border-right:silver .5pt solid;border-top-style:none;border-bottom:silver .5pt solid;border-left-style:none;"><p style="text-align:center;"><a href="http://by1.storage.msn.com/y1pH5Es9Ox64kac4tTS20jKGGnR_M4lgzipRZQbM_AQLIqF3-9xO7i4d-OPdw6DoEzRjEgptRoeMCTy9Ht3y20evIws2MDIZuFm?PARTNER=WRITER"><img src="http://byfiles.storage.msn.com/y1pIcO_924THoc57D0q03XPiJ7QnZ3cNyD9PLKVxcsCxAzJ5LIwnXVU3C1jhg8HkHlPwb2Ltutw2cI?PARTNER=WRITER" border="0"></a></p></td></tr></tbody></table>

**2\. 布尔类型**
    布尔类型在 DLL 函数和 LabVIEW VI 之间传递没有专有的数据类型,是利用数值类型来传递的。输入时先把布尔值转变为数值,在传递给DLL函数;输出时再把数值转为布尔值。

<table style="border-collapse:collapse;" border="0"><colgroup><col style="width:84px;"> <col style="width:214px;"> <col style="width:230px;"></colgroup><tbody valign="top"><tr style="height:22px;"><td style="border-right:silver .5pt solid;border-top:silver .5pt solid;border-left:silver .5pt solid;border-bottom:silver .5pt solid;" valign="center"><p style="text-align:center;"><span style="font-size:9pt;"><strong><span style="font-family:宋体;">输入</span><span style="font-family:times new roman;">/</span><span style="font-family:宋体;">输出</span></strong></span></p></td><td style="border-right:silver .5pt solid;border-top:silver .5pt solid;border-bottom:silver .5pt solid;border-left-style:none;"><p style="text-align:center;"><span style="font-size:9pt;color:black;font-family:宋体;">输入</span></p></td><td style="border-right:silver .5pt solid;border-top:silver .5pt solid;border-bottom:silver .5pt solid;border-left-style:none;"><p style="text-align:center;"><span style="font-size:9pt;color:black;font-family:宋体;">输出或兼作输入输出</span></p></td></tr><tr style="height:22px;"><td style="border-right:silver .5pt solid;border-left:silver .5pt solid;border-top-style:none;border-bottom:silver .5pt solid;" valign="center"><p style="text-align:center;"><span style="font-size:9pt;"><strong><span style="font-family:times new roman;">C</span><span style="font-family:宋体;">语言声明</span></strong></span></p></td><td style="border-right:silver .5pt solid;border-top-style:none;border-bottom:silver .5pt solid;border-left-style:none;"><p style="text-align:center;"><span style="font-size:9pt;color:black;font-family:times new roman;">bool red;</span></p></td><td style="border-right:silver .5pt solid;border-top-style:none;border-bottom:silver .5pt solid;border-left-style:none;"><p style="text-align:center;"><span style="font-size:9pt;color:black;font-family:times new roman;">bool* red;</span></p></td></tr><tr style="height:21px;"><td style="border-right:silver .5pt solid;border-left:silver .5pt solid;border-top-style:none;border-bottom:silver .5pt solid;" valign="center"><p style="text-align:center;"><span style="font-size:9pt;"><strong><span style="font-family:times new roman;">LabVIEW<br></span><span style="font-family:宋体;">中的配置</span></strong></span></p></td><td style="border-right:silver .5pt solid;border-top-style:none;border-bottom:silver .5pt solid;border-left-style:none;"><p style="text-align:center;"><a href="http://byfiles.storage.msn.com/y1pIcO_924THocDmwHto586PLKhoXVJmfPT5nhLDBcGWXl2k9cFLZxpV3HLtVIS-08TIeRzYBCHklM?PARTNER=WRITER"><img src="http://byfiles.storage.msn.com/y1pIcO_924THodr6fgiy75aepHOG9D57Hg03kIWieqas_ZXWni6GjRsDrUUJ0gzU__5o-LeniKQKis?PARTNER=WRITER" border="0"></a></p></td><td style="border-right:silver .5pt solid;border-top-style:none;border-bottom:silver .5pt solid;border-left-style:none;"><p style="text-align:center;"><a href="http://byfiles.storage.msn.com/y1pIcO_924THocoJH-P5yBZo5NdHkKyd84mtzUio3RzGXAeJFBC_XlB7TzJqPXZxCZJcXoKLuDvfHY?PARTNER=WRITER"><img src="http://byfiles.storage.msn.com/y1pIcO_924THodKBfjrIokJpAcKxU5VtyAL4KThMbCQE1hE5yULerClJrA0FsJ-Twk6tVvju3rebJI?PARTNER=WRITER" border="0"></a></p></td></tr><tr style="height:28px;"><td style="border-right:silver .5pt solid;border-left:silver .5pt solid;border-top-style:none;border-bottom:silver .5pt solid;" valign="center"><p style="text-align:center;"><span style="font-size:9pt;"><strong><span style="font-family:times new roman;">LabVIEW<br></span><span style="font-family:宋体;">的使用</span></strong></span></p></td><td style="border-right:silver .5pt solid;border-top-style:none;border-bottom:silver .5pt solid;border-left-style:none;"><p style="text-align:center;"><a href="http://byfiles.storage.msn.com/y1pIcO_924THoctJ7FcjPnalQjChKC_35T4R8U0g0VEuwIaNqzpVtfgJDvqdXRKeMO25quOPohAYCU?PARTNER=WRITER"><img src="http://byfiles.storage.msn.com/y1pIcO_924THodHYaaxncDLaFCjmLsWBFJWtjVfpMUlcjmgPQMbra6jebDbDEhsU1yJrR7GT-lfG9k?PARTNER=WRITER" border="0"></a></p></td><td style="border-right:silver .5pt solid;border-top-style:none;border-bottom:silver .5pt solid;border-left-style:none;"><p style="text-align:center;"><a href="http://byfiles.storage.msn.com/y1pIcO_924THodewzzj-rysSNQPPB4fOIPc3-lH_7Kl6w80ABfDRk2n5mdTUVSXIZsuxWTwtU2xqmk?PARTNER=WRITER"><img src="http://by1.storage.msn.com/y1pH5Es9Ox64kZy4cpQ-XwNaPemrW-TVyjJV4TmuWnlJiK1cRpssVGucbk2DZFbISlvSoRbcaug68ewiNbmO-f0QLksHuqakQQB?PARTNER=WRITER" border="0"></a></p></td></tr></tbody></table>

**3\. 数值型数组**
    对于数组的传递,LabVIEW 只支持 C 数据类型中的数值型数组。传递数组类型需要注意的的是“Array Format”要选择“Array Data Pointer”。这个设置中还有其他两个选项,像这种带有“Handle”的参数类型都是表示LabVIEW定义的特殊类型的。在第三方的DLL中不会使用到。
    数组参数作为输出值时,要记得为输出的数组数据开辟空间。开辟数据空间的方法有两种:
    第一种方法,创建一个长度满足要求的数组,作为初始值传递给参数,输出数的数据就会被放置在输入数组的所在的内存空间内。
    第二种方法是直接在参数配置面板上进行设置。在 Minimum size 中写入一个固定的数值,LabVIEW 就会按此大小为输出的数组开辟空间。在 Minimum size 中选择函数的其它数值参数,而不是固定数值。这样 LabVIEW 会按照当时被选择的参数的值的大小来开辟空间。

<table style="border-collapse:collapse;" border="0"><colgroup><col style="width:84px;"> <col style="width:214px;"> <col style="width:230px;"></colgroup><tbody valign="top"><tr style="height:22px;"><td style="border-right:silver .5pt solid;border-top:silver .5pt solid;border-left:silver .5pt solid;border-bottom:silver .5pt solid;" valign="center"><p style="text-align:center;"><span style="font-size:9pt;"><strong><span style="font-family:宋体;">输入</span><span style="font-family:times new roman;">/</span><span style="font-family:宋体;">输出</span></strong></span></p></td><td style="border-right:silver .5pt solid;border-top:silver .5pt solid;border-bottom:silver .5pt solid;border-left-style:none;"><p style="text-align:center;"><span style="font-size:9pt;color:black;font-family:宋体;">输入</span></p></td><td style="border-right:silver .5pt solid;border-top:silver .5pt solid;border-bottom:silver .5pt solid;border-left-style:none;"><p style="text-align:center;"><span style="font-size:9pt;color:black;font-family:宋体;">输出或兼作输入输出</span></p></td></tr><tr style="height:22px;"><td style="border-right:silver .5pt solid;border-left:silver .5pt solid;border-top-style:none;border-bottom:silver .5pt solid;" valign="center"><p style="text-align:center;"><span style="font-size:9pt;"><strong><span style="font-family:times new roman;">C</span><span style="font-family:宋体;">语言声明</span></strong></span></p></td><td style="border-right:silver .5pt solid;border-top-style:none;border-bottom:silver .5pt solid;border-left-style:none;"><p style="text-align:center;"><span style="font-size:9pt;color:black;font-family:times new roman;">int values[];</span></p></td><td style="border-right:silver .5pt solid;border-top-style:none;border-bottom:silver .5pt solid;border-left-style:none;"><p style="text-align:center;"><span style="font-size:9pt;color:black;font-family:times new roman;">int values[];</span></p></td></tr><tr style="height:21px;"><td style="border-right:silver .5pt solid;border-left:silver .5pt solid;border-top-style:none;border-bottom:silver .5pt solid;" valign="center"><p style="text-align:center;"><span style="font-size:9pt;"><strong><span style="font-family:times new roman;">LabVIEW<br></span><span style="font-family:宋体;">中的配置</span></strong></span></p></td><td style="border-right:silver .5pt solid;border-top-style:none;border-bottom:silver .5pt solid;border-left-style:none;"><p style="text-align:center;"><a href="http://by1.storage.msn.com/y1pH5Es9Ox64ka1mTIYvvA92RIQKMqy9iRaZXJHMBW6y_eAS0Lxoo8mfp0Ae8n0v6oynweZ8W7dBMwr__zsloupFkGUQwrt76bn?PARTNER=WRITER"><img src="http://by1.storage.msn.com/y1pH5Es9Ox64kYdHsX7kvdyGMeTDBd3N7f8uV-9D37YO-UjgSQZY1ruvlKJbzs00pkQ4NmRwz475YH2-C0BD7Hlkh_sXp1Jd1b8?PARTNER=WRITER" border="0"></a></p></td><td style="border-right:silver .5pt solid;border-top-style:none;border-bottom:silver .5pt solid;border-left-style:none;"><p style="text-align:center;"><a href="http://by1.storage.msn.com/y1pH5Es9Ox64kYnLAHfjNgb2B0K31oAwMtQWnkGeo-frRryNCOu3zurtj3kPffWt9FmhF0pJTGToSDM_iaZKhOQUSU0kI54XGmQ?PARTNER=WRITER"><img src="http://byfiles.storage.msn.com/y1pIcO_924THofrCKlaxqyxWCSCiYHs-pt_fDGz6SrPyR_z8djyJa6HR8n_RSOb0zr-ysgrvmSlRrs?PARTNER=WRITER" border="0"></a></p></td></tr><tr style="height:47px;"><td style="border-right:silver .5pt solid;border-left:silver .5pt solid;border-top-style:none;border-bottom:silver .5pt solid;" valign="center"><p style="text-align:center;"><span style="font-size:9pt;"><strong><span style="font-family:times new roman;">LabVIEW<br></span><span style="font-family:宋体;">的使用</span></strong></span></p></td><td style="border-right:silver .5pt solid;border-top-style:none;border-bottom:silver .5pt solid;border-left-style:none;"><p style="text-align:center;"><a href="http://byfiles.storage.msn.com/y1pIcO_924THoe2j22_6JVPI7a_XXSeyGgkTOvKostTrZXpLh5Q0LT3W1tG07cnrIDKrjGb9WsXNmU?PARTNER=WRITER"><img src="http://byfiles.storage.msn.com/y1pIcO_924THod9rqdn4ywJD4c8YlO1_PiGWZBDboR6au9OSwm9jsCKX-kGF80OF_LaQf0nqaNPwXU?PARTNER=WRITER" border="0"></a></p></td><td style="border-right:silver .5pt solid;border-top-style:none;border-bottom:silver .5pt solid;border-left-style:none;"><p style="text-align:center;"><a href="http://byfiles.storage.msn.com/y1pIcO_924THofvb9stdBpPSFTzZT5n1WF9WIM4FhUIgMvaq9eosqAUN27GAKmpi6-n9vQoYlsaOpk?PARTNER=WRITER"><img src="http://byfiles.storage.msn.com/y1pIcO_924THoc_JjryYTx6hzk82iEbtwEjc2gSc1n73Dp4ZuebCllJPd7uxibV920h6ZVtAuL-7rQ?PARTNER=WRITER" border="0"></a></p></td></tr></tbody></table>

**4\. 字符串类型**
    字符串与使用与数组是非常类似的,实际上在C语言中字符串就是一个I8数组。

<table style="border-collapse:collapse;" border="0"><colgroup><col style="width:84px;"> <col style="width:214px;"> <col style="width:230px;"></colgroup><tbody valign="top"><tr style="height:22px;"><td style="border-right:silver .5pt solid;border-top:silver .5pt solid;border-left:silver .5pt solid;border-bottom:silver .5pt solid;" valign="center"><p style="text-align:center;"><span style="font-size:9pt;"><strong><span style="font-family:宋体;">输入</span><span style="font-family:times new roman;">/</span><span style="font-family:宋体;">输出</span></strong></span></p></td><td style="border-right:silver .5pt solid;border-top:silver .5pt solid;border-bottom:silver .5pt solid;border-left-style:none;"><p style="text-align:center;"><span style="font-size:9pt;color:black;font-family:宋体;">输入</span></p></td><td style="border-right:silver .5pt solid;border-top:silver .5pt solid;border-bottom:silver .5pt solid;border-left-style:none;"><p style="text-align:center;"><span style="font-size:9pt;color:black;font-family:宋体;">输出或兼作输入输出</span></p></td></tr><tr style="height:22px;"><td style="border-right:silver .5pt solid;border-left:silver .5pt solid;border-top-style:none;border-bottom:silver .5pt solid;" valign="center"><p style="text-align:center;"><span style="font-size:9pt;"><strong><span style="font-family:times new roman;">C</span><span style="font-family:宋体;">语言声明</span></strong></span></p></td><td style="border-right:silver .5pt solid;border-top-style:none;border-bottom:silver .5pt solid;border-left-style:none;"><p style="text-align:center;"><span style="font-size:9pt;color:black;font-family:times new roman;">char* name;</span></p></td><td style="border-right:silver .5pt solid;border-top-style:none;border-bottom:silver .5pt solid;border-left-style:none;"><p style="text-align:center;"><span style="font-size:9pt;color:black;font-family:times new roman;">char* name;</span></p></td></tr><tr style="height:21px;"><td style="border-right:silver .5pt solid;border-left:silver .5pt solid;border-top-style:none;border-bottom:silver .5pt solid;" valign="center"><p style="text-align:center;"><span style="font-size:9pt;"><strong><span style="font-family:times new roman;">LabVIEW<br></span><span style="font-family:宋体;">中的配置</span></strong></span></p></td><td style="border-right:silver .5pt solid;border-top-style:none;border-bottom:silver .5pt solid;border-left-style:none;"><p style="text-align:center;"><a href="http://byfiles.storage.msn.com/y1pIcO_924THofKeI81gen3zGuj5ipT1di9pDS2sAJElnNO3juZFjW9nOhkD6pmMQSisKdnPCvHl3w?PARTNER=WRITER"><img src="http://byfiles.storage.msn.com/y1pIcO_924THod7K5B2xzXCOoajjjRL3oyStl-In55KSCfad5iczQUo-NrRoQT5B1oLs5FplJhCvmw?PARTNER=WRITER" border="0"></a></p></td><td style="border-right:silver .5pt solid;border-top-style:none;border-bottom:silver .5pt solid;border-left-style:none;"><p style="text-align:center;"><a href="http://byfiles.storage.msn.com/y1pIcO_924THoeQevI4z4UxUILmPRNFVvwFM7xYGvvSejt_JZGDHpTt1th7YZq-InBVcXepzzCA6XI?PARTNER=WRITER"><img src="http://byfiles.storage.msn.com/y1pIcO_924THoc4I_MHXOvEuaa-d9SROc2xUAwUIMHbgpfTGxuKNk7fhMxOBaTvGfI7bv4tkmNrP-U?PARTNER=WRITER" border="0"></a></p></td></tr><tr style="height:28px;"><td style="border-right:silver .5pt solid;border-left:silver .5pt solid;border-top-style:none;border-bottom:silver .5pt solid;" valign="center"><p style="text-align:center;"><span style="font-size:9pt;"><strong><span style="font-family:times new roman;">LabVIEW<br></span><span style="font-family:宋体;">的使用</span></strong></span></p></td><td style="border-right:silver .5pt solid;border-top-style:none;border-bottom:silver .5pt solid;border-left-style:none;"><p style="text-align:center;"><a href="http://byfiles.storage.msn.com/y1pIcO_924THodpa4TfplmBnVXbfqpUKFj3_JPXBbgVmSZLUCiN5mC7_OXwdYa-fC0bMFtm_0TUZGY?PARTNER=WRITER"><img src="http://byfiles.storage.msn.com/y1pIcO_924THoeP_dfNcKseB_kSsDF8sm-yEPOtFHULP3tnGsEioZMrmvC5wbWqmFyhcxT6NKGjAwE?PARTNER=WRITER" border="0"></a></p></td><td style="border-right:silver .5pt solid;border-top-style:none;border-bottom:silver .5pt solid;border-left-style:none;"><p style="text-align:center;"><a href="http://byfiles.storage.msn.com/y1pIcO_924THoelz19hDWGYZzsLSJqNZC4B4gFavAGOxb827uGH31ly9MgaWJkD14MHy5r2lk_Z-s8?PARTNER=WRITER"><img src="http://byfiles.storage.msn.com/y1pIcO_924THocM6QuhNPmbN0uVKlNBBpaXYD72gBTohiIA0-_fIP-peyoEpsXkXLWcmTs9_9V6myg?PARTNER=WRITER" border="0"></a></p></td></tr></tbody></table>


结构型参数的设置

https://lv.qizhen.xyz/external_call_dll

    C 语言中的结构(struct),在一些简单情况下,可以和 LabVIEW 中的 Cluster 相对应。但是,对于比较复杂的情况,LabVIEW 中的 Cluster 要做适当调整,才能够对应起来。

    在讨论结构型参数的映射前,一定要先了解一下[字节对齐](http://www.google.cn/search?hl=zh-CN&q=%E5%AD%97%E8%8A%82%E5%AF%B9%E9%BD%90&btnG=Google+%E6%90%9C%E7%B4%A2&meta=)的概念。我在这里只做一个简单说明,详细内容可以查找相关的专题文章。
    C 语言中的一个结构 typedef struct { char a; int b} MyStct;  结构中的元素a所在的地址是:0xAAAA0000,b 的存放地址是与结构的字节对齐设置相关的。如果采用1字节对齐,b是紧挨着a存放的,b的地址就是:0xAAAA0001;如果采用2字节对齐,b的存放地址是紧挨着a的第一个偶数地址,也就是:0xAAAA0002;如果采用4字节对齐,b的存放地址是紧挨着a的第一个4整数倍地址,也就是:0xAAAA0004……
    C 语言的字节对齐数可以由 #pragma pack 指令指定,也可以在工程属性里指定。但是 LabVIEW 的 Cluster 只能是1字节对齐的。因此,C 语言中,非1字节对齐的结构与Cluster对应时,一定要做适当调整。比如,结构 typedef struct { char a; int b} MyStct;  是2字节对齐的,那么,对应的LabVIEW Cluster 第一个元素还应该是 I8 型的a,但是,不能紧接着就放b,因为C语言中,b的起始地址不是紧挨着a的,他们中间还有一个无意义的数据,C的结构体虽然表现不出来,LabVIEW中却需要把它考虑进去。
    如果是自己编写一个DLL给LabVIEW使用,为了方便,可以把C代码中所有的结构都设为1字节对齐。

    C 语言的结构中如果还嵌套了数组,是不能直接对应于LabVIEW中嵌套了数组的Cluster的。在LabVIEW中,只能把数组的元素都拆开来放在Cluster中。

    下面是一些对应的实例:

<table style="border-collapse:collapse;" border="0" width="545"><colgroup><col style="width:84px;"> <col style="width:214px;"> <col style="width:230px;"></colgroup><tbody valign="top"><tr style="height:22px;"><td style="border:silver 0.5pt solid;" valign="center" width="342"><strong>C</strong></td><td style="border-right:silver 0.5pt solid;border-top:silver 0.5pt solid;border-bottom:silver 0.5pt solid;border-left-style:none;" width="195"><strong>LabVIEW</strong></td></tr><tr style="height:22px;"><td style="border-right:silver 0.5pt solid;border-left:silver 0.5pt solid;border-top-style:none;border-bottom:silver 0.5pt solid;" valign="center" width="342"><p align="left">#pragma pack (1)<br>typedef struct { char a; int b} MyStct;<br>MyStct* testStruct;</p></td><td style="border-right:silver 0.5pt solid;border-top-style:none;border-bottom:silver 0.5pt solid;border-left-style:none;" width="195"><a href="http://byfiles.storage.msn.com/y1pIcO_924THof8g9SPZtAYBQ19vizW929tnBCHRFqqilKJ2JWxsnLfn1F58XTATFGIucBEQtaBaZE?PARTNER=WRITER"><img src="http://byfiles.storage.msn.com/y1pIcO_924THoeZYKyGo9U3GBYg1AQzcqE0Qlav9GpWleBMliPhGkpueyOzlrC-hL9mu1eMQJ8j6No?PARTNER=WRITER" border="0"></a></td></tr><tr style="height:21px;"><td style="border-right:silver 0.5pt solid;border-left:silver 0.5pt solid;border-top-style:none;border-bottom:silver 0.5pt solid;" valign="center" width="342"><p align="left">#pragma pack (2)<br>typedef struct { char a; int b} MyStct;<br>MyStct* testStruct;</p></td><td style="border-right:silver 0.5pt solid;border-top-style:none;border-bottom:silver 0.5pt solid;border-left-style:none;" width="195"><a href="http://byfiles.storage.msn.com/y1pIcO_924THoehf1UlUcN_pCJ26RDxShJnnFB005piWSnZKwd8QWx46CMMXWo2Ql-UzYrxfL5sO44?PARTNER=WRITER"><img src="http://byfiles.storage.msn.com/y1pIcO_924THodbSuud3ZScEin6Gr48B-Mdye9352ZJfQRbgDfXz_L4HSn70RqZbEpWtmVq4kZGei0?PARTNER=WRITER" border="0"></a></td></tr><tr style="height:28px;"><td style="border-right:silver 0.5pt solid;border-left:silver 0.5pt solid;border-top-style:none;border-bottom:silver 0.5pt solid;" valign="center" width="342"><p align="left">#pragma pack (4)<br>typedef struct { char a; int b} MyStct;<br>MyStct* testStruct;</p></td><td style="border-right:silver 0.5pt solid;border-top-style:none;border-bottom:silver 0.5pt solid;border-left-style:none;" width="195"><a href="http://byfiles.storage.msn.com/y1pIcO_924THof78CPrt6CfjQoDF5wYlHTZjz00g8H14fkkfw-gYGBRfBXCgN-FnGZsMnX7aUCb5_E?PARTNER=WRITER"><img src="http://byfiles.storage.msn.com/y1pIcO_924THoeyinvvkoBtEjhyrrN1zLklOEC7gKOtIDvCxPer41FHzjPHe_wYZguLpZ8JiT4Tbkw?PARTNER=WRITER" border="0"></a></td></tr><tr style="height:28px;"><td style="border-right:silver 0.5pt solid;border-left:silver 0.5pt solid;border-top-style:none;border-bottom:silver 0.5pt solid;" valign="center" width="342"><p align="left">#pragma pack (1)<br>typedef struct { char a; char* str; int b} MyStct;<br>MyStct* testStruct;</p></td><td style="border-right:silver 0.5pt solid;border-top-style:none;border-bottom:silver 0.5pt solid;border-left-style:none;" width="195"><a href="http://byfiles.storage.msn.com/y1pIcO_924THoc1gVpsLyUVR28eZBSo99BnjW6XhtGM665M3ZAy-1t7P1oPZhO3jmyFTCO3Zg_OVKk?PARTNER=WRITER"><img src="http://byfiles.storage.msn.com/y1pIcO_924THoeQsG1b5-UiugbbP7Jk8uAjrey8c-zkh-fvv2aVzpm3LnxcsTDweQQ0hurCI5AnxFc?PARTNER=WRITER" border="0"></a></td></tr><tr style="height:28px;"><td style="border-right:silver 0.5pt solid;border-left:silver 0.5pt solid;border-top-style:none;border-bottom:silver 0.5pt solid;" valign="center" width="342"><p align="left">#pragma pack (1)<br>typedef struct { char a; char str[5]; int b} MyStct;<br>MyStct* testStruct;</p></td><td style="border-right:silver 0.5pt solid;border-top-style:none;border-bottom:silver 0.5pt solid;border-left-style:none;" width="195"><a href="http://byfiles.storage.msn.com/y1pIcO_924THoeP7oOBJYfdeO9dqun8p2XR5e1bdI4XgXKlJyjv3lgQLXSuNrT1CQ1qeCnFl7pe88Y?PARTNER=WRITER"><img src="http://byfiles.storage.msn.com/y1pIcO_924THoc_f3MkJezqU4C-it1fYrl49UhTY5kZ-09rrHyGlaHo4mUUVt5g4n_T3xYkzeyGWKI?PARTNER=WRITER" border="0"></a></td></tr></tbody></table>

    上面这个表中有两点需要注意的:
    一是,表格中的第四个例子,结构中含有一个指针,LabVIEW中的Cluster只能用一个U32数值(32位系统上,64位系统上使用U64)来表示指针的地址。不能把指针指向的内容放到Cluser中去。后面的章节再讨论当我们在LabVIEW中得到了一个数据的地址后,如何从这个地址中把数据拿出来。
    第二,上面C语言中声明的 testStruct 变量,是指向结构的指针。就是说C函数的变量类型为结构的指针时,才能在LabVIEW中使用Cluster与之对应。CLN节点的配置面板中,没有一个专门的参数类型叫做“struct”或者“Cluster”,选择“Adapt to Type”就可以了。
    如果参数的类型就是结构而非指针,考虑到C函数参数的压栈顺序,把一个结构体作为参数传给函数,等价于把结构中每个元素分别作为参数传递给函数。下面是一个例子:

<table style="border-collapse:collapse;" border="0"><colgroup><col style="width:84px;"> <col style="width:214px;"> <col style="width:230px;"></colgroup><tbody valign="top"><tr style="height:22px;"><td style="border:silver 0.5pt solid;" valign="center"><p style="text-align:center;"><span style="font-size:9pt;"><strong><span style="font-family:宋体;">输入</span><span style="font-family:times new roman;">/</span><span style="font-family:宋体;">输出</span></strong></span></p></td><td style="border-right:silver 0.5pt solid;border-top:silver 0.5pt solid;border-bottom:silver 0.5pt solid;border-left-style:none;"><p style="text-align:center;"><span style="font-size:9pt;color:black;font-family:宋体;">输入</span></p></td><td style="border-right:silver 0.5pt solid;border-top:silver 0.5pt solid;border-bottom:silver 0.5pt solid;border-left-style:none;"><p style="text-align:center;"><span style="font-size:9pt;color:black;font-family:宋体;">输出或兼作输入输出</span></p></td></tr><tr style="height:22px;"><td style="border-right:silver 0.5pt solid;border-left:silver 0.5pt solid;border-top-style:none;border-bottom:silver 0.5pt solid;" valign="center"><p style="text-align:center;"><span style="font-size:9pt;"><strong><span style="font-family:times new roman;">C</span><span style="font-family:宋体;">语言声明</span></strong></span></p></td><td style="border-right:silver 0.5pt solid;border-top-style:none;border-bottom:silver 0.5pt solid;border-left-style:none;"><p align="left">typedef struct{int left; int top;} Position;<br>long TestStructure(Position inPos);</p></td><td style="border-right:silver 0.5pt solid;border-top-style:none;border-bottom:silver 0.5pt solid;border-left-style:none;"><p align="left">typedef struct{int left; int top;} Position;<br>long TestStructure(Position *pos);</p></td></tr><tr style="height:21px;"><td style="border-right:silver 0.5pt solid;border-left:silver 0.5pt solid;border-top-style:none;border-bottom:silver 0.5pt solid;" valign="center"><p style="text-align:center;"><span style="font-size:9pt;"><strong><span style="font-family:times new roman;">LabVIEW<br></span><span style="font-family:宋体;">中的配置</span></strong></span></p></td><td style="border-right:silver 0.5pt solid;border-top-style:none;border-bottom:silver 0.5pt solid;border-left-style:none;"><p style="text-align:center;"><a href="http://byfiles.storage.msn.com/y1pIcO_924THocmuZCdAW9fRKtv1HSM6HPqfq-5ily7xquBIl_RZ4mJjF68H2mLoroRb4WlW1XlMWU?PARTNER=WRITER"><img src="http://byfiles.storage.msn.com/y1pIcO_924THocI_T9MvCXXnPZPHPL-cbCBv25GNUcF6sVNKCNueaNyDOSKgs-5VUlOELfimxBN84c?PARTNER=WRITER" border="0"></a>&nbsp;</p></td><td style="border-right:silver 0.5pt solid;border-top-style:none;border-bottom:silver 0.5pt solid;border-left-style:none;"><p style="text-align:center;"><a href="http://byfiles.storage.msn.com/y1pIcO_924THodStphb5N9L8wnVxAS_5L4ZQCwG6iqYrBuBF6X8AMYpBSBADcwnpnMXU7y2e-63DeU?PARTNER=WRITER"><img src="http://byfiles.storage.msn.com/y1pIcO_924THofIhXfVX26fccZpl7exiu8XO7c9M_Gf8rswhSsSvUmQxu3xQLd47W8raz-ymRQ_BMY?PARTNER=WRITER" border="0"></a>&nbsp;</p></td></tr><tr style="height:28px;"><td style="border-right:silver 0.5pt solid;border-left:silver 0.5pt solid;border-top-style:none;border-bottom:silver 0.5pt solid;" valign="center"><p style="text-align:center;"><span style="font-size:9pt;"><strong><span style="font-family:times new roman;">LabVIEW<br></span><span style="font-family:宋体;">的使用</span></strong></span></p></td><td style="border-right:silver 0.5pt solid;border-top-style:none;border-bottom:silver 0.5pt solid;border-left-style:none;"><p style="text-align:center;">&nbsp;<a href="http://byfiles.storage.msn.com/y1pIcO_924THofXv99ibU0UDpJIV0M7UiYBZZMODz0WJ7SkP9sau2pBDs-pzhqVChGRRcqKjqtq19Q?PARTNER=WRITER"><img src="http://byfiles.storage.msn.com/y1pIcO_924THodyuxsZUUAbjNFiY0YHEoBraUwVgQWtsbIsfqVCdV-aYp0z7jwo45GYKF7ca9cO5aA?PARTNER=WRITER" border="0"></a></p></td><td style="border-right:silver 0.5pt solid;border-top-style:none;border-bottom:silver 0.5pt solid;border-left-style:none;"><p style="text-align:center;">&nbsp;<a href="http://byfiles.storage.msn.com/y1pIcO_924THofKd5tH5QzUlmIFf0CAegYIvoy83Dp0qzKcevnIPYbgrtccNiNbl7ZKChOxPEzOl2Q?PARTNER=WRITER"><img src="http://byfiles.storage.msn.com/y1pIcO_924THodRS1kRTgPdfK1csPYaq2XCZCfaa3pj7LWe04EgXYI_CX9igejQi3hKu7id0R8NcnY?PARTNER=WRITER" border="0"></a></p></td></tr></tbody></table>

LabVIEW 中对 C 语言指针的处理

https://lv.qizhen.xyz/#docs/external_call_dll

C 语言函数常有指针类型的参数,有时候,在 LabVIEW 中只能得到一个指向某个数据的指针。比如,在第4节里的一个例子:

 #pragma pack (1) typedef struct { char a; char\* str; int b} MyStct; MyStct\* testStruct; long TestStructure(MyStct\* tempStct);

在 LabVIEW CLN 节点中,就只能返回以整数类型表示的 str 的指针。 在大多数情况下,并不需要在 LabVIEW 中得到指针指向内存的具体数据,对这些数据的操作是在DLL的函数中完成的。我们只需在LabVIEW中得到这个指针的地址,再把它传递到下一个 CLN 节点就可以了。

但在某些情况下,我们仍然需要在 LabVIEW 中得到指针指向的内容,这只能借助 C 语言来完成。在上面的例子,我们需要另外写一个 C 函数,把函数 TestStructure 返回的 tempStct 结构中的元素拆开成简单数据类型,作为新的函数的参数(新函数中的一个参数就是 char* str,LabVIEW可以识别它)。在LabVIEW 中调用这个新的函数,可以得到这些简单数据类型的数据。 有些函数需要在外部开辟的一块内存中写入数据,LabVIEW 中没有分配内存的操作,也需要再写一个 C 的函数分配好内存,给被调用的函数使用。 这种做法的缺点是针对每个需要得到内容的指针都要做个包装函数,相当麻烦。

一个减少C代码的方法是:编写一个C函数,负责把指针指向的内存中的数据以数组的形式读出,再在 LabVIEW 中把它们从新组织成合理的数据类型。这种方法其实更复杂,好在 LabVIEW 8.5 中自带的一些 VI 已经做了这个工作。如果你需要,不需要再额外编写代码,直接用 LabVIEW 提供的 VI 就可以了。 [LabVIEW]\vi.lib\Utility\importsl\GetValueByPointer\GetValueByPointer.xnode 就是用来得到指针内容的一个VI。告诉它指针地址、数据类型,它就会返回正确的 LabVIEW 数据。参见下图中的示例:

    DLLMemory.dll:ReturnPointerToConstant 返回的是一个指针,指向我在C语言中声明的一个整数常量。把这个指针传给 GetValueByPointer.xnode 并且告诉它数据类型是I32,GetValueByPointer.xnode 就会得到这个指针指向的内容。 \[LabVIEW\]\\vi.lib\\Utility\\importsl\\ 中还有几个 VI 可以在调用DLL时起到帮助作用。比如,对于函数需要使用外部开辟的内存的,就可以使用 DSNewPtr.vi 开辟一块内存,然后把地址传递给这个函数。 需要注意的是,这几个 VI 不是 NI 承诺给用户使用的,所以没有什么文档,需要用户自己研究它们的用法。

作为函数返回值的字符串为什么不用在 VI 中先分配内存

    Call Library Node 是 LabVIEW 中调用 DLL 函数的节点。如果被调用的函数有一参数数据类型为 char*,用来输出字符串。我们需要在 CLN 中这个参数对应的左侧接线端连进一个字符串,并且输入字符串的长度要保证大于输出字符串的长度。这个输入字符串的内容是没有用的,它只被用作是被开辟的内存,保存输出字符串。否则,会出现数组越界的运行错误,LabVIEW会莫名其妙死掉。

    更糟糕的是,LabVIEW 不会在刚好出现数组越界错误时死掉,而是在之后的某一部确定时候死掉。如果你意识不到自己的程序中有这种错误,或者你有几百个类似的 CLN,那你调试起来可能会类似的。

    有人问我,如果函数不是用参数输出字符串而是返回字符串,CLN 返回参数是没有左接线端的。这可咋开辟内存捏?

    我打开 LabVIEW 一试,可不是嘛。函数返回字符串的地方根本没法输入任何信息。自己编了一个DLL试了试,发现 CLN 是可以正确输出函数返回的字符串的,不需要特别指定字符串的大小。

    今天早上起得太早,于是就有点发晕,心想,如果既然 LabVIEW 不需要为函数返回的字符串开辟内存,干嘛非要难为我们为参数输出的字符串开辟内存。否则可以避免多少潜在的错误啊。DLL 函数参数输出字符串是个比较常见的导致程序崩溃的陷阱。

    琢磨了半天,脑袋才清醒过来。所谓返回或输出字符串是口头上的语言。换成计算机的语言来解释就清楚了:)

    函数返回字符串的情况,实际上是函数返回了一个指向字符串指针。既然是函数返回的,LabVIEW就可以得到该指针,进而就可以得到它所指的字符串。在LabVIEW内部,调用以下 strlen() 得到字符串的长度,开辟一个相应大小的buffer,再调用以下 strcpy() 就把这个字符串考到 LabVIEW 控件的数据区了。

    而参数输出字符串的情况并不是真的输出,而是函数要求输入一个指针。LabVIEW 必须为DLL函数提供这样一个指针。而LabVIEW自己又不能自动开辟一片缓存就把指针传给函数,因为这时候我们想要的字符串还不存在呢,LabVIEW没办法知道应该开辟多大的缓存。只好把指定缓存大小的任务交给编程人员了:(

    LabVIEW 编程如果不是考虑调用C编出来的函数,根本不需要内存分配回收的问题。有了内存分配就是烦啊。

 《我和 LabVIEW》目录