使用dlltool -y
它可以为现有的 .dll 或 .def 文件创建延迟导入库。这似乎工作得很好,直到在没有相应 dll 的系统上需要 dll (正如延迟导入/加载的 dll 所预期的那样)。但是,我找不到有关如何捕获延迟加载期间生成的错误(缺少模块或缺少函数)的任何信息。
在 MSVC 上,您将使用__try {} __except (...) {}
SEH 异常处理,但是,这在 MinGW 上不可用(我也不知道 dlltool 使用哪种异常机制)。
常规try {} catch(...) {}
也不起作用(应用程序崩溃的方式与没有任何异常处理的方式相同)。
GDB 输出也不是特别有用:
gdb: unknown target exception 0xc06d007e at 0x7fefccfaaad
Program received signal ?, Unknown signal.
0x000007fefccfaaad in RaiseException ()
from C:\Windows\system32\KernelBase.dll
如果我没记错的话,RaiseException 中发生的未知异常似乎表明了 SEH 异常。
因此,问题是,有没有人成功处理 MinGW-w64 中的延迟加载以及如何处理?
编辑:经过一些实验,我想出了以下解决方案:
extern "C" __declspec(dllexport) void foo(int);
#include <windows.h>
#include <csetjmp>
#include <stdexcept>
#include <memory>
#include <cstdio>
thread_local auto do_handler = true;
thread_local jmp_buf env;
LONG CALLBACK handler(PEXCEPTION_POINTERS e)
{
if(do_handler)
{
// this flag is necessary to prevent a recursive call to handler
do_handler = false;
longjmp(env, 1);
}
else
{
return EXCEPTION_CONTINUE_SEARCH;
}
}
struct veh_remover
{
void operator() (void * veh) const
{
RemoveVectoredExceptionHandler(veh);
do_handler = true;
}
};
int main(int, char**)
{
#define CHECKED_DELAY(fn, ...) \
do { \
auto h = std::unique_ptr<void, veh_remover>(AddVectoredExceptionHandler(1, handler)); \
if(!setjmp(env)) fn(__VA_ARGS__); \
else throw std::runtime_error(#fn " not available"); \
} while(0);
try { CHECKED_DELAY(foo, 0); }
catch(std::exception & e) { printf("%s\n", e.what()); }
}
但是,我不确定这段代码的行为是否定义明确(毕竟我是从处理程序中跳出来的)。而且看起来也不是特别干净。
编辑2:我尝试了一种不同的方法,设置__pfnDliFailureHook2
:
extern "C" __declspec(dllimport) void foo(int);
#include <windows.h>
#include <csetjmp>
#include <stdexcept>
#include <memory>
#include <cstdio>
#include <cassert>
#include <delayimp.h>
FARPROC WINAPI delayHook(unsigned dliNotify, PDelayLoadInfo)
{
switch(dliNotify)
{
case dliFailLoadLib: throw std::runtime_error("module could not be loaded");
case dliFailGetProc: throw std::runtime_error("could not find procedure in module");
default: return 0;
};
}
int main(int, char**)
{
__pfnDliFailureHook2 = &delayHook;
try
{
foo(0);
}
catch(std::exception & e)
{
printf("%s\n", e.what());
}
}
此方法失败,因为异常没有正确传播并导致0x20474343
SEH 异常。似乎有一个相关的 GCC 错误应该修复,但至少使用 MinGW-w64 g++ 4.9.2 这仍然失败(这是可用的最新版本)