如何在MinGW(-w64)中捕获延迟导入dll错误(缺少dll或符号)?

Joe*_*Joe 5 c c++ mingw mingw-w64 dlltool

使用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
Run Code Online (Sandbox Code Playgroud)

如果我没记错的话,在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()); }
}
Run Code Online (Sandbox Code Playgroud)

但是,我不确定此代码的行为是否定义正确(毕竟我正在跳出处理程序)。而且看起来也不是特别干净。

编辑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());
    }
}
Run Code Online (Sandbox Code Playgroud)

此方法失败,因为异常未正确传播,并导致0x20474343SEH异常。似乎有一个相关的GCC错误应予以修复,但至少使用MinGW-w64 g ++ 4.9.2仍会失败(并且这是最新的可用版本)