今天我們來講講DLL的高級應用:DLL的延遲加載
若要延遲加載,必須要多加兩個鏈接程序開關
/Lib:DelayImp.lib
/DelayLoad:MyDll.dll
第一個開關告訴鏈接程序將一個特殊的函數delayLoadHelper嵌入你的可執行模塊
第二個開關有以下幾個功能:
1.從可執行文件的輸入節中刪除MyDll.dll,這樣,程序在初始化時就不會顯示加載dll
2.建立一個延遲輸入節,以指明哪些函數從MyDll.dll中被輸入
3.通過轉移到對delayLoadHelper的調用,轉換到對延遲加載函數的調用
delayLoadHelper引用延遲輸入節,並且知道先調用LoadLibrary之後,再調用GetProcAddress,一旦獲得了延遲加載函數的地址,下次再調用延遲加載函數就無需再通過delayLoadHelper轉換到對延遲加載函數的調用
但是,如果你下次調用的是dll中的另一個函數,那就還需要通過delayLoadHelper去找到這個函數的地址
延遲加載的函數也可以即時卸載__FUnloadDelayLoadedDLL2(pszDll)
但是一定要設定鏈接開關/Delay:unload
附上一個例子,dll是自己編寫的,裏面有add和sub兩個函數
生成的dll的名稱爲DLLGenerator.dll
在這裏需要注意的是,延遲加載和可卸載的開關一定要在編程軟件中設置,通過#pragma comment命令設置無效
2>main.obj : warning LNK4229: 遇到無效的指令“/DelayLoad:DLLGenerator.dll”;已忽略
2>main.obj : warning LNK4229: 遇到無效的指令“/Delay:unload”;已忽略
設置如下
#include <Windows.h>
#include <stdio.h>
#include <delayimp.h>
#include "DLLGenerator.h"
TCHAR g_szModuleName[] = TEXT("DLLGenerator");
void IsModuleLoaded(PCTSTR pszModuleName);
#pragma comment(lib,"Delayimp.lib")
#pragma comment(lib,"DLLGenerator.lib")
//#pragma comment(linker,"/DelayLoad:DLLGenerator.dll") //註釋的這兩句
//#pragma comment(linker,"/Delay:unload")
int main()
{
IsModuleLoaded(g_szModuleName);
add(1, 2);
IsModuleLoaded(g_szModuleName);
add(1, 2);
PCSTR pszDll = "DLLGenerator.dll";
__FUnloadDelayLoadedDLL2(pszDll);//卸載dll
IsModuleLoaded(g_szModuleName);
add(1, 2);
IsModuleLoaded(g_szModuleName);
system("pause");
return 0;
}
void IsModuleLoaded(PCTSTR pszModuleName)
{
HMODULE hMod = GetModuleHandle(pszModuleName);
char sz[100];
#ifdef UNICODE
wsprintfA(sz, "Module \"%S\" is %S loaded\n", pszModuleName, (hMod == NULL) ? L"not" : L"");
#else
wsprintfA(sz, "Module \"%s\" is %s loaded\n", pszModuleName, (hMod == NULL) ? "not" : "");
#endif
printf(sz);
}
運行結果
沒調用add之前,dll沒被加載
調用add之後,dll被加載了
又調用了一次add,但是調用了__FUnloadDelayLoadedDLL2函數,所以,dll顯示沒被加載
再次調用add,dll再次被加載了