搜档网
当前位置:搜档网 › LabVIEW如何方便地调用DLL文件

LabVIEW如何方便地调用DLL文件

LabVIEW如何方便地调用DLL文件
LabVIEW如何方便地调用DLL文件

LabVIEW如何方便地调用DLL文件

LabVIEW调用DLL文件

LabVIEW支持通过调用DLL文件的方式与其它编程语言混合使用。比如,在实际的工程项目中,用户可以用C++语言实现软件的运算部分,并把这些功能构建在DLL文件中,然后再使用LabVIEW编写程序的界面部分,并通过调用编写好的DLL来调用运算部分的功能。

LabVIEW 中是通过Call Library Function Node(CLN)节点来完成DLL文件调用的。创建一个新的VI,右击程序框图,在Functions Palette中依次选中Connectivity——Libraries & Executables工具栏即可找到该节点(如下图)。

将节点放置在程序框图中,双击会出现它的配置对话框,共有四页。第一页用于填写被调用函数的信息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栏用于设定哪个线程里运行被调用的函数。用户可以通过CLN 节点的配置面板来指定被调用函数运行所在的线程。CLN 的线程选项非常简单,只有两项:Run in UI thread和Run in any thread。LabVIEW的程序框图上直接可以看出一个CLN节点是选用的什么线程。如果Run in UI thread,节点颜色是橙色的;Run in any thread则是浅黄色的

通常情况下,除非使用的动态库是多线程安全的,CLN 中选择Run in any thread方式;否则必须选择Run in UI thread方式。判断一个动态库是不是多线程安全的,需通过以下方法:如果一个动态库的文档中没有明确说明它是多线程安全的,那么就要当作是非多线程安全的;在可以看到动态库源代码的条件下,如果代码中存在全局变量、静态变量或者代码中看不到有lock一类的操作,那么这个动态库也就肯定不是多线程安全的。

选择了Run in any thread方式,LabVIEW会在最方便的线程运行动态库函数,且一般会与调用它的VI在同一个线程运行。因为LabVIEW是自动多线程的语言,它也很可能会把动态库函数分配给一个单独的线程运行。如果程序中存在没有直接或间接先后关系的两个CLN节点,LabVIEW很可能会同时在不同的线程运行它们所调用的函数,也许是同一函数。对于非多线程安全的动态库,这是很危险的操作。很容易引起数据混乱,甚至是程序崩溃。

选择Run in UI thread方式,因为LabVIEW只有一个界面线程,所以如果所有的CLN设置都是界面线程,那么就可以保证这些CLN调用的函数肯定全部都运行在同一线程下,肯定不会被同时调用。对于非多线程安全的动态库,这种方式就保证了它的安全。

让我们回到配置对话框第一页,Calling convention用于指明被调用函数的调用约定。这里只支持两种约定:stdcall 和C call。它们之间的区别在于,stdcall由被调用者负责清理堆栈,C call由调用者清理堆栈。这个设置错误时,可能会引起LabVIEW崩溃,也就是说如果LabVIEW调用DLL函数时出现异常,首先应该考虑这个设置是否正确。(Windows API一般使用的都是stdcall;标准C的库函数大多使用C call。如果函数声明中有类似__stdcall这样的关键字,它就是stdcall的。)

第二页是函数参数的配置(图4)。

DLL和LabVIEW之间传递参数,最常用的三种数据类型是数值、数值型数组和字符串。C语言中经常把指针或者数据的地址在函数间传递,在32位操作系统中,可以使用int32数值来表示指针。因此,当需要在LabVIEW中传递指针数据时,可以使用I32或U32数值类型来表示这个地址类型的数据。但是,64位的程序中,数据的地址只能使用I64或U64来表示。这样,如果一个调用了DLL函数的VI,并且函数参数中有地址型数据,使用固定数据类型的数值来表示地址,就要准备两份代码。解决方法是使用LabVIEW中的新的数据类型Pointer-sized Integer。这个数据类型的长度在不同的平台上会自动使用32位或64位长度。如果在C语言函数参数声明中有const关键字,可以选中Constant选项。布尔类型在DLL函数和LabVIEW VI之间传递没有专有的数据类型,是利用数值类型来传递的。输入时先把布尔值转变为数值,在传递给DLL函数;输出时再把数值转为布尔值。对于数组的传递,LabVIEW只支持C 数据类型中的数值型数组,传递数组类型需要注意的的是“Array Format”要选择“Array Data Pointer”。这个设置中还有其他两个选项,带有“Handle”的参数类型都是表示LabVIEW定义的特殊类型的。在第三方的DLL中不会使用到数组参数作为输出值时,要记得为输出的数组数开辟空间。开辟数据空间的方法有两种:第一种方法,创建一个长度满足要求的数组,作为初始值传递给参数,输出数的数据就会被放置在输入数组的所在的存空间。第二种方法是直接在

参数配置面板上进行设置。在Minimum size中写入一个固定的数值,LabVIEW就会按此大小为输出的数组开辟空间。在Minimum size 中选择函数的其它数值参数,而不是固定数值。这样LabVIEW会按照当时被选择的参数值的大小来开辟空间。字符串与使用与数组是非常类似的,实际上在C语言中字符串就是一个I8数组。

在NI软件的安装路径下打开当前使用版本的LabVIEW文件夹,通过examples\dll\data passing\Call NativeCode.llb找到简单数据类型在LabVIEW与C之间的对应关系。部分常见关系见表1。

第三页用于为DLL设置一些回调函数,可以使用这些回调函数在特定的情形下完成初始化、清理资源等工作(图5)

如果为Reserve选择了一个回调函数,那么当一个新的线程开始调用这个DLL时,这个回调函数首先被调用。可以利用这个函数为新线程使用到的数据做初始化工作。线程在使用完这个DLL之后,它会去调用Unreserve中指定的回调函数。Abort中指定的函数用于VI非正常结束时被调用,也就是让一个程序在运行完前停止。这些回调函数的原型在Prototype for these procedures中列出,必须要由DLL的开发者按照特定的格式实现。如果使用的DLL不是专为LabVIEW设计的,一般不会包含这样的回调函数。

第四页是错误处理方式,用户可根据需要选择相应的错误检查级别。

另外还需要注意的是,C语言中的struct在LabVIEW中可以使用cluster来表示,但有时需要作出相应的调整。这是因为在C语言中,struct的字节对齐是可以进行设置的,这就决定了其各元素的存放地址的可变性。C语言中的对字节对齐数可通过#pragma pack指令或在工程属性中进行指定。而在LabVIEW的cluster中,所有元素只能是1字节对齐的,所以如果要和C语言中非1字节对齐的struct对应,需要做出一些调整。比如,对于C语言中2字节对齐的struct,第一个元素如果是I8型的,在LabVIEW的cluster中第一个元素对应不变,但不能紧挨着放第二个元素,必须留一个无意义的空位。C语言的struct其实也是如此,只不过没有表现出来。所以为了方便,如果自己用C 语言生成DLL文件供LabVIEW调用最好将struct都设为1字节对齐。C语言的struct中可以嵌套数组,但是这和LabVIEW中含有数组元素的cluster是不一样的,LabVIEW中需要将数组中的元素都拆开放入cluster中。

如果C语言的struct中含有一个指针,LabVIEW中的cluster只能用一个U32数值(32位系统上,64位系统上使用U64)来表示指针的地址,而不能将指针所指向的容放到Cluster中去。如果声明的是指向struct的指针,才能在LabVIEW中使用cluster与之对应。CLN节点的配置面板中,没有一个专门命名的“struct”或者“cluster”参数类型,应选择“Adapt to Type”就可以了。如果参数的类型就是结构而非指针,考虑到C函数参数的压栈顺序,把一个结构体作为参数传给函数,相当于把结构中每个元素分别作为参数传递给函数。图6为C语言中struct和LabVIEW中cluster的部分匹配图。

相关主题