DLL模块的显示载入和符号链接
LoadLibrary/Ex 显示加载dll,映射到进程的地址空间
FreeLibrary 显示卸载dll
FreeLibraryAndExitThread 适用dll中创建线程的情况
同一进程中多次加载一个dll会递增使用计数
不同进程之间维护各自的dll使用计数
可以使用GetModuleHanle检测dll是否被映射到进程的地址空间
GetProcAddress 获取符号地址(对应函数)
第二个参数可以是函数名(ANSI,而不是UNICODE)或者序号
比如
GetProcAddress(hDll,”Func”);
GetProcAddress(hDll,MAKEINTRESOURCE(2)); 虽然快,但是不建议用~
DLL的入口点函数
需要在dll中接收通知(用以进行初始化或者清理工作)时,可以实现入口函数,否则无需
DllMain 区分大小写~
避免在DllMain中调用LoadLibrary,循环依赖
DllMain应该执行简单初始化:设置线程局部存储区、创建内核对象、打开文件等,避免调用User、Shell、ODBC、COM、RPC以及套接字函数,因为他们可能尚未初始化完毕或者内部调用LoadLibrary
DLL_PROCESS_ATTACH通知:
只有第一次进程映射dll时才会触发此通知,之后的会增加dll的使用计数
此时DllMain的返回值表示初始化是否成功
DLL_PROCESS_DETACH通知:
Dll从进程空间撤销映射时触发此通知,执行与进程相关的清理工作
如果是因为进程中的一个线程调用FreeLibrary而撤销映射,此线程将执行DllMain中的代码,处理完此通知之前,线程不会返回
Dll可能阻止进程的终止,只有当每个dll处理完此通知之后,进程才会终止
TerminateProcess 不会触发此通知调用dll的DllMain,从而没有机会进行清理共工作,所以应该避免使用此函数终止进程
DLL_THREAD_ATTACH通知:
进程创建线程时,映射到此进程的所有dll文件,会触发此通知调用DllMain,执行线程相关的初始化,而之后映射的dll(在创建线程之后)不会再触发此通知
另外,主线程不会调用此通知
DLL_THREAD_DETACH通知:
ExitThread中止线程前会触发此通知执行与线程相关的清理工作
同理Dll可能会妨碍线程的终止,只有当每个dll处理完此通知,线程终止
同理TerminateThread尽量避免使用
由于DllMain的序列化调用,不要在DllMain中调用WaitForSingleObject
如果不提供自己的Dllmain,编译器会链接C/C++运行库的DllMain
延迟载入DLL
延迟载入dll是隐式链接,只有当我们引用其中符号时才会载入dll,而不是一开始就载入
延迟载入的dll可以被卸载
延迟载入可以得到通知或覆盖默认的行为
函数转发器
将一个函数调用转发到另一个dll中的另一个函数
#pragma comment(linker,”/export:func=Dll2.Func2”)
已知的DLL
系统对操作系统提供的某些dll进行了特殊处理,这些dll在同一个目录中,可以在注册表中查看:HCM\SYSTEM\CurrentControlSet\Control\Session Manager\KnowDlls
LoadLibrary加载前先判断后缀名是否有.dll,没有就正常规则搜索,有的话就先去掉再在KnowDlls中查找,找到加载,找不到按正常规则搜索
DLL重定向
强制操作系统的加载程序首先从程序的目录中载入模块,找不到再去其他目录找
XXXXXX.exe.local放到程序目录
这个特性对COM对象来说很有用
模块的基地址重定位
能够显著提高系统性能
ReBase.exe
ReBaseImage
模块的绑定
进一步提高性能~
用该模块导入的所有符号的虚拟地址对该模块的导入段进行预处理
Bind.exe
BindImageEx