11.2.1 DLL技术简介
(1)DLL入口函数。在创建DLL时,可以有选择地指定入口点函数。当进程或线程将它们自身附加到DLL或者将它们自身从DLL分离时,将调用入口点函数。可以使用入口点函数根据DLL的需要来初始化数据结构或者销毁数据结构。此外,如果应用程序是多线程的,则可以在入口点函数中使用线程本地存储(TLS)来分配各个线程专用的内存。以本实例的入口函数为例,如下所示。
- 01 BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call,
- 02 LPVOID lpReserved)
- 03 {
- 04 //保存模块实例句柄
- 05 g_hInstance = (HINSTANCE)hModule;
- 06 //在进程结束或线程结束时卸载钩子
- 07 switch (ul_reason_for_call)
- 08 {
- 09 case DLL_PROCESS_ATTACH:
- 10 break;
- 11 case DLL_THREAD_ATTACH:
- 12 break;
- 13 case DLL_PROCESS_DETACH:
- 14 case DLL_THREAD_DETACH:
- 15 delete g_lpdwVirtualKey;
- 16 if (g_hHook != NULL) UnhookWindowsHookEx(g_hHook);
- 17 break;
- 18 }
- 19 return TRUE;
- 20 }
(2)导出DLL函数。有两种方法可以导出DLL函数,可以为导出的DLL函数添加函数关键字,也可以创建模块定义文件(.def)以列出导出的DLL函数:
为导出的DLL函数添加函数关键字。要使用函数关键字,但必须使用以下关键字来声明要导出的各个函数:
- __declspec(dllexport)
并且在应用程序中使用导出的DLL函数,必须使用以下关键字来声明要导入的各个函数:- __declspec(dllimport)
说明:通常情况下,最好使用一个包含 define 语句和 ifdef 语句的头文件,以便分隔导出语句和导入语句。
创建模块定义文件(.def)以列出导出的DLL函数。使用模块定义文件来声明导出的DLL函数。当使用模块定义文件时,不必向导出的DLL函数中添加函数关键字。在模块定义文件中,可以声明DLL的LIBRARY语句和EXPORTS语句。本实例使用的就是这种方法。
下面的代码演示怎样同.def文件将函数add声明为DLL导出函数(需在dllTest工程中添加lib.def文件)。
- ; lib.def : 导出DLL函数
- LIBRARY dllTest
- EXPORTS
- add @ 1
.def文件的规则如下:
LIBRARY语句说明.def文件相应的DLL。
EXPORTS语句后列出要导出函数的名称。可以在.def文件中的导出函数名后加@n,表示要导出函数的序号为n(在进行函数调用时,这个序号将发挥其作用)。
.def 文件中的注释由每个注释行开始处的分号(;)指定,且注释不能与语句共享一行。
由此可以看出,例子中lib.def文件的含义为生成名为dllTest的动态链接库,导出其中的add函数,并指定add函数的序号为1。
(3)链接DLL到可执行程序。有隐式链接及显式链接两种方法链接DLL到可执行 程序。
隐式链接方法是指应用程序先链接到编译DLL时生成的导入库文件(lib)。在执行应用程序时,系统会载入lib对应的DLL。这种方式在应用退出程序前无法释放DLL。
显式链接是指利用Win API函数LoadLibrary()来动态载入DLL,获取并保存DLL模块的句柄。然后利用GetProAddress()函数来获取DLL函数地址的指针。在使用完毕后,可以调用FreeLibrary()函数释放DLL。