DLL简介:
DLL是动态链接库(Dynamic Link Library)的缩写,通过名称来看它是一个库文件。库文件有静态库文件和动态库文件之分,静态库文件需要包含到项目工程中,然后通过编译链接完成的库文件提供功能的调用;动态库文件就不需要静态库文件这些操作,但在调用动态库文件提供的功能时,需要应用程序将该库文件加载到内存中,然后调用该库文件提供的功能。从这你有没有看出DLL的作用,通常我们将需要经常变动的功能模块代码做成DLL库文件,然后约定好DLL的应用接口,在更新DLL功能模块代码后,我们只要提供新的DLL库文件就行,而不用重新编译整个应用工程。以笔者的经验,在嵌入式开发中,工程师一般接触到驱动类的DLL比较多,这类DLL一般用于驱动设备,下面笔者将介绍LabwindowsCVI DLL的创建和使用。
DLL工程的创建:
- 新建Labwindows Workspace
- 在Worspace新建dll工程
- 在dll工程上新建.c和.h头文件。
- 在.c文件的空白地方单击,然后单击 Edit->Insert Construct->DllMain,将生成DllMain函数
代码分析:
Dll工程的.c代码如下所示:
#include "MakeDLL.h"
#include <ansi_c.h>
#include <cvirte.h>
int __stdcall DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
printf("DLL_PROCESS_ATTACH\r\n");
break;
case DLL_PROCESS_DETACH:
printf("DLL_PROCESS_DETACH\r\n");
break;
}
return 1;
}
int __stdcall DllEntryPoint (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
/* Included for compatibility with Borland */
return DllMain (hinstDLL, fdwReason, lpvReserved);
}
void __stdcall MyDLLStdcallFunction(void) {
printf("Dll function is called\r\n");
}
其中MakeDLL.h是DLL库文件的头文件,主要用于声明DLL函数的接口和一些宏定义。本文件包含3个函数,分别是DllMain、DllEntryPoint、MyDLLStdcallFunction,其中DllMain、DllEntryPoint是Dll库文件的入口函数,本质上这2个函数实现的功能是一样的,MyDLLStdcallFunction是自定义的DLL函数。下面将分别介绍DllMain函数与MyDLLStdcallFunction函数。
DllMain函数:
DllMain是库文件的入口函数,就像main一样,在该函数中我们主要处理DLL_PROCESS_ATTACH和DLL_PROCESS_DETACH事件,其中DLL_PROCESS_ATTACH事件在该Dll库文件被加载时触发,我们可以在该事件中添加初始化代码; DLL_PROCESS_DETACH事件是在该Dll库文件被释放时触发,我们可以在该事件中添加释放资源的代码。在本测试代码中只打印事件被触发时的名称。
MyDLLStdcallFunction函数:
MyDLLStdcallFunction是我们自定义的DLL函数,用于外部程序的加载和调用。在要导出的DLL函数的声明和定义中,要添加__stdcall关键词。
LabwindowsCVI DLL函数导出设置:
对LabwindowsCVI Dll工程来说,需要配置IDE来告诉其哪些DLL函数是可以被外部程序加载和调用的,其步骤如下所示:
- 单击Build->Target Settings,如下图所示:
- 在弹出的对话框中,在Exports方框中单击Chang..
- 在弹出的对话框中,选择要导出的DLL函数声明的头文件并在Export what选择 Include file and marked symbols,如下图所示。
- 单OK按钮,完成配置。
LabwindowsCVI DLL库文件的使用:
LabwindowsCVI DLL库文件的使用与Visual C++的使用一样,就是调用windows的底层库来实现的,其主要由LoadLibrary、GetProcAddress、FreeLibrary 这3个函数来实现,下面将分别介绍这些函数:
LoadLibrary函数:
LoadLibrary函数用于加载DLL库文件到内存中,这样我们就能调用DLL函数了,其函数原型如下所示:
HMODULE LoadLibrary(LPCSTR lpLibFileName);
可以看出其参数就是DLL库文件的路径,当其加载DLL库文件成功后,将返回DLL库文件的句柄,否则返回NULL。
GetProcAddress函数:
GetProcAddress函数用于提取DLL库文件导出的某个具体DLL函数,其函数原型如下所示:
FARPROC GetProcAddress(HMODULE hModule,LPCSTR lpProcName);
可以看出其有2个参数,一个为DLL库文件的句柄,另一个为DLL导出函数的名称,当其在hModule库中找到与lpProcName想匹配的DLL函数时,返回该DLL函数的地址,否则返回NULL。
FreeLibrary函数:
FreeLibrary函数用于释放DLL模块,其函数原型如下所示:
BOOL FreeLibrary( HMODULE hLibModule);
可以看出其只有一个参数,就是DLL模块的句柄,当其释放DLL模块成功时,将返回非零的值,否则返回0值。
项目实践:
在LabwindowsCVI工程中需要调用DLL库文件的函数时,需要在工程中添加 #include <windows.h>头文件,如下是调用DLL函数的工程代码:
#include <cvirte.h>
#include <windows.h>
#include <ansi_c.h>
#include <utility.h>
typedef void (*MyDLLFunctionType)(void);
HMODULE dllHandle = 0;
MyDLLFunctionType dllFunc;
int main (int argc, char *argv[])
{
if (InitCVIRTE (0, argv, 0) == 0)
return -1; /* out of memory */
dllHandle = LoadLibrary(".\\MakeDllTest.dll");
if(dllHandle != NULL)
{
printf("LoadLibrary Successful\r\n");
dllFunc = GetProcAddress(dllHandle,"MyDLLStdcallFunction");
if(dllFunc != NULL)
{
dllFunc();
}
else
{
printf("GetProcAddress Failed\r\n");
}
FreeLibrary(dllHandle);
}
else
{
}
Delay(5.0);
return 0;
}
其执行效果,如下所示: