20.3 延迟载入DLL
(1)延迟载入的目的
①如果应用程序使用了多个DLL,那么它的初始化可能比慢,因为加载程序要将所有必需的DLL映射到进程的地址空间。→利用延迟加载可将载入过程延伸到执行过程时
②如果我们的代码调用的操作系统的一个新函数,但程序又试图在老版本的操作系统运行。这时程序会被终止。这时可以有两种解决方法:一种是判断利用GetVersionEx操作系统,在老系统中使用旧函数而不使用新函数。另一种是通过延迟载入,通过SEH来捕获异常。
(2)延迟载入技术
①延迟载入是针对隐式链接DLL的
②一个导出了字段(如即全局变量)的DLL是无法延迟载入的
③Kernel32.dll模块是无法延迟载入的,因为必须载入该模块才能调用LoadLibrary和GetProcAddress。
④不应在DllMain入口函数中调用一个延迟载入的函数,这可能导致程序崩溃
(3)使用方法及相关说明
①使用方法
A常规建立DLL和可执行模块
B链接可执行模块时,添加延迟加载开关
Ⅰ、为了延迟加载Dll,还需要在解决方案的该项目“属性”->“配置属性”->“链接器”->“输入”->“延迟加载的Dll”中输入MyDll.dll(注意/DelayLoad:MyDll.dll这个开关不 能用#pragma comment(linker, "/DelayLoad:MyDll.dll")来设置。
Ⅱ、增加/Lib:DelayImp.lib开关:这可以用#include <delayimp.h>和#pragma comment(lib, "Delayimp.lib")。这个开关告诉链接器将delayimp 中的__delayLoadHelper2函数嵌入到我们的可执行文件中。
Ⅲ、如果需要手动卸载Dll,则需在可选“链接器”→“高级”中指定“卸载延迟加载的DLL”中输入“MyDll.dll”。但要注意两点:一是卸载时只能 调用__FUnloadDelayLoadedDll2(PCSTR szDll)函数,而不能调用FreeLibrary。二是该卸载操作是可选的,不是必需的,只有在需要手动卸载Dll时才设置。
②/Lib:DelayImp.lib此时链接器将执行下列的事项
A、将MyDll.dll从.exe的导入段去除,这样操作系统就不会隐式载入该DLL
B、在.exe中嵌 入一个新的延迟载入段(Delay Import Section,称为.didata)表示要从MyDll.dll中导入哪些函数。
C、对延迟载入函数的调用会跳转到__delayLoadHelper2函数,来完成对延迟载入函数的解析。
③其他说明
A、应用程序对延迟载入函数的调用实际上会调用__delayLoadHelper2函数,该函数会引用那个特殊的延迟载入段,并用LoadLibrary和GetProcAddress得到延迟载入函数的地址,然后修改对该函数的调用,这样以后将直接调用该延迟载入函数。
B、同一个DLL中的其他函数仍然必须在第一次被调用的时修复,即其他函数第1次调用时仍然会LoadLibrary+GetProcAddess并修复函数地址。
【Export/ImportDelay程序】演示延迟载入Dll
注意图中第1次列出模块中没有20_ExportDelay.dll,而第2次有且执行了其DllMain函数
//Dll源文件
/************************************************************************ Module: ExportDelay.h ************************************************************************/ #pragma once #ifdef DELAYLIB_EXPORT //MYLIB_EXPORT必须在Dll源文件包含该头件前被定义 #define DELAYAPI extern "C" __declspec(dllexport) //本例中所有的函数和变量都会被导出 #else #define DELAYAPI extern "C" __declspec(dllimport) #endif //定义要导出的函数的原型 DELAYAPI int Func_A(int iVal); DELAYAPI int Func_B(int iVal1, int iVal2); DELAYAPI int Func_C(int iVal1, int iVal2,int iVal3);
#include <windows.h> #include <tchar.h> #include <locale.h> //在这个DLL源文件定义要导出的函数和变量 #define DELAYLIB_EXPORT //这个源文件中须定义这个宏,以告诉编译器函数要 //__declspect(dllexport),这个宏须在包含MyLib.h //之前被定义 #include "ExportDelay.h" BOOL APIENTRY DllMain(HMODULE hDllHandle, DWORD dwReason, LPVOID lpreserved) { static TCHAR pModuleName[MAX_PATH] = {}; switch (dwReason) { case DLL_PROCESS_ATTACH: _tsetlocale(LC_ALL, _T("chs")); GetModuleFileName(hDllHandle, pModuleName, MAX_PATH); _tprintf(_T("进程[%u]调用线程[0x%X]加载DLL[0x%08X]:%s\n"), GetCurrentProcessId(),GetCurrentThreadId(),hDllHandle,pModuleName); break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: break; case DLL_PROCESS_DETACH: //GetModuleFileName(hDllHandle, pModuleName, MAX_PATH); _tprintf(_T("进程[%u]调用线程[0x%X]卸载DLL[0x%08X]:%s\n"), GetCurrentProcessId(), GetCurrentThreadId(), hDllHandle, pModuleName); break; } return TRUE; } int Func_A(int iVal) { return iVal; } int Func_B(int iVal1, int iVal2) { return iVal1 + iVal2; } int Func_C(int iVal1, int iVal2, int iVal3) { return iVal1 + iVal2 + iVal3; }
//exe源文件
#include <windows.h> #include <tchar.h> #include <strsafe.h> #include <locale.h> #include <Psapi.h> //For EnumProcessModules函数 #include "../../Chap20/20_ExportDelay/ExportDelay.h" //#include <delayimp.h> //#pragma comment(lib, "Delayimp.lib") //Vs2013这两行可要可不要了。 #pragma comment(lib,"psapi") #pragma comment(lib,"../../Debug/20_ExportDelay.lib") //1、延迟加载是针对Dll的隐式链接的 //2、为了延迟加载Dll,还需要在解决方案的该项目“属性”->“配置属性”-> //“链接器”->“输入”->“延迟加载的Dll”中输入20_ExportDelay.dll void PrintModules(DWORD dwProcessID){ HMODULE* phMods = NULL; HANDLE hProcess = NULL; DWORD dwNeeded = 0; TCHAR szModName[MAX_PATH] = {}; hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwProcessID); if (NULL == hProcess){ _tprintf(_T("不能打开进程[ID:0x%X],错误码:[%u]\n"), dwProcessID, GetLastError()); return; } EnumProcessModules(hProcess, NULL, 0, &dwNeeded); phMods = (HMODULE*)malloc(dwNeeded); if (EnumProcessModules(hProcess,phMods,dwNeeded,&dwNeeded)){ for (DWORD i = 0; i < (dwNeeded / sizeof(HMODULE));i++){ ZeroMemory(szModName, MAX_PATH*sizeof(TCHAR)); if (GetModuleFileNameEx(hProcess,phMods[i],szModName,MAX_PATH)){ _tprintf(_T("\t(0x%08X)\t%s\n"), phMods[i], szModName); } } } free(phMods); CloseHandle(hProcess); } int _tmain(){ _tsetlocale(LC_ALL, _T("chs")); //显示进程中己加载的模块(此时不含(ExportDelay.dll) PrintModules(GetCurrentProcessId()); _tsystem(_T("PAUSE")); int iVal1 = 10; int iVal2 = 20; int iVal3 = 30; _tprintf(_T("Func_A(%d)=%d\n"),iVal1,Func_A(iVal1)); _tprintf(_T("Func_B(%d,%d)=%d\n"), iVal1,iVal2,Func_B(iVal1,iVal2)); _tprintf(_T("Func_C(%d,%d,%d)=%d\n"), iVal1,iVal2,iVal3, Func_C(iVal1,iVal2,iVal3)); PrintModules(GetCurrentProcessId()); _tsystem(_T("PAUSE")); return 0; }
原文:http://www.cnblogs.com/5iedu/p/5014798.html