Dynamic Link Libraries
DLL
DLL可以将一些代码段编译后放到一个库中,并可以被多个程序所使用。也就是说,只需用存储一份动态连接库文件就可以使多个程序共享,从而减小了程序的体积。DLL与静态链接库的不同之处在于在编译的时候静态链接库会被编译到可执行程序中,而DLL则不会进入程序。这样程序在运行时就可以只在必要时才加载DLL,从而节省了内存空间。
_ _ declspec
_ _ declspec这个关键字并不被微软官方支持,但使用的人很多,几乎成为默认的标准。其中,最重要的两个是
• __declspec(dllexport)
• __declspec(dllimport)
dllexport
这个声明是指本函数可以被其它程序使用,否则那个函数就只能被库里的其它函数使用。
示例:
__declspec(dllexport) int MyFunc1(int foo)
dllimport
当我们要使用某个DLL中的函数时,我们在函数声明时需要添加这个关键字。
示例:
__declspec(dllimport) int MyFunc1(int foo);
在使用这个声明后,这时我们就可以像使用自己的函数一样使用这个库函数,底层的细节是由操作系统和编译器一起来处理。
我们可以使用宏定义的方法来简化这两个声明的使用:
#ifdef BUILDING_DLL
#define DLL_FUNCTION __declspec(dllexport)
#else
#define DLL_FUNCTION __declspec(dllimport)
#endif
这样,只有当我们在创建DLL时才需要定义BUILDING_DLL这个宏。
示例:
DLL_FUNCTION int MyFunc1(void);
DLL_FUNCTION int MyFunc2(void);
DllMain
当windows将一个dll库链接到程序时,windows会自动调用程序的 DllMain 函数,也就是说,每个DLL都必须 含有 DllMain 这个函数。DllMain 函数的定义如下:
BOOL APIENTRY DllMain (HINSTANCE hInstance, DWORD reason, LPVOID reserved)
APIENTRY是windows系统内的一个自定义常量,没必要过多的了解它。reason 是下面四个不同值中的一个:
DLL_PROCESS_ATTACH
一个新程序第一次链接这个库
DLL_PROCESS_DETACH
一个程序与这个库的链接断开
DLL-THREAD_ATTACH
一个程序中的线程与这个库链接
DLL_THREAD_DETACH
一个程序中的线程与这个库解链接
一个典型的 DllMain 函数
BOOL APIENTRY DllMain (HINSTANCE hInst, DWORD reason, LPVOID reserved)
{
switch (reason)
{
case DLL_PROCESS_ATTACH:
break;
case DLL_PROCESS_DETACH:
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
}
return TRUE;
}
动态链接库链接到程序的两种方式:
(1)静态链接方式
这时主要工作是由连接器来做。在编译一个DLL时,编译器将生成两个文件,一个是DLL库文件,另一个是静态链接存根.LIB文件。这个.LIB文件工作起来类似于一个小的静态库,可以告诉连接器从哪儿可以找到相应的DLL文件。
(2)动态加载DLL方式
这意味着程序在执行时可以动态地查找和加载相应的库文件,而不必重新编译。实现这个功能的方法是 LoadLibrary 方法,使用loadlibrary方法可以得到所需库的句柄(handle),从而可以将这个句柄(handle)传递给程序,使得程序可以从DLL中拿东西。LoadLibrary方法的原型是:
HMODULE WINAPI LoadLibrary(LPCTSTR lpFileName);
HMODULE是一个程序模块的句柄。lpFileName是希望加载的DLL文件的文件名。当我们在加载一个模块时,系统会先去检查你的PATH设置。如果你希望系统先去其它的目录中查找DLL,使用SetDllDirectory 这个函数。
在一个DLL加载后,你可以做以下这些事情:
(1)使用GetProcAddress函数来返回一个库函数的指针。
(2)使用LoadResouce从DLL中拿资源。
(3)当我们使用完库函数时,使用FreeLibrary函数将其从内存中移除。