第十九章 DLL的一些基本概念
1.DLL的两种链接方式:
在使用DLL前,程序必先加载DLL程序(将DLL映射到进程的地址空间)。有如下两种方式加载DLL程序:
(1)隐式链接
(2)显示链接
2.静态与动态CRT库
一个进程的地址空间是由一个可执行程序模块和多个DLL模块组成的。
这些模块有的链接的是CRT静态库,有的链接的是CRT动态库(DLL),有的没有链接CRT.
这将导致在进程地址空间中存在多个CRT的副本(由CRT静态库导致)
在这种情况下,程序运行可能会产生问题,如下:
//DLL A中的函数
VOID EXEFunc()
{
PVOID pv = AllocMem();//调用DLL B中的函数
free(pv);//这里可能会出问题
}
//DLL B中的函数
PVOID AllocMem()
{
return(malloc(100));
}
DLL B中提供了一个函数AllocMem,该函数功能是分配一块内存,并将所得内存的地址返回。
DLL A调用DLL B中的AllocMem函数获取内存,然后调用free去释放所得内存。在free的时候可能会产生问题。
解决办法如下有以下两种:
(1)所有DLL模块(不引用CRT的DLL模块除外)和可执行程序模块都只引用动态库。
(2)DLL B再提供一个释放内存的函数FreeMem,让DLL A调用FreeMem来释放内存。
即保证同一模块中若提供了内存分配的函数的同时也要提供释放内存的函数
//修改后的DLL A
VOID EXEFunc()
{
PVOID pv = AllocMem ();
FreeMem (pv);
}
//在DLL B中新加一个函数
BOOL FreeMem(PVOID pv)
{
return(free(pv));
}
3.构造DLL
(1)仅导出函数
DLL可以导出全局变量和类,但我们不建议这么做,建议导出函数。
(2).lib
每个DLL都有与之相对应的.lib文件,该文件中列出了DLL中导出的函数和变量的符号名
(3)指定要导出的函数名
因为不同编译器的Name mangle规则不同,这就导致DLL不能跨编译器使用。
有以下两种方法可以解决这个问题:
1.在.def文件中指定要导出的函数名
2.在编译指中指定要导出的函数名:
#pragma comment(linker, "/export:MyFunc=_MyFunc@8")
4.DLL加载路径
当需要加载一个DLL时,系统会依照下面的顺序去寻找所需DLL直到找到为止,然后加载,否则加载失败。
(1)当前可执行文件路径
(2)GetWindowsDirectory返回的Windows系统路径
(3)16位系统的路径 windows"system
(4)GetSystemDirectory返回的Windows系统路径
(5)当前进程所在路径
(6)PATH环境中所指定的路径
5.Common API:
LoadLibrary LoadLibraryEx
GetModuleFileName GetModuleHandle GetProcAddress
FreeLibrary FreeLibraryAndExitThread