今天我们来讲讲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再次被加载了