labwindows/cvi 2019入门(5)——通过LoadLibrary调用DLL文件

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函数。

我发现读取的硬盘串号前面一大堆空格,不知道什么问题。


image.png

全部源代码如下:

#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;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容