技术频道

VC++和基于Lab Windows/CVI的DLL在测控技术中的应用

摘 要:介绍了动态链接库(DLL)的运行机制,阐述了基于Lab Windows/CVI的DLL的开发以及它和VC++程序的集成方法,对于两种程序之间的通讯,给出了一种基于内存映射的实现方法。文章最后给出了一个技术上具体实现的例子加以说明。

关键词: 虚拟仪器;Lab Windows/CVI;DLL;内存映射

1、引言

  大型测控系统的软件系统通常采用高级语言结合专用测控软件来开发。如何将不同的软件程序加以集成,以及如何在它们之间进行通讯是必须解决的问题。

  虚拟仪器测控系统中关键工作就是对数据的采集、分析、处理并模拟真实仪器面板的功能。由于VC++等高级语言的非针对性,采用其实现起来不仅复杂(如创建仪器面板,数据分析等),而且有些功能无法实现,源代码效率也较低;若采用Lab Windows/CVI,不仅开发方便,而且直观,有些开发工作只需几行代码即可完成,可以大大提高系统开发的效率,节约时间。在笔者所参与的测控系统的开发中,采用的是用VC++开发系统软件主干程序,负责系统软件各方面的调度、管理,采用Lab Windows/CVI开发具体的测控功能,以具有不同功能的DLL形式组成测试功能模块库,集成到主干程序中实现各种不同的测控功能,采用内存映射实现两种程序间通讯和数据传输。

2、Lab Windows/CVI中DLL的开发

  2.1 Lab Windows/CVI中DLL的开发方式及其运行机制

  在CVI环境下,开发用于测控领域的动态链接库,需将编译目标文件类型设置为DLL,并设置DLL名称、存储路径、DLL函数/变量导出方式等,才能通过Create Debuggable Dynamic Link Library生成DLL工程。

  DLL不是可执行文件,它需要由应用程和另外的DLL调用执行。一个程序使用DLL,它只能通过这个DLL的导出函数/变量访问其内部。在Lab Windows/CVI下创建DLL,有两种方式导出函数/变量:头文件法和导出关键字法。头文件法使用头文件确定要输出的标号,头文件中必须包含要导出标号的声明;导出关键字法把每一个要导出的函数和变量都标记一个关键字,如__cdecl、__stdcall等,根据不同的编译器使用不同的导出关键字。在DLL中只有被导出的函数/变量才能被外界所使用,所以只有指定DLL的导出函数/变量,该DLL才具有实际的使用意义。

  DLL被调用时,它有自己的运行机制:每一个DLL都有一个DLLMain主函数,在进入和退出DLL时,应用程序分别调用这个函数,它常常被用来执行进程的初始化和清理工作。因为在该函数中定义了两个事件句柄——DLL_PROCESS_ATTACH和DLL_PROCESS_DETACH。当一个DLL被调用,即它被首次映射到进程的地址空间时,系统触发DLL_PROCESS_ATTACH事件,一般在这部分执行特定进程的DLL初始化工作,如调用InitCVIRTE()函数初始化引擎等;当应用程序结束DLL的调用时,系统触发DLL_PROCESS_DETACH事件,一般在这部分进行退出DLL时前的资源清理工作,如调用CloseCVIRTE ()释放被DLL所占用的内存。另外,CVI还提供了一个RunStateCallback()函数来对程序的执行情况进行记录,使用时通过该函数也可以控制程序的执行及系统资源分配。

  2.2工程中DLL的开发

  笔者所参与的测控系统的开发中, DLL开发采用的是另外一种方式,由于DLL本身的不可执行性,调试上没有可执行文件方便,而且工程上需要在导出的DLL中包含虚拟仪器软面板,且保持软面板对虚拟仪器的控制性,因此,在实际的开发中,先编写CVI用于实现具体测控功能的可执行文件,调试成功后,把该可执行文件改成具有相同功能的DLL。改编时,仅仅对源文件(.c文件)的main函数做一些改变,并在头文件(.h文件)中输出,而无需做多的改变即可导出仪器软面板,这样在VC++主干程序调用该DLL时就可以方便的进行测控工作。

  具体操作上:

  (1)首先编写满足测控要求的CVI的可执行文件并调试成功;

  (2)在工程文件中打开包含工程主函数main()的.c文件,向文件中插入DLL所必须的DLLMain函数。

  (3)改写.c文件中的main函数。修改其函数名及参数,将其参数改为Windows的实例句柄HINSTANCE,它指向DLL被调用时DLL被映射到的进程的地址空间。另外由于调用时要使用仪器软面板,就必须导出相应的.uir函数,因此main中的编译环境默认的Loadpanel函数要用LoadpanelEx替代,因为使用Loadpanel函数时,用户界面库无法找到在.uir文件中定义的但没有被DLL输出的那些回调函数,从而使DLL在被调用时出错。对于LoadpanelEx,因为使用CVI编写DLL时,CVI自动在工程中生成一个.uir中的回调函数列表,DLL被调用时,LoadpanelEx首先在此列表中进行查找,它可以实现回调函数的导出,从而保证在DLL中软面板对虚拟仪器的正常控制。

  (4)将修改后的main函数的声明加入到原工程的头文件。

  (5)选择头文件导出法,编译成DLL文件。

  经过改编后原工程就变成了可以由VC++主干程序调用的DLL,而它的所有测试功能都没有变。事实证明,因为这种创建DLL的方法不需要增加额外的程序进行代码调试,它比一般的直接创建方法要有效的多。

3 VC++应用程序对DLL的调用

  3.1 VC程序调用DLL的两种方式

  VC中链接DLL到应用程序中有两种方式:隐式链接和显式链接。隐式链接时,使用DLL的可执行程序链接到DLL导入库(.lib文件)中,导入库中包含了DLL中的每个导出符合和序号。当加载使用DLL的可执行程序时,操作系统同时加载DLL。为了隐式链接DLL,可执行程序需要从DLL提供者获取以下内容:包含导出函数声明的头文件(.h),导入库(.lib)文件和实际的DLL(.dll)文件。显示链接时,使用DLL的可执行程序在运行时通过函数调用来显式加载或卸载DLL,并通过函数指针来调用DLL的导出函数,应用程序通过Loadlibrary函数来加载DLL并获取模块句柄;通过GetProcessAddress来获取应用程序要调用的导出函数的指针。

  工程中笔者采用的是显式链接方式调用DLL,与隐式链接相比,显示链接较灵活,在这种方式下,我们可以决定什么时候装载和卸去DLL,以及决定加载哪个DLL,可以节约系统资源,代码执行效率高。

  3.2 VC程序与测试模块间的通信

  在用VC++调用CVI的DLL时,存在着在不同软件程序间的数据传递问题,工程中VC++程序是主干,它调用DLL,因此它的变量对DLL来说都是透明的、可用的;但是由Lab Windows/CVI开发的DLL文件生成的导出库文件(.lib)不包含导出变量,因此DLL中的变量值,VC++不能直接获得。工程中采用创建内存映射文件的方法来实现二者之间的数据传递。所谓内存映射文件是指在内存中申请一块内存空间,将一个文件与这块空间相联系,再进行内存映射,这样操作文件就有和操作内存一样的效率,数据可以通过该映射文件进行中转(写入和读出),几个进程可通过操作该映射文件,实现进程间在内存一级的高速数据交互。

  具体方法为:首先在DLL中通过API函数CreateFileMapping及MapViewOfFile在内存中建立一个文件映射对象并产生它的一个视,然后通过CopyMemory将测控数据存入

  该视中,然后应用程序就可使用OpenFileMapping打开该映射文件并对其进行读写操作,从而实现二者间通信。

4 代码实例

  限于篇幅,文中只给出部分重要的程序代码。

  4.1 CVI中程序代码

  1)对源文件的main函数进行改写:

  int main1 (HINSTANCE CVIUserHInst)// 修改函数名以及参数

  {

  if ((panelHandle = LoadPanelEx (0, "get.uir", PANEL,__CVIUserHInst)) < 0) //使用loadPanelEx函数可导出回调函数

  return -1;

  ……

  return 0;

  }

  (2)创建内存映射文件并拷入数据

  hValue=CreateFileMapping((HANDLE)0xFFFFFFFF,NULL,PAGE_READWRITE,0,0x300,"MapFile");//建立文件映射对象,MapFile为 映射文件名

  ……

  ipmid=(mid*)MapViewOfFile(hValue,

  FILE_MAP_ALL_ACCESS,0,0,0);//创建映射对象的一个视,mid为测控数据类型

  ……

  CopyMemory(ipmid,&data,sizeof(mid));//将数据拷贝到共享的内存中

  (3)在.h文件中加入修改后的main函数的导出声明

  int main1 (HINSTANCE CVIUserHInst);

  4.2 VC中程序代码

  typedef int(*main1)(HINSTANCE); HINSTANCE USEInst;

  USEInst=LoadLibrary("sousuoleidafashe_dbg.dll"); //加载DLL

  main1 main; main=(main1)GetProcAddress(USEInst,

  "main1");//得到DLL中导出函数main1的地址

  main(USEInst);//执行main1,调出仪器软面板进行测试,测控数据写入”MapFile”中

  FreeLibrary(USEInst);

  HValue=OpenFileMapping(…);//打开映射文件”MapFile”

  ipmid=(mid*)MapViewOfFile(hValue;

  FILE_MAP_ALL_ACCESS,0,0,0); //得到测试数据

  至此,就完成了LabWindows/CVI中的DLL开发和VC++主干程序对它的调用工作以及数据传送工作。

5 结束语

  本文介绍了一种把VC++和LabWindows/CVI结合起来进行测控系统开发的方法,根据两种开发工具各自的优缺点,取VC++对数据库、多媒体等的强大的开发能力和它广泛的适用性,结合LabWindows/CVI对测控数据强大的分析处理能力和它简单直观的仪器软面板设计方法。实践证明这种方法是完全可行的。

6 参考文献

  [1]NI Corporation.LabWindows/CVI User Interface Reference Manual [M/CD], 1996.

  [2] 张磊, 虚拟仪器测试系统中实现数据共享的方法[J],计算机自动测量与控制, 2000年第05 期:58-60

  [3] 白凤山,动态连接库(DLL)在虚拟仪器中的应用[J],自动化与仪表,第16卷,2001年第01期:21-22

  [4] [美]David J.Kruglinski,Visual C++ 技术内幕[M],北京,清华大学出版社,1999年

文章版权归西部工控xbgk所有,未经许可不得转载。