Windows 动态链接库编程
1、介绍
Windows操纵系统是应用最关的操纵系统,因此动态链接库也为措施员所熟悉,即使对付普通的使用者来说,很多时候也会碰到.dll结尾的文件,这就是动态链接库文件。Windows下的动态链接库可以通过参考头文件和.lib库文件进行编译,从而使得动态链接库隐式地被使用;也可以使用LoadLibrary、GetProcAddress等函数来显式挪用动态链接库。
2、语法、导入导出
在Windows编程中,对付要使用或被使用的函数或者变量,需要使用 __declspec 关键字来声明,以报告编译器该变量或函数不是普通的变量或函数,而是一个动态链接库的接口属性。
如果界说一个要被其他代码使用的函数,可以写成:
__declspec( dllexport ) int add(int a, int b);
如果在该代码中,筹算使用此外一个措施中的变量,则可以写成:
__declspec( dllimport ) char *name;
动态链接库凡是包罗一系列供其他措施使用函数,,因此 declspec( dllexport ) 语法形式最为常用。如果动态库需要其他措施中的界说的全局变量,则需要在其他措施中使用导出该变量,在动态链接库中则需要使用 extern declspec( dllexport ) 将该变量声明为外部变量以便使用。
3、链接方法
可以以下列两种方法之一链接到(或加载)DLL:
隐式链接
显式链接
隐式链接有时称为静态加载或加载时动态链接。显式链接有时称为动态加载或运行时动态链接。在隐式链接下,使用 DLL 的可执行文件链接到该 DLL 的创建者所供给的导入库(.lib 文件)。使用 DLL 的可执行文件加载时,操纵系统加载此 DLL。客户端可执行文件挪用 DLL 的导出函数,就仿佛这些函数包罗在可执行文件内一样。
在显式链接下,使用 DLL 的可执行文件必需进行函数挪用以显式加载和卸载该 DLL,并访谒该 DLL 的导出函数。客户端可执行文件必需通过函数指针挪用导出函数。可执行文件对两种链接要领可以使用同一个 DLL。此外,由于一个可执行文件可隐式链接到某个 DLL,而另一个可显式附加到此 DLL,故这些机制不是互斥的。
4、隐式链接
隐式链接动态链接库对照简单,不予详述。
5、显式链接API函数
显式链接主要涉及到3个API函数( LoadLibrary , GetProcAddress 和 FreeLibrary ),要使用这些函数包罗windows.h头文件即可。
(1)HINSTANCE LoadLibrary(LPCSTR lpLibFileName);
该函数用来加载指定动态库文件,并且返回句柄。
参数lpLibFileName为动态链接库的名称。Windows 首先搜索“已知 DLL”,如 Kernel32.dll 和 User32.dll。然后按下列挨次搜索 DLL:
1、当前进程的可执行模块地址的目录。
2、当前目录。
3、Windows 系统目录。GetSystemDirectory 函数检索此目录的路径。
4、Windows 目录。GetWindowsDirectory 函数检索此目录的路径。
5、PATH 环境变量中列出的目录。
(2)FARPROC GetProcAddress (HMODULE hModule, LPCSTR lpProcName);
函数GetProcAddress 用来获取 DLL 导出函数的地点。返回由lpProcName指定的变量或函数指针。
参数hModule为已经加载的动态库文件的句柄。
参数lpProcName为要挪用的变量或函数名称。
(3)BOOL FreeLibrary(HMODULE hModule);
从内存中释放hModule所代表的动态链接库。
(4)如果产生错误,可以挪用GetLastError()函数或去错误代码。
6、显示链接举例
(1)动态库文件代码:dll_demo.c
#include <stdio.h>
__declspec( dllexport ) int add(int a, int b)
{
printf("calling add\n");
return a + b;
}
该文件中的add()函数计算两个整数之和,并且返回之前打印提示字符串。函数使用 __declspec( dllexport ) 语法来说明函数add(int a, int b)要被导出。
(2)客户端事例代码:main.c
#include <stdio.h>
#include <windows.h>
int main (int argc, char *argv[])
{
int a = 10, b = 20;
int c = 0;
HINSTANCE hInstLibrary = NULL;
int (*add)();
printf ("Load DLL file\n");
if ((hInstLibrary = LoadLibrary(L"dll_demo.dll")) == NULL)
{
printf ("***LoadLibrary ERROR: %d.\n", GetLastError());
return 1;
}
if((add = (int (*)())GetProcAddress(hInstLibrary, "add")) == NULL) {
printf ("***GetProcAddress ERROR: %d.\n", GetLastError());
return 1;
}
c = add(a, b);
printf("%d + %d = %d\n", a, b, c);
FreeLibrary(hInstLibrary);
return 0;
}
措施操作LoadLibrary函数加载动态链接dll_demo.dll,操作FreeLibrary*句柄,操作GetLastError()获取错误代码,操作GetProcAddress定位共享库中的add函数,然后挪用该函数执行加法计算,并打印功效。
(3)编译与运行
编译共享库:
在VS.Net中创建一个动态链接库项目,名称为dll_demo,插手文件dll_demo.c,编译后生成dll_demo.dll文件。
编译事例措施:
在VS.Net中创建一个动态链接库项目,名称为dll_main,插手文件main.c,编译后生成dll_main.exe可以执行文件。
运行:
将 dll_demo.dll 和 dll_main.exe 放在同一个目录下,然后双击运行 dll_main.exe。
输出:
Load DLL file
calling add
10 + 20 = 30
7、挪用动态链接库中的变量
也可以使用动态链接库中的变量。例如,在动态链接库中界说:
__declspec( dllexport ) int num = 100;
那么可以在事例措施中这样挪用:
int *d;
d = (int *)GetProcAddress(hInstLibrary, "num");
printf("num = %d\n", *d);