CVI可以通过多种方法调用外部dll文件,如通过“Create ActiveX Controller”或者“Create .NET Controller”等。但并不是都有效,如本文中的gethw.dll就不行。通过使用LoadLibrary方法却成功了。这种方法的一个好处是动态加载,在需要的地方加载,用完就释放掉。缺点是要知道dll中的函数原型。而通过CVI工具菜单中的create controller的方法可以自动分析和生成新的方法头文件供调用,但要确保activex动态链接库已激活注册或者是纯正的.net动态链接库。
动态载入 DLL
动态载入DLL方式是指在编译之前并不知道将会调用哪些 DLL 函数, 完全是在运行过程中根据需要决定应调用哪些函数。
方法是:用 LoadLibrary 函数加载动态链接库到内存,用 GetProcAddress函数动态获得 DLL 函数的入口地址。当一个 DLL 文件用 LoadLibrary 显式加载后,在任何时刻均可以通过调用 FreeLibrary 函数显式地从内存中把它给卸载。
动态调用DLL使用的 Windows API 函数主要有 3 个, 分别是 LoadLibrary、 GetProcAddress 和FreeLibrary。
三个函数的功能说明
1.LoadLibrary 函数
[格式]:function LoadLibrary(LibFileName : PChar): Thandle;
[功能]:加载由参数 LibFileName 指定的 DLL 文件。
[说明]:参数 LibFileName 指定了要装载的 DLL 文件名,如果 LibFileName 没有包含一个路径,系统将按照:当前目录、Windows 目录、Windows 系统目录、包含当前任务可执行文件的目录、列在 PATH 环境变量中的目录等顺序查找文件。
如果函数操作成功,将返回装载 DLL 库模块的实例句柄,否则,将返回一个错误代码,错误代码的定义如下表所示。
错误代码 | 含义 |
---|---|
0 | 系统内存不够,可执行文件被破坏或调用非法 |
2 | 文件没有被发现 |
3 | 路径没有被发现 |
5 | 企图动态链接一个任务错误或者有一个共享或网络保护错误 |
6 | 库需要为每个任务建立分离的数据段 |
8 | 没有足够的内存启动应用程序 |
10 | Windows 版本不正确 |
11 | 可执行文件非法或不是Windows 应用程序,或在. EXE映像中有错误 |
12 | 应用程序为一个不同的操作系统设计(如 OS/2) |
13 | 应用程序为 MS DOS 4. 0 设计 |
14 | 可执行文件的类型不知道 |
15 | 试图装载一个实模式应用程序(为早期Windows 版本设计) |
16 | 试图装载包含可写的多个数据段的可执行文件的第二个实例 |
19 | 试图装载一个压缩的可执行文件(文件必须被解压后才能被装载) |
20 | DLL 文件非法 |
21 | 应用程序需要 32 位扩展 |
假如在应用程序中用 LoadLibrary 函数装入某一个 DLL 前, 其他应用程序已把该 DLL 装入内存中了,则系统将不再装入该 DLL 的另一个实例,而是使该 DLL 的“引用计数”加 1 。
2.GetProcAddress 函数
[格式]:function GetProcAddress(Module:Thandle; ProcName:PChar): TfarProc;
[功能]:返回参数 Module 指定的模块中,由参数 ProcName 指定的过程或函数的入口地址。
[说明]:参数 Module 是包含被调用函数的 DLL 句柄,这个值由 LoadLibrary 返回, ProcName是指向含有函数名的以null结尾的字符串指针,或者可以是函数的次序值,但大多数情况下,用函数名是一种更稳妥的选择。如果该函数执行成功,则返回 DLL 中由参数 ProcName 指定的过程或函数的入口地址,否则返回 null 。
3.FreeLibrary 函数
[格式]:procedure FreeLibrary(Module: Thandle);
[功能]:将由参数 Module 指定的 DLL 文件从内存中卸载 1 次。
[说明]:Module 为 DLL 库的句柄。这个值由 LoadLibrary 返回。由于 DLL 在内存中只装载一次,因此调用 FreeLibrary 首先使 DLL 的引用计数减 1,如果计数减为 0 则卸载该 DLL。
[注意]:每调用一次 LoadLibrary 函数就应调用一次 FreeLibrary 函数,以保证不会有多余的库模块在应用程序结束后仍留在内存中,否则导致内存泄漏。
关于GetHW.dll
这个动态链接库文件可以在网上搜到,很早的一个文件,据查应该是Delphi编写的。主要用于获取计算机的硬件信息。去年一次偶然机会,发现我们采购的设备上位机软件中用这个文件生成机器码进行软件授权,感觉是个不错的思路,所以研究了一下。找不到可以QQ我:491114509
软件应用实例
通过CVI构建如下图所示程序,通过动态调用Get'HW.dll文件,可以获取本地计算机的相关硬件信息。
//记得加载windows头文件
#include <windows.h>//我们需要WINDOWS的API函数。
我发现读取的硬盘串号前面一大堆空格,不知道什么问题。
全部源代码如下:
#include <windows.h>
#include <formatio.h>
#include <cvirte.h>
#include <userint.h>
#include "GetHW.h"
//定义dll中函数原型
typedef char * (__stdcall *GetIdeSN)();
typedef char * (__stdcall *GetBiosSN)();
typedef char * (__stdcall *GetMAC)(int);
typedef char * (__stdcall *GetCPUInfomation)(int);
static int panelHandle;
char *id;//硬盘ID
char *bios;//BIOS信息
char *mac;//MAC地址
char *CPUSerialNumber;//CPU规格
char *CPUSpecification;//CPU规格
char *CPUManufacturers;//CPU制造商
GetIdeSN getIdeSN;
GetBiosSN getBiosSN;
GetMAC getMAC;
GetCPUInfomation getCPUInfomation;
/*请注意以下几个声明,它们可是很重要的!!!*/
BOOL fFreeResult, fRunTimeLinkSuccess = FALSE;
int main (int argc, char *argv[])
{
if (InitCVIRTE (0, argv, 0) == 0)
return -1; /* out of memory */
if ((panelHandle = LoadPanel (0, "GetHW.uir", PANEL)) < 0)
return -1;
DisplayPanel (panelHandle);
RunUserInterface ();
DiscardPanel (panelHandle);
return 0;
}
int CVICALLBACK Exitwin (int panel, int control, int event,
void *callbackData, int eventData1, int eventData2)
{
switch (event)
{
case EVENT_COMMIT:
QuitUserInterface (0);
break;
case EVENT_GOT_FOCUS:
break;
case EVENT_LOST_FOCUS:
break;
}
return 0;
}
int CVICALLBACK Run (int panel, int control, int event,
void *callbackData, int eventData1, int eventData2)
{
switch (event)
{
case EVENT_COMMIT:
//装载动态链接库GetHW.dll
HINSTANCE hInstDll = LoadLibrary("GetHW.dll");
// If the handle is valid, try to get the function address.
if (hInstDll != NULL)//成功装载动态链接库GetHW.dll
{
getIdeSN = (GetIdeSN)GetProcAddress(hInstDll, "getIdeSN");
//取函数指针地址
// If the function address is valid, call the function.
fRunTimeLinkSuccess = (getIdeSN != NULL);
if (fRunTimeLinkSuccess) //dll中有函数MyDLLCdeclFunction()
{
id = getIdeSN();
SetCtrlVal (panelHandle, PANEL_STRING, id);
}
getBiosSN = (GetBiosSN)GetProcAddress(hInstDll, "getBiosSN");
fRunTimeLinkSuccess = (getBiosSN != NULL);
if (fRunTimeLinkSuccess) //dll中有函数MyDLLCdeclFunction()
{
bios = getBiosSN();
SetCtrlVal (panelHandle, PANEL_STRING2, bios);
}
else
goto endcall;
getMAC = (GetMAC)GetProcAddress(hInstDll, "getMAC");
fRunTimeLinkSuccess = (getMAC != NULL);
if (fRunTimeLinkSuccess) //dll中有函数MyDLLCdeclFunction()
{
mac = getMAC(0);
SetCtrlVal (panelHandle, PANEL_STRING3, mac);
}
else
goto endcall;
getCPUInfomation = (GetCPUInfomation)GetProcAddress(hInstDll, "getCPUInfomation");
fRunTimeLinkSuccess = (getCPUInfomation != NULL);
if (fRunTimeLinkSuccess ) //dll中有函数MyDLLCdeclFunction()
{
CPUSerialNumber = getCPUInfomation(1);
SetCtrlVal (panelHandle, PANEL_STRING4, CPUSerialNumber);
CPUSpecification = getCPUInfomation(2);
SetCtrlVal (panelHandle, PANEL_STRING5, CPUSpecification);
CPUManufacturers = getCPUInfomation(3);
SetCtrlVal (panelHandle, PANEL_STRING6, CPUManufacturers);
}
else
goto endcall;
// Free the DLL module
endcall:
fFreeResult = FreeLibrary(hInstDll);//卸载动态链接库GetHW.dll
}
// 如果调用dll失败,显示错误提示。
if (! fRunTimeLinkSuccess)
{
MessagePopup ("错误提示:","从GetHW.dll中加载方法失败!");
}
break;
case EVENT_GOT_FOCUS:
break;
case EVENT_LOST_FOCUS:
break;
}
return 0;
}