返回
巧用初始化表,三招化解DLL加载失败问题
见解分享
2023-11-27 03:51:16
前言
在项目开发过程中,DLL加载失败是一个令人头疼的问题。本文将通过一个经典示例,详细剖析DLL加载失败的根源,并提供三种化解方案,助力您轻松攻克DLL加载难题。
案例分析
以下是一个简化的示例,包含一个EXE工程和两个DLL工程:
- EXE工程:main.cpp
- DLL工程1:dll1.cpp
- DLL工程2:dll2.cpp
main.cpp中包含以下代码:
#include <windows.h>
#include "dll1.h"
#include "dll2.h"
int main()
{
// 加载dll1.dll
HMODULE hDll1 = LoadLibrary("dll1.dll");
if (hDll1 == NULL)
{
// 加载失败
return -1;
}
// 获取dll1.dll中的导出函数GetCallCount
FARPROC pGetCallCount = GetProcAddress(hDll1, "GetCallCount");
if (pGetCallCount == NULL)
{
// 获取导出函数失败
FreeLibrary(hDll1);
return -1;
}
// 调用dll1.dll中的导出函数GetCallCount
int callCount = ((int(__stdcall*)(void))pGetCallCount)();
// 加载dll2.dll
HMODULE hDll2 = LoadLibrary("dll2.dll");
if (hDll2 == NULL)
{
// 加载失败
FreeLibrary(hDll1);
return -1;
}
// 获取dll2.dll中的导出函数GetCallCount
FARPROC pGetCallCount2 = GetProcAddress(hDll2, "GetCallCount");
if (pGetCallCount2 == NULL)
{
// 获取导出函数失败
FreeLibrary(hDll1);
FreeLibrary(hDll2);
return -1;
}
// 调用dll2.dll中的导出函数GetCallCount
int callCount2 = ((int(__stdcall*)(void))pGetCallCount2)();
// 释放dll1.dll和dll2.dll
FreeLibrary(hDll1);
FreeLibrary(hDll2);
return 0;
}
dll1.cpp中包含以下代码:
#include <windows.h>
// 全局变量
int g_callCount = 0;
// 导出函数
__declspec(dllexport) int GetCallCount()
{
return g_callCount++;
}
dll2.cpp中包含以下代码:
#include <windows.h>
// 导出函数
__declspec(dllexport) int GetCallCount()
{
return 0;
}
运行main.cpp后,可能会遇到DLL加载失败的问题。原因在于,dll1.dll中的全局变量g_callCount在dll2.dll中被引用,但dll2.dll没有加载dll1.dll。因此,当dll2.dll中的导出函数GetCallCount被调用时,会因为找不到dll1.dll中的g_callCount而导致DLL加载失败。
解决方法
为了解决DLL加载失败的问题,有以下三种方法:
- 在dll2.dll中显式加载dll1.dll
// dll2.cpp
#include <windows.h>
// 显式加载dll1.dll
HMODULE hDll1 = LoadLibrary("dll1.dll");
// 导出函数
__declspec(dllexport) int GetCallCount()
{
// 如果dll1.dll加载失败,返回0
if (hDll1 == NULL)
{
return 0;
}
// 获取dll1.dll中的导出函数GetCallCount
FARPROC pGetCallCount = GetProcAddress(hDll1, "GetCallCount");
// 如果获取导出函数失败,返回0
if (pGetCallCount == NULL)
{
return 0;
}
// 调用dll1.dll中的导出函数GetCallCount
int callCount = ((int(__stdcall*)(void))pGetCallCount)();
// 释放dll1.dll
FreeLibrary(hDll1);
return callCount;
}
- 在dll1.dll中使用延时加载
// dll1.cpp
#include <windows.h>
// 全局变量
__declspec(dllexport) int g_callCount = 0;
// 导出函数
__declspec(dllexport) int GetCallCount()
{
return g_callCount++;
}
// dllmain函数
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved)
{
switch (dwReason)
{
case DLL_PROCESS_ATTACH:
// 加载dll2.dll
LoadLibrary("dll2.dll");
break;
case DLL_PROCESS_DETACH:
// 释放dll2.dll
FreeLibrary(GetModuleHandle("dll2.dll"));
break;
}
return TRUE;
}
- 在main.cpp中使用初始化表
// main.cpp
#include <windows.h>
#include "dll1.h"
#include "dll2.h"
#pragma data_seg(".CRT$XIU")
__declspec(allocate(".CRT$XIU")) extern const PIMAGE_TLS_CALLBACK __xi_a[];
#pragma data_seg()
#pragma comment(linker, "/merge:.CRT=.rdata")
BOOL WINAPI XiCallback(PVOID hDllHandle, DWORD dwReason, PVOID lpReserved)
{
switch (dwReason)
{
case DLL_PROCESS_ATTACH:
// 加载dll2.dll
LoadLibrary("dll2.dll");
break;
case DLL_PROCESS_DETACH:
// 释放dll2.dll
FreeLibrary(GetModuleHandle("dll2.dll"));
break;
}
return TRUE;
}
int main()
{
// 加载dll1.dll
HMODULE hDll1 = LoadLibrary("dll1.dll");
if (hDll1 == NULL)
{
// 加载失败
return -1;
}
// 获取dll1.dll中的导出函数GetCallCount
FARPROC pGetCallCount = GetProcAddress(hDll1, "GetCallCount");
if (pGetCallCount == NULL)
{
// 获取导出函数失败
FreeLibrary(hDll1);
return -1;
}
// 调用dll1.dll中的导出函数GetCallCount
int callCount = ((int(__stdcall*)(void))pGetCallCount)();
// 释放dll1.dll
FreeLibrary(hDll1);
return 0;
}
总结
本文通过一个经典示例,详细剖析了DLL加载失败的根源,并提供了三种化解方案。希望对您解决DLL加载失败问题有所帮助。