LabWindows / CVI Chapter IV of entry: library file (rpm)

note:

  After using reference books Reference CVI CVI to generate dynamic library calls in another project DLL, compiled pass, after the reference to this article, suddenly.

http://blog.sina.com.cn/s/blog_6373e9e60101bpsm.html

4.1 Static and dynamic library

 

  4.1.1 Introduction

    Through the study of the first few chapters, we have mastered the use of CVI development programs related to the UI, hardware and software components. But in real life, the project code of the first few chapters few dozen lines of hundreds of lines as an example of a program almost non-existent, we encountered more of a future line of tens of thousands and even tens of millions of lines of code in software development projects. This medium-sized and large software projects generally require persons to engage in cooperative development, it is necessary at this time the project is divided into a small functional modules to allow other programmers to call in the application. In Windows, often using object files (* .obj), a static library (* .lib) or dynamic link library file (* .dll) divided functional modules.

    Object files and static library files more similar, functions, and stored data is compiled into a binary machine code after, when using object files or static library files, the linker (Linker) to find these representatives from the target file or static library function and binary code data and copies them to the exe application file, and other modules are combined and generate the final exe file can be executed. Therefore, the object files and static library files only play the role of a "binary data source", when the final release version of the exe, because the exe file already contains data in the target file or static library, so no released with the exe . Object files and static library files is different is that the target file is usually binary files or other programming languages ​​C file is compiled get, not to publish; and static library files are generally released for use by other programmers or other development environments.

    Dynamic Link Library (DLL, Dynamic Link Library) files are introduced functional modules since the first version of the Windows operating system, there's an alternative called multiple programs from Microsoft. With the target file, as statically linked library, most of the DLL is stored in the machine code can be executed directly (use the term "majority" instead of "all" reasons will be under "4.4 CVI call .Net platform the DLL "is mentioned). With the static library file is different, when the application is running is linked to a DLL function modules, that is, when the application is published its link DLL files need to publish. In programming, the generally more inclined to use DLL rather than a static library for the following reasons:

· DLL supports any programming language under other Windows, to avoid compatibility problems compiler

· As long as the DLL export functions the same interface, modify the dynamic link library function modules do not have to modify the code of other modules with interdependent

· In the same Windows operating systems, different applications can share the same DLL, can reduce the application's executable file size, save space

· To publish DLL program can provide a convenient secondary development platform to the user without having to worry about their own source code leak

  4.1.2 The use of static and dynamic library

    Because the static library file is actually compiled block of code in a project to add a static library, like adding a "encrypted" files as ordinary programming languages. After when you need to call the static library file, simply static library file (* .lib) and the appropriate header files added to the project, a reference library header files in other files, call a function or variable to a static library .

    When using dynamic library files, often used in the other two files: an import library (* .lib) file and a header (* .h) files. Although the import library file extension is .lib, but the introduction of library files and dynamic link library static library file has a fundamentally different. In general, a static library files contain all variables and functions as well as function machine code execution, while the DLL import library file contains only the name of the DLL functions and variables derived, and real executable machine code in DLL file.

    Under normal circumstances, when compiled DLL import library (.lib) file will be created together. However, if the import library file is missing, the CVI in the menu by clicking O ptions-the Generate DLL the I Mport Library ... to generate an import library file.

  Examples 4.1.3 Static library generation and use of

    In order to facilitate full understanding of the concepts and functions of static library, we might as well give an example. Examples of dynamic libraries will be "4.2 CVI generating DLL" and "4.3 CVI call DLL" is given, this measure is not given specific examples.

    We called a new "StaticLib" of the project, the new Main.c, StaticLib.c and StaticLib.h three files in the project.

    Wherein, in the document StaticLib.c added the following code:

image

    Add the following code StaticLib.h header file:

image

image

    Add the following code in Main.c:

image

    After the establishment of the entire project is completed, the project under the CVI directory shown in Figure 4-1.

image

Figure 4-1 New project directory

    We can see from the above code, in StaticLib.c file, we wrote a AddTest function, enter two int variables, and returns two variables. In StaticLib.h file, we provide a statement AddTest function. In Main.c file, we call AddTest function. Engineering results of the above operation shown in Figure 4-2.

image

Figure 4-2 project operating results

    若此时我们右击StaticLib工程中的Main.c文件,选择Exclude File from Build,如图 4‑3所示,然后点击CVI菜单-Build-Target Type-Static Library,将工程的输出由exe文件改为静态库文件,则编译之后,CVI将会编译未被排除在编译列表的StaticLib.c文件,并在工程目录下会生成一个名为“StaticLib.lib”的静态库文件。

image

4‑3 将Main.c文件从编译列表中排除

    生成静态库文件之后,将StaticLib.c文件排除在编译列表之外,将刚生成的StaticLib.lib文件添加进工程中,并恢复Main.c文件到编译列表中,恢复工程编译生成的目标类型为可执行文件(菜单Build-Target Type-Executable)。此时工程目录如图 4‑4所示。

image

4‑4 添加库文件的工程目录

    重新编译工程,运行后将会得到跟刚才完全一样的结果。如图 4‑5所示。

image

4‑5 从静态库中运行的结果

4.2 CVI生成DLL

 

 4.2.1 CVI下生成DLL文件

    在上一节中,我们通过一个实例了解了在CVI下生成静态库文件的过程。生成DLL的步骤跟生成静态库的步骤基本相同,除了配置生成文件类型的时候选择“Dynamic Link Library”而不是“Static Library”之外。

    设置生成文件类型为DLL后,在工程中只保留StaticLib.c文件与StaticLib.h文件在编译列表中,将其他文件排除到编译列表之外,继续Build上一节的工程。此时CVI弹出如所示的对话框,提示用户没有任何函数被导出。

image

图 4‑6 CVI提示没有任何函数被导出

    若此时点击OK,DLL文件仍然可以被生成,但是DLL文件里不包含任何函数的任何信息。此时若使用CVI安装目录sdk\bin下的DEPENDS.EXE程序查看生成的DLL中包含的函数,则可以知道,在生成的DLL文件中确实不包含任何的函数的信息。如图 4‑7所示。

image

4‑7 使用Depends程序查看DLL中发现不包含任何函数信息

    出现以上问题的原因在于,我们没有定义导出的函数库列表。点击CVI菜单-Build-Target Settings…之后,弹出如图 4‑8所示的窗口,在窗口中可以对生成的DLL进行设置。

image

4‑8 设置目标DLL的属性

    在上面的对话框中,点击Exports中的Change…按钮,将Export what设置为Include file symbols,并点击下面的StaticLib.h使得前面打勾,点击OK即可。

image

图 4‑9 设置工程的导出内容

    图 4‑8对话框各种设置说明如下:

    (1) DLL file 设置DLL文件的类型(Debug/Release)以及位置、文件名。Debug类型的DLL文件可以方便的进行调试,而Release类型的DLL文件中去除了调试信息,文件体积较小,运行速度相对较快,适合发布。

    (2)Import library base name 设置DLL导入库文件名。

    (3)Where to copy DLL 设置是否将生成的DLL文件拷贝到System目录下或驱动目录下

    (4)Run-time support 是否提供CVI运行引擎的支持

    (5)Embed project .UIRs 是否将*.uir文件编译到DLL文件中

    (6)Generate map file 是否生成map文件

    (7)Version Info设置DLL文件的版本信息

    (8)Import Library Choices… 设置生成DLL导入库的方式,是只与当前编译器兼容还是与CVI支持的四种编译器兼容。

    (9)Type Library 在CVI中很多库文件拥有自己独特的数据类型,如VISA中整数类型为ViInt32,在这个选项中可以定义是否将这些类型库和帮助文档连接到DLL文件。

    (10)LoadExternalModule Options 是否将外部的库文件编译到DLL中

    (11)Exports 设置输出的方式,是将头文件定义的内容全部导出,还是只导出具有导出标志的内容。

设置导出列表之后,Build工程,则CVI发出如图 4‑10所示的提示,告诉用户DLL已经成功生成。

image

4‑10 DLL生成成功的提示

    其中.dll(Dynamic Link Library)文件是动态链接库文件,.cdb(CVI Debug)是供调试用的参数文件,.lib(Library)文件是DLL文件的导入库文件。需要注意的是,此时的.lib导入库文件跟之前生成的.lib静态库文件内容与大小均不一样。这再次提醒我们,静态库文件与DLL的导入库文件虽然都是*.lib,但是内容不同,不能混淆。

    若需将此次编译出来的DLL发布以供其他编译程序或者其他程序员使用,只需要将StaticLib_dbg.dll、StaticLib_dbg.lib以及StaticLib.h这三个文件打包发布即可。

  4.2.2 在CVI下调用CVI生成的DLL

    发布DLL文件时,一般会连同导入库(*.lib)文件、头(*.h)文件一起发布。有了导入库(*.lib)文件的帮助,在CVI下调用动态链接库DLL的方法跟调用静态链接库(*.lib)的方法大同小异。

    调用静态库时,我们需要将静态库(*.lib)文件加入工程,并将必要的头文件加入工程之后,在工程的其他文件中即可调用静态库文件中的函数或者参数。

    而在调用动态链接库时,我们需要把动态链接库的导入库(*.lib)文件加入工程,并将必要的头文件加入工程之后,在工程的其他文件中即可调用动态链接库中的函数或者参数。

    需要额外注意的是,因为动态链接库(*.dll)文件中的二进制机器码并不会被连接器(Linker)复制到exe文件中,所以在运行、发布时,需要确保DLL文件已经在系统的system目录下或者工程当前目录下存在。

    在下面的例子中,我们将使用上一节生成并发布的StaticLib_dbg.dll、StaticLib_dbg.lib以及StaticLib.h。

    我们在工程中添加Main.c源文件、StaticLib.h头文件以及StaticLib_dbg.lib导入库文件,将StaticLib_dbg.dll文件复制到工程所在目录下。此时工程目录如图 4‑11所示。

image

4‑11 调用DLL文件的工程目录

    其中Main.c文件与上一节中的内容完全一致。如下所示:

image

image

    将工程的生成文件类型设置为Executable,编译、运行程序,程序运行结果如图 4‑12所示。

image

4‑12 调用DLL运行结果

  4.2.3 在VC下调用CVI生成的DLL

    在VC中调用DLL跟在CVI中调用DLL的整体思路相同。将DLL的导入库文件以及库文件的头文件加入工程中,即可在其他编程语言文件中调用该DLL中的函数以及参数。

    下面我们将以一个实例来调用上一节发布的StaticLib_dbg.dll、StaticLib_dbg.lib以及StaticLib.h文件。

   建立VC工程

    打开VC6.0,在VC6.0中建立一个名为TestDll的控制台工程。如图 4‑13所示,将StaticLib.h文件、StaticLib_dbg.lib导入库文件以及StaticLib_dbg.dll动态链接库文件复制到工程所在目录下,并将头文件及导入库文件加入到工程中。

image

4‑13 在VC中新建控制台工程

   编写C语言文件

    新建TestDll.cpp文件,并在cpp文件中输入以下代码,并将其加入到工程。

image

   编译、调试

    点击组建按钮,我们注意到,编译器此时并没有成功的找到AddTest函数,而是报以下错误:

image

    在上一节我们使用CVI调用该DLL时,完全正常,但是为什么此时会出现问题呢?

   问题的原因

    我们注意到,上述错误是一个连接器错误。而且我们考虑到,从CVI迁移到VC,无非是换了一个编译器。而编译器理论上应该都支持ANSI C标准,理论上这种改变不会导致出现此类问题。两个集成开发环境最大的不同在于其环境变量不同。

    问题就出在这儿。由于每个C语言(*.c)或C++语言(*.cpp)文件都是被编译器编译为目标文件之后再交由连接器(Linker)进行连接的。每个编译器在生成目标文件的过程中,编程语言文件中的函数名会被重命名,而对于C++编译器来说,环境变量不同,重命名的方法也就不同。

    作为一种面向对象的语言,C++支持函数重载,而过程式语言C则不支持。函数被C++编译后在符号库中的名字与C语言的不同。例如,假设某个函数的原型为:

void foo( int x, int y );

    该函数被C编译器编译后在符号库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字(不同的编译器在不同的环境中可能生成的名字还略有区别)。

    在上述例子中,C++编译器对TestDll.cpp进行编译时,AddTest函数在目标文件中被重命名为_Add_Test_int_int,而CVI中C编译器对StaticLib.c进行编译时AddTest函数在导入库文件中被重命名为_Add_Test。两个函数在目标文件中的名字不相同,连接器自然也就无法成功的将两个文件组建成一个exe文件。

   解决C++编译器兼容问题

    C++语言的创建初衷是“a better C”,因此C++之父在设计C++之时就考虑到C++里面的重载会引起C库与C++库的不兼容。为了在C++中尽可能的支持C,extern “C”{}就是其中的一个策略。extern “C”{}常用在头文件中,使用方法如下:

image

    为了让编译器得知StaticLib.h文件中所有的函数及参数都是由C编译器编译产生的,我们只需要把头文件的所有内容加入到extern “C”内即可。

    但是将extern “C”{}加入头文件以使得C与C++编译器兼容存在另外一个问题。extern “C”{}本身并不是C语言的关键字,extern “C”加在C语言中会引起编译错误。为了避免C语言编译错误,我们只需要在extern “C”的声明前后分别加入#if defined (__cplusplus)以及#endif即可。

    修改之后的StaticLib.h文件如下所示:

image

   再次编译、运行

    再次编译,运行,则原先的连接错误不再出现,运行结果如图 4‑14所示。

image

4‑14 VC调用CVI生成的DLL运行测试

4.3 CVI调用DLL

 

    上节我们已经初步接触到了在CVI中调用DLL的方法。在本节中,我们将系统的介绍CVI调用其他编译器生成的DLL的方法。

    调用DLL的方法分为‘显示调用’和‘隐式调用’两种方法。

    隐式调用方法需要被调用DLL的头文件,采用创建DLL导入库.lib文件的方法调用。该方式使用Options选项卡中的Generate DLL Import Library向导创建DLL导入库.lib(若提供DLL同时也提供了.lib则不需要此步),将导入库.lib添加至工程项目后,就可以通过函数名方便灵活的使用被调用DLL中的函数,需要注意的是,头文件中需要对所调用的函数进行申明。

    显式调用方法不需要被调用DLL的头文件以及导入库文件,使用windows.h提供的Loadlibrary、GetProcAddress、Freelibrary函数,直接根据指针访问DLL中的函数。该方式适用于没有DLL的头文件以及导入库文件,但知道被调用函数的原型的场合。

  4.3.1 显式调用方法的例子

    下面我们创建一个名为LoadLibrary的工程,并只将上节发布的StaticLib_dbg.dll文件复制到工程目录下,使用显示调用的方法使用StaticLib_dbg.dll文件中的AddTest函数。

    我们在LoadLibrary工程中创建一个LoadLibrary.c的C语言源文件,创建完毕之后,LoadLibrary工程目录如所示。

image

图 4‑15 LoadLibrary工程目录

    向LoadLibrary.c文件中添加如下内容:

image

    在上述文件中,我们先通过加载DLL文件获取DLL文件的句柄,然后从DLL文件中查得AddTest函数的指针地址,然后赋值给一个函数指针,随即执行函数指针所指向的函数,即可显示最终执行的结果。

    上述工程不需要使用头文件也不需要使用导入库文件。运行结果如图 4‑16所示。

image

4‑16 显示调用DLL文件运行结果

    对大家来说,上述代码让人不解的地方可能在于函数指针的使用。其实函数指针是内核编程等常用到的一种指针,通过函数指针的使用大大增加了灵活度,使程序变得更加清晰和简洁。如果大家的C语言功底足够强,应该可以发现,上述C语言代码中,main函数内的代码可以精简为以下四行代码:

image

    对函数指针感兴趣的同学可以深入研究可以这样精简的原因,此处尽起一个抛砖引玉的作用,具体内容不再深究。

  4.3.2 VC生成DLL文件

    在VC下编写一个简单的DLL程序也并不复杂。下面我们将以一个实例来说明在VC下编写一个简单的DLL程序的方法。

   组建VC下的DLL工程

    打开VC,点击菜单-文件-新建,在工程页面中选择Win32 Dynamic-Link Library,选择工程路径与工程名称之后,点击确定,创建一个空白的DLL工程。

image

图 4‑17 VC下新建一DLL工程

    新建一个C++源文件(*.cpp)与头文件(*.h),添加到工程中。其中cpp文件的源代码如下:

image

    头文件的源代码如下:

image

image

    点击VC组建按钮,则VC发出如下提示,告诉大家组建成功,DLL文件已经生成。

image

    我们使用“4.2.1 CVI下生成DLL文件”中提到的DEPENDS工具查看生成的DLL文件内部是否已经包含了我们所需要的SubTest函数。结果如图 4‑18所示。

image

4‑18 生成的DLL文件内部函数查看

    我们并没有在生成的DLL文件中发现任何函数。同时,我们也没有在DLL的目录下发现有导入库(*.lib)文件生成!

   问题的原因

    跟在CVI下生成DLL时遇到的问题相似,我们并没有定义要导出的函数。在VC中,我们需要使用__declspec关键词来定义需要导出的函数。

    __declspec 是MFC提供的修饰符号。在要输出的函数、类、数据的声明前加上__declspec(dllexport)的修饰符,表示该函数、类、数据需要被输出到DLL文件中。

    因此,我们若把头文件中SubTest的声明中添加__declspec(dllexport),即告诉编译器我们需要将SubTest函数导出到DLL文件中。

    添加__declspec(dllexport)关键词之后,我们重新编译,组建,则在Debug目录下生成了我们所需要的导入库文件、DLL文件。用DEPENDS工具查看生成的DLL,可以发现SubTest函数已经在DLL文件中了。如图 4‑19所示。

image

4‑19 DLL文件中已经包含导出的SubTest函数

  4.3.3 CVI下调用VC生成的DLL

    在CVI下调用VC生成的DLL,跟调用CVI生成的DLL并无区别。

    我们新建一CVI工程,将上一小节生成的DLL文件、导入库(*.lib)文件以及头文件复制到CVI工程目录下,将导入库(*.lib)文件添加到工程中,在CVI工程中添加一C语言源文件,代码如下:

image

    编译、连接、运行之后,测试结果如图 4‑20所示。

image

4‑20 CVI调用VC生成的DLL的运行测试

    通过上面的示例程序我们可以发现,使用CVI调用VC生成的DLL的步骤跟调用CVI生成的DLL步骤几乎完全一样。

    需要额外注意的是,倘若VC生成的DLL运行时也依赖与其他的非系统system下的DLL(运行依赖的DLL可以使用DEPENDS工具查看),那么也需要将对应的DLL文件复制到CVI工程所在文件夹下或者复制到系统的system目录下。

4.4 CVI调用.Net平台下的DLL

 

    常用的DLL包括托管DLL和非托管DLL两种。托管DLL为中间代码,完全依赖于.NET平台运行,而非托管DLL是机器代码,不依赖于.NET平台运行。C#、VB.NET和F#编程采用纯.NET语言开发,生成的DLL属于托管DLL。而平时接触到的其它语言编写的DLL,包括C/C++、CVI、LabVIEW编写的DLL都属于非托管DLL。托管的应用程序和DLL可以直接调用非托管的DLL,而非托管的应用程序和DLL必须通过.NET Runtime才能调用托管的DLL。

    前两节介绍的调用的DLL与生成的DLL都是非托管的DLL,是机器代码,不依赖于.Net平台运行。但是若在CVI下调用.Net平台下的DLL,那么则需要借助CVI的.Net工具。

    CVI提供了帮助用户调用.Net程序集的.Net库。用户可以通过菜单-Tools-Create .Net Controller…向导生成可在CVI下使用的.Net库。通过使用CVI以及.Net库,我们可以完成以下工作:

·注册.Net控件并且加载控件

·创建.Net对象并且调用.Net对象

·管理系统资源

·创建.Net数组,并从.Net数据中获取元素

·获取.Net的错误与异常信息

·提供.Net组件的基本信息

·同COM组件交互操作

    但是CVI对.Net的支持也有一些限制。CVI不支持.Net事件和委托。并且CVI也不是一个.Net控件容器。因此,在CVI中不能使用.Net的用户界面对象。

    CVI调用C#等.NET语言编写的DLL需要使用工具选项卡中的‘Creat .NET controller’。通过它生成一个调用.NET汇编代码的包装器(wrapper),该包装器包含对应的仪器驱动、源文件和头文件。包装器生成具体步骤如下:

    (1)选择Tools选项卡中的‘Creat .NET controller’

    (2)一般第三方开发的DLL都不在Global Assembly Cache中,因此弹出对话框中勾选‘Specify Assembly by Path’,选择需要调用的 DLL

    (3)在‘Target Instrument’中指定一个仪器驱动.fp文件,点击OK,CVI程序便生成一个可以调用所选DLL的仪器驱动

    (4)将生成的.fp文件添加至工程项目

    包装器自动生成的仪器驱动中会封装好一些调用函数,函数的命名方式为 [命名空间]_[类名称] ,命名空间和类名称都是在编写.NET程序时定义好的,命名空间也就是DLL的名称。如图 4‑21,ClassLibrary1为命名空间,Class1为类名称,一个DLL中包括一个命名空间,一个命名空间下可以包含一个或多个类,一个类下又可以包含多个函数。图 4‑21中的ClassLibrary1_Class1__add就是一个DLL中的加法函数。

    一般如 Initialize_[命名空间] 和 Close_[命名空间] 两个函数分别调用 CDotNetLoadAssembly 和CDotNetDiscardAssemblyHandle 两个函数,[命名空间]_[类名称]__Create 调用CDotNetCreateGenericInstance 函数, [命名空间]_[类名称]_[函数名称] 调用CDotNetInvokeGenericStaticMember 函数等。

image

4‑21 生成的仪器驱动函数树

    特别注意的是,由于被调用的DLL没有在全局程序集缓存(GAC,Global Assembly Cache)中,调用前需要使用CDotNetRegisterAssemblyPath函数先注册.NET的DLL。如果没有注册将出现如图4所提示的调用失败的错误。全局程序集缓存所在的文件夹为C:\WINDOWS\assembly。

image

图 4‑22 .NET的DLL加载失败错误提示

    另外,C#编程中无需关心垃圾内存的回收(Garbage Collector),而回到C环境中被调用函数涉及的变量最后需要通过手动调用释放内存的函数来释放变量的内存空间。

    编程的主要流程如下:

    (1)声明“[DLL名称]_[类名称]”类型的句柄;

    (2)调用CDotNetRegisterAssemblyPath("[DLL名称] , Version=x.x.x.x, Culture=xx , PublicKeyToken=xx" , ''Full Path of DLL")注册.NET的DLL,DLL的路径分隔符用"\",如D:\\CVI\\Projects\\C#net DLL call;

    (3)调用“Initialize_[DLL名称]”函数初始化.NET controller;

    (4)根据句柄,调用“[DLL名称]_[类名称]__Create”创建被调用DLL的实例;

    (5)调用“[DLL名称]_[类名称]_[函数名称]”等具体函数,编写相应代码;

    (6)调用CDotNetDiscardHandle释放.NET DLL实例句柄;

    (7)调用CDotNetFreeMemory释放变量内存;

    (8)调用“Close_[DLL名称]”卸载.NET DLL;

    需要额外注意数据类型转换的问题,.NET语言中的Enum、Rectangular Array、String、System.Decimal和System.Boolean可以自动完成转换,而COM Run-Time Callable Wrapper (RCW) types、Jagged arrays和Boxed data types需要手动调用库函数转换。

    在接下来的示例中,我们将在C#中写一个基于.Net的DLL,并在系统的全局程序集缓存(GAC)中注册,并在CVI中调用此DLL。

   新建C#类库工程

    打开Visual Studio 2008,新建一个C#类库(Class Library)工程。如图 4‑23所示。

image

4‑23 新建C#类库工程

   为工程添加密钥文件

    点击开始菜单-程序- Microsoft Visual Studio 2008 SDK- Tools- System Definition Model Command Prompt,敲入sn.exe –k c:\1.snk,为C#的工程生成一个密钥文件。生成的密钥文件将会保存在指定的c:\1.snk下。

    打开C#工程的AssemblyInfo.cs文件,在其中插入以下代码:

image

    在工程的Class1.cs文件中,输入以下代码:

image

image

    点击Visual Studio 2008菜单Build-Build Solution,编译C#工程。在工程目录的bin\Debug目录下会生成ClassLibrary1.dll文件。

   把DLL文件注册到系统全局程序集缓存中

    点击开始菜单-程序-管理工具-Microsoft .NET Framework 2.0 配置,如所示,在程序集缓存中,将生成的DLL文件加入全局程序集缓存(GAC)中。

image

图 4‑24 Microsoft .NET Framework 2.0 配置

   添加.Net控件

    新建CVI工程,在CVI中点击菜单Tools-Create .Net Controller…,添加生成的ClassLibrary1.dll文件,并添加生成的.fp文件的路径。点击确定之后,在CVI左下角的函数库窗口的Instruments文件夹下多出了ClassLibrary1库,在当前工程下多出了刚才指定的.fp文件。

   编写代码,编译运行

    新建一C文件,在C文件中添加如下代码,并添加到当前工程中:

image

image

    CVI程序目的是调用基于C#.Net的DLL的函数实现计算1+2的功能。编译运行后,程序运行结果如图 4‑25所示。

image

4‑25 .Net程序运行结果

4.5 示例:CVI获取计算机CPU、硬盘、网卡ID

 

    在实际的软件项目中,当一个收费软件发布时,常常需要用户购买“注册码”,输入正确的注册码之后才可以正常使用。然而,若该注册码一旦被人公开,整个收费软件的注册码形同虚设。为了避免注册码共享后即可被所有人使用的问题,我们必须针对每一台计算机生成独一无二的注册号。

    对于一台计算机而言,软件环境经常会被改变,所以靠检测软件使用环境来识别一台计算机显然不够严密。一般情况下,最常用的方法是获取计算机的CPU序列号、网卡号或者硬盘序列号后来计算得知该计算机的“注册码”。

    然而,在Intel的CPU中,获取计算机序列号是靠一条叫做cpuid的汇编指令来完成的。而在CVI下,笔者尚未发现嵌入汇编语言的方法。倘若一个软件整体框架是采用CVI写的,那么我们可以通过将使用VC来获取CPU序列号的代码封装成一个DLL,以提供给CVI使用。

    软件运行时,首先调用这个DLL获取CPU序列号与网卡号,经过某种算法计算得到“注册码”之后与用户输入的注册码比较,若相同则软件继续运行,若不相同,则用户软件提示相应的注册信息并退出。

    在本小节例子中,我们将只演示获取CPU序列号以及网卡物理地址的内容。获取硬盘序列号的方法略复杂,步骤与获取网卡物理地址类似,此处不再给出具体代码,具体代码可以从例程中获得。

   获取CPU序列号(Intel

    在Intel的处理器中,获取CPU序列号需要用到汇编指令CPUID。由于入口参数存放在EAX寄存器中,执行前,往EAX寄存器赋值,再执行CPUID指令,即可从EAX、EBX、ECX以及EDX中获取CPUID的返回值。具体EAX输入以及四个寄存器输出的对应关系参见表 4‑1。

4‑1 CPUID指令返回值与输入值对应关系表

image

    从上面的表格可以看出,往EAX中赋值0x00,则运行CPUID后可以得到字符GenuineIntel。往EAX中赋值0x01,运行CPUID后从EAX与EBX中获得处理器签名以及一些特性值。往EAX中赋值0x03,运行CPUID后从ECX以及EDX后即可得到CPU的序列号。

   获取网卡号

    获取网卡号通过Windows IP辅助API库(IPHlpApi.h)来完成,通过GetAdaptersInfo函数可以获取网卡的信息,并且将PIP_ADAPTER_INFO结构体中的Address等成员变量进行处理之后显示出来即可。

    运行GetAdaptersInfo函数需要库文件Iphlpapi.lib的支持。Iphlpapi.lib在VC6.0的SDK(可能需要单独安装)的lib文件夹下,若添加

image

    语句之后还不能找到Iphlpapi.lib文件可能是因为没有将Microsoft SDK加入VC的连接目录中导致的。此时需要点击VC菜单-工具-选项-目录-Library files,将Microsoft SDK目录加入其中即可。

   VC最终代码

    了解如何使用VC获取计算机的网卡号与CPU序列号之后,我们就可以着手实现具体程序了。

    首先新建一个空的DLL工程,具体步骤参见“4.3.2 VC生成DLL文件”,并在工程中创建、添加一个cpp文件、一个头文件。CPP文件代码如下:

image

image

image

    头文件代码如下:

image

    In the above code, we performed nested C ++ code and assembly code instruction by __asm. We use Windows IP GetAdaptersInfo auxiliary library functions to achieve the acquired physical address of the network card. By interface functions GetSerialNum, we can easily call the DLL in CVI, implementation of functionality get the CPU serial number, and physical address of the network card.

   CVI calling DLL

    In CVI, we created a project called GetPhyNum, copy the generated DLL file created VC, lib file and header files to the project directory. In a new project for the GetPhyNum.c C language source file and SerialNum.lib import library file is added into the project. Once added, as shown in the project directory.

image

Figure 4-26 CVI acquisition project directory CPU, network card serial number

    GetPhyNum.c source code as follows:

image

    In the C language file, we pass GetSerialNum function by defining a string and string pointer. Completion string will be assigned to a function pointer GetSerialNum runtime. Finally, we call the printf function display to get the string contains the serial number of the CPU and network card physical address obtained.

   Commissioning

    When CVI runs, the CPU will show the card serial number and a physical address on the screen. The results are shown running.

image

FIG 4-27 CVI acquisition CPU serial number, network card physical address operation results

    Run computer hardware detection program Everest view your computer's CPU serial number and network card physical address, can be found in the above program detects the resulting CPU serial number and network card physical address are accurate.

image

FIG 4-28 CPU card serial number and a physical address obtained by the detection software Everest

4.6 Research and Experiment

 

  1.6.1 Experiment

    Get real-time image using CVI USB camera is displayed in the interface, and uses an algorithm to detect a black square on a piece of paper. Detected box with red boxes marked out in real time.

  1.6.2 explore

    Explore the use of Matlab generate DLL files and DLL method of Matlab generated calls in the CVI.

Guess you like

Origin www.cnblogs.com/xihong2014/p/11346676.html