Labview调用外部程序DLL文件的CLN.docx 您所在的位置:网站首页 labview程序框图介绍 Labview调用外部程序DLL文件的CLN.docx

Labview调用外部程序DLL文件的CLN.docx

2023-04-15 16:58| 来源: 网络整理| 查看: 265

Labview调用外部程序DLL文件的CLN.docx

《Labview调用外部程序DLL文件的CLN.docx》由会员分享,可在线阅读,更多相关《Labview调用外部程序DLL文件的CLN.docx(20页珍藏版)》请在冰豆网上搜索。

Labview调用外部程序DLL文件的CLN.docx

Labview调用外部程序DLL文件的CLN

NILabVIEW调用外部程序DLL文件的CLN

产品简介:

LabVIEW中是通过CallLibraryFunctionNode(CLN)节点来完成DLL文件调用的。

创建一个新的VI,右击程序框图,在FunctionsPalette中依次选中Connectivity——Libraries&Executables工具栏即可找到该节点。

详细介绍:

  

 

  LabVIEW调用DLL文件

  LabVIEW支持通过调用DLL文件的方式与其它编程语言混合使用。

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

  LabVIEW中是通过CallLibraryFunctionNode(CLN)节点来完成DLL文件调用的。

创建一个新的VI,右击程序框图,在FunctionsPalette中依次选中Connectivity——Libraries&Executables工具栏即可找到该节点(图1)。

                 图1CallLibraryFunctionNode

  将节点放置在程序框图中,双击会出现它的配置对话框,共有四页。

第一页用于填写被调用函数的信息(图2)。

Librarynameorpath需给出DLL文件名和路径,操作系统路径下的DLL文件,直接输入文件名也可调用,否则必须输入全路径。

在这里已经给出名字的DLL是被静态加载到程序中的,也就是说当调用了这个DLL的VI被装入内存时,DLL同时被装入内存。

LabVIEW也可动态加载DLL,只要勾选上Specifypathondiagram的选项即可。

选择了这个选项,在Librarynameorpath中输入的内容就无效了,取而代之的是CLN节点多出一对输入输出,用于指明所需要使用的DLL的路径。

这样,当VI被打开时,DLL不会被装入内存,只用程序运行到需要使用这个DLL中的函数时,才把其装入内存。

Functionname是需要调用的函数的名称,LabVIEW会把DLL中所有的暴露出来的函数都列出,用户只要在下拉框中选取即可。

Thread栏用于设定哪个线程里运行被调用的函数。

用户可以通过CLN节点的配置面板来指定被调用函数运行所在的线程。

CLN的线程选项非常简单,只有两项:

RuninUIthread和Runinanythread。

LabVIEW的程序框图上直接可以看出一个CLN节点是选用

                  图2填写被调用函数信息

的什么线程。

如果RuninUIthread,节点颜色是橙色的;Runinanythread则是浅黄色的(图3)。

                 图3CLN不同线程对比

  通常情况下,除非使用的动态链接库是多线程安全的,CLN中选择Runinanythread方式;否则必须选择RuninUIthread方式。

判断一个动态链接库是不是多线程安全的,需通过以下方法:

如果一个动态链接库的文档中没有明确说明它是多线程安全的,那么就要当作是非多线程安全的;在可以看到动态链接库源代码的条件下,如果代码中存在全局变量、静态变量或者代码中看不到有lock一类的操作,那么这个动态链接库也就肯定不是多线程安全的。

  选择了Runinanythread方式,LabVIEW会在最方便的线程内运行动态链接库函数,且一般会与调用它的VI在同一个线程内运行。

因为LabVIEW是自动多线程的语言,它也很可能会把动态链接库函数分配给一个单独的线程运行。

如果程序中存在没有直接或间接先后关系的两个CLN节点,LabVIEW很可能会同时在不同的线程内运行它们所调用的函数,也许是同一函数。

对于非多线程安全的动态链接库,这是很危险的操作。

很容易引起数据混乱,甚至是程序崩溃。

  选择RuninUIthread方式,因为LabVIEW只有一个界面线程,所以如果所有的CLN设置都是界面线程,那么就可以保证这些CLN调用的函数肯定全部都运行在同一线程下,肯定不会被同时调用。

对于非多线程安全的动态链接库,这种方式就保证了它的安全。

  让我们回到配置对话框第一页,Callingconvention用于指明被调用函数的调用约定。

这里只支持两种约定:

stdcall和Ccall。

它们之间的区别在于,stdcall由被调用者负责清理堆栈,Ccall由调用者清理堆栈。

这个设置错误时,可能会引起LabVIEW崩溃,也就是说如果LabVIEW调用DLL函数时出现异常,首先应该考虑这个设置是否正确。

(WindowsAPI一般使用的都是stdcall;标准C的库函数大多使用Ccall。

如果函数声明中有类似__stdcall这样的关键字,它就是stdcall的。

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

                  图4配置函数的参数

  DLL和LabVIEW之间传递参数,最常用的三种数据类型是数值、数值型数组和字符串。

C语言中经常把指针或者数据的地址在函数间传递,在32位操作系统中,可以使用int32数值来表示指针。

因此,当需要在LabVIEW中传递指针数据时,可以使用I32或U32数值类型来表示这个地址类型的数据。

但是,64位的程序中,数据的地址只能使用I64或U64来表示。

这样,如果一个调用了DLL函数的VI,并且函数参数中有地址型数据,使用固定数据类型的数值来表示地址,就要准备两份代码。

解决方法是使用LabVIEW中的新的数据类型Pointer-sizedInteger。

这个数据类型的长度在不同的平台上会自动使用32位或64位长度。

如果在C语言函数参数声明中有const关键字,可以选中Constant选项。

布尔类型在DLL函数和LabVIEWVI之间传递没有专有的数据类型,是利用数值类型来传递的。

输入时先把布尔值转变为数值,在传递给DLL函数;输出时再把数值转为布尔值。

对于数组的传递,LabVIEW只支持C数据类型中的数值型数组,传递数组类型需要注意的的是“ArrayFormat”要选择“ArrayDataPointer”。

这个设置中还有其他两个选项,带有“Handle”的参数类型都是表示LabVIEW定义的特殊类型的。

在第三方的DLL中不会使用到数组参数作为输出值时,要记得为输出的数组数开辟空间。

开辟数据空间的方法有两种:

第一种方法,创建一个长度满足要求的数组,作为初始值传递给参数,输出数的数据就会被放置在输入数组的所在的内存空间内。

第二种方法是直接在参数配置面板上进行设置。

在Minimumsize中写入一个固定的数值,LabVIEW就会按此大小为输出的数组开辟空间。

在Minimumsize中选择函数的其它数值参数,而不是固定数值。

这样LabVIEW会按照当时被选择的参数值的大小来开辟空间。

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

  在NI软件的安装路径下打开当前使用版本的LabVIEW文件夹,通过examples\dll\datapassing\CallNativeCode.llb找到简单数据类型在LabVIEW与C之间的对应关系。

部分常见关系见表1。

 

输入/输出

输入

输出

C语言声明

doublea

double*a

LabVIEW中的配置

 

LabVIEW的使用

C语言声明

floata

float*a

LabVIEW中的配置

 

 

LabVIEW的使用

C语言声明

inta

Int*a

LabVIEW中的配置

 

LabVIEW的使用

C语言声明

longinta

longint*a

LabVIEW中的配置

LabVIEW的使用

C语言声明

Shortinta

Shortint*a

LabVIEW中的配置

LabVIEW的使用

C语言声明

unsignedinta

unsignedint*a

LabVIEW中的配置

LabVIEW的使用

C语言声明

unsignedlonginta

unsignedlongint*a

LabVIEW中的配置

LabVIEW的使用

C语言声明

unsignedshortinta

unsignedshortint*a

LabVIEW中的配置

LabVIEW的使用

C语言声明

chata[]

chat*a

LabVIEW中的配置

LabVIEW的使用

C语言声明

unsign chara[]

unsign char*a

LabVIEW中的配置

LabVIEW的使用

 

C语言声明

boola

bool*a

LabVIEW中的配置

LabVIEW的使用

 

 

C语言声明

Inta[]

Inta[]

LabVIEW中的配置

 

 

LabVIEW的使用

                   表1数据类型对比

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

                      图5设置回调函数

  如果为Reserve选择了一个回调函数,那么当一个新的线程开始调用这个DLL时,这个回调函数首先被调用。

可以利用这个函数为新线程使用到的数据做初始化工作。

线程在使用完这个DLL之后,它会去调用Unreserve中指定的回调函数。

Abort中指定的函数用于VI非正常结束时被调用,也就是让一个程序在运行完前停止。

这些回调函数的原型在Prototypefortheseprocedures中列出,必须要由DLL的开发者按照特定的格式实现。

如果使用的DLL不是专为LabVIEW设计的,一般不会包含这样的回调函数。

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

  另外还需要注意的是,C语言中的struct在LabVIEW中可以使用cluster来表示,但有时需要作出相应的调整。

这是因为在C语言中,struct的字节对齐是可以进行设置的,这就决定了其各元素的存放地址的可变性。

C语言中的对字节对齐数可通过#pragmapack指令或在工程属性中进行指定。

而在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”参数类型,应选择“AdapttoType”就可以了。

如果参数的类型就是结构而非指针,考虑到C函数参数的压栈顺序,把一个结构体作为参数传给函数,相当于把结构中每个元素分别作为参数传递给函数。

图6为C语言中struct和LabVIEW中cluster的部分匹配图。

                    图6struct和cluster匹配

  LabVIEW打包DLL文件

  我们接下来学习如何使用LabVIEW来打包一个DLL文件。

  首先我们编写一个名为Scale.vi的程序,功能很简单就是对输入的数据乘上10,然后再输出(图7)。

                       图7scale.vi

  必须在任务管理器中才能生成.dll文件。

所以我们首先建立一个project,过程如下:

  点击File>>NewProject:

                      图8生成新项目

  接着弹出是否将该VI添加到新项目的对话框:

                 图9是否添加VI到新建项目

  选择Add,生成新的项目管理器,将其保存在需要的路径下:

                       图10项目管理器

  右键单击项目浏览器窗口中的BuildSpecifications,在快捷菜单中选择New>>SharedLibrary(DLL),弹出对DLL文件进行设置的对话框。

点击Category>>Information,根据自己需求修改Buildspecificationname和Targetfilename:

                    图11Information页面

  点击SourceFiles>>ProjectFiles>>Scale.vi>>,弹出对话框,直接用默认值,点击OK:

                    图12DefineVIPrototype

  点击Destination>>Scale.dll,点击 ,可选择需要保存的路径。

然后再点击SupportDirectory,这是指明了DLL支持文件的路径(比如数据文件之类的放在哪个文件夹),选择默认即可:

                  图13Destination页面

  Category中的SourceFiles可供用户对打包VI的属性和密码做一些设置;Advanced和AdditionalExclusions可以做一些高级的设置,这些均按默认值即可。

VersionInformation可让用户填写版本号、名称、版权、公司等信息:

                           图14VersionInformation

  点击Run-TimeLanguages,可对支持语言进行选择,默认即可。

点击Preview>>GeneratePreview,可以预览到结果:

                  图15预览生成

  点击Build,弹出生成状态对话框:

                   图16生成状态框

  点击Done,生成完成,打开DLL文件保存的路径查看:

                   图17DLL文件保存路径

  LabVIEW调用DLL文件

  LabVIEW可以方便地调用DLL文件,这些DLL文件可以是其他编译工具,如VC,生成的。

  LabVIEW可以直接通过CLN节点来调用DLL文件,以前面生成的Scale.dll文件为例。

现有一个内部定时连续采集程序,通过调用该DLL文件,使读取的值为实际采集值的10倍(图18)。

                     图18连续采集程序

  方法一

  在程序框图放入CallLibraryFunctionNode,双击弹出对话框。

在Function页面的Librarynameorpath中给入生成的Scale.dll文件的路径,Functionname选择Scale,其他选项默认。

                     图19Function页面

  由于是LabVIEW生成的DLL文件,在Parameters页面不需要做改动,但是由于VI还有一路输出,所以还需要添加一个参数y,作为DLL文件的输出。

  如果是C语言等非LabVIEW生成的DLL函数,需要将retuentype的type选项和Datatype选项改成函数定义的参数类型,对于函数里输入的参数也都需要自行添加。

Callbacks和ErrorChecking则不需要改动。

                       图20Parameters页面

  点击“OK”,将生成的CLN的输入段连接到DAQmxRead.vi,returntype输出连接到波形图表上,即可实现采集值放大10倍的功能。

                      图21完成后的程序

  方法二

  LabVIEW中还有一种方法可以调用DLL文件,在VI的选项栏,依次选择Tools——Import——SharedLibrary(.dll),弹出ImportSharedLibrary对话框。

                  图22生成ImportSharedLibrary对话框

  选择CreateVIsforasharedlibrary,点击Next,在SharedLibrary(.dll)Files中输入Scale.dll文件的路径,Head(.h)File里填写头文件的路径。

                    图23选择DLL文件路径和头文件路径

  点击Next,如果DLL文件中依赖其他的一些DLL文件,需要在IncludePaths中填写这些文件的路径。

其他选项可以根据客户需求设置,一般默认即可。

这样一直点击Next到最后,选择Openthegeneratedlibrary,点击Finish。

这样可以生成一个.lvlib格式的库文件,里面包含了Scale.vi,这是将调用该DLL文件的方法封装好的VI,只留下输入和输出接口,方便运用到LabVIEW的程序中。

直接将Scale.vi拖放到刚才的连续采集中即可完成方法一的功能。

                     图24完成的程序

  VC调用LabVIEW生成的DLL文件

  刚才介绍了LabVIEW调用DLL文件的方法,使用VC调用LabVIEW生成的DLL文件也很简单。

还是以之前生成Scale的DLL文件为例,不同的是采集电压程序使用的是C语言的例程,但和LavVIEW实现的功能相同。

  首先将先前生成ScaleDLL文件时,路径下所有的文件全部复制粘帖到C语言例程的文件夹下。

打开连续采集程序,点击状态栏的Project——Settings,在ProjectSettings对话框中加载入Scale.lib的静态链接库。

  在程序中键入#include"Scale.h",以便引入该DLL函数。

下面是C程序的代码,功能是有限点采集电压,通过Scale.dll文件实现采样值放大10倍的功能。

加粗部分是因为调用DLL文件所做的改动。

#include

#include"NIDAQmx.h"

#include"Scale.h"

#defineDAQmxErrChk(functionCall)if(DAQmxFailed(error=(functionCall)))gotoError;else

intmain(void)

{

 int32      error=0;

 TaskHandle taskHandle=0;

 int32      read;

 float64    data[1000];

char       errBuff[2048]={'\0'};

   inti=0;

 double x10=0;

 /*********************************************/

 //DAQmxConfigureCode

 /*********************************************/

 DAQmxErrChk(DAQmxCreateTask("",&taskHandle));

 DAQmxErrChk(DAQmxCreateAIVoltageChan(taskHandle,"Dev1/ai0"/*Configcorrectdevice*/,"",DAQmx_Val_Cfg_Default,-10.0,10.0,DAQmx_Val_Volts,NULL));//

 DAQmxErrChk(DAQmxCfgSampClkTiming(taskHandle,"",10000.0,DAQmx_Val_Rising,DAQmx_Val_FiniteSamps,1000));

 /*********************************************/

 //DAQmxStartCode

 /*********************************************/

 DAQmxErrChk(DAQmxStartTask(taskHandle));

 /*********************************************/

 //DAQmxReadCode

 /*********************************************/

 DAQmxErrChk(DAQmxReadAnalogF64(taskHandle,1000,10.0,DAQmx_Val_GroupByChannel,data,1000,&read,NULL));

 printf("Acquired%dpoints\n",read);

   

 for(i=0;i

       Scale(data[i],&x10);

    printf("the%dValueis:

%f\n",i,x10);

 }

Error:

 if(DAQmxFailed(error))

  DAQmxGetExtendedErrorInfo(errBuff,2048);

 if(taskHandle!

=0) {

  /*********************************************/

  //DAQmxStopCode

  /*********************************************/

  DAQmxStopTask(taskHandle);

  DAQmxClearTask(taskHandle);

 }

 if(DAQmxFailed(error))

  printf("DAQmxError:

%s\n",errBuff);

 printf("Endofprogram,pressEnterkeytoquit\n");

 getchar();

 return0;

}

  所以,使用LabVIE



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有