错误处理和通知

如果程序使用延迟加载的 DLL,则它必须可靠地处理错误,因为运行程序时发生的故障会导致未经处理的异常。 故障处理由两部分组成:通过挂钩进行恢复,以及通过异常进行报告。

有关 DLL 延迟加载错误处理和通知的详细信息,请参阅了解帮助程序函数

有关挂钩函数的详细信息,请参阅结构和常量定义

通过挂钩进行恢复

代码可能需要在出现故障时进行恢复,或者需要提供备用库或例程。 你可以为帮助程序函数提供一个可以提供替代代码的挂钩,或纠正这种情况。 挂钩例程需要返回合适的值以便处理可以继续进行(HINSTANCEFARPROC)。 否则它可能会返回 0,以指示应引发异常。 它还可能会引发其自身的异常或 longjmp 出挂钩。 有通知挂钩和失败挂钩。 同一例程可用于两者。

通知挂钩

仅在帮助程序例程中执行以下操作之前调用延迟加载通知挂钩:

  • 检查库的存储句柄,看它是否已加载。

  • 调用 LoadLibrary 即可尝试加载 DLL。

  • 调用 GetProcAddress 即可尝试获取过程的地址。

  • 返回到延迟导入加载 thunk。

通过以下方式启用通知挂钩:

  • 提供指针 __pfnDliNotifyHook2 的新定义,该指针已初始化为指向你自己的用于接收通知的函数。

    - 或者 -

  • 先设置指向挂钩函数的指针 __pfnDliNotifyHook2,然后再调用程序正对其进行延迟加载的 DLL。

如果通知是 dliStartProcessing,挂钩函数可能返回:

  • NULL

    默认帮助程序处理 DLL 的加载。 可以仅出于信息性目的进行调用。

  • 函数指针

    绕过默认延迟加载处理。 它允许你提供自己的负载处理程序。

如果通知是 dliNotePreLoadLibrary,挂钩函数可能返回:

  • 0(如果它只是想要信息性通知)。

  • 已加载的 DLL 的 HMODULE(如果它加载了 DLL 本身)。

如果通知是 dliNotePreGetProcAddress,挂钩函数可能返回:

  • 0(如果它只是想要信息性通知)。

  • 导入的函数的地址(如果挂钩函数获取地址本身)。

如果通知是 dliNoteEndProcessing,则忽略挂钩函数的返回值。

如果此指针已初始化(非零),则延迟加载帮助程序会在执行过程中的特定通知点调用此函数。 函数指针有以下定义:

// The "notify hook" gets called for every call to the
// delay load helper.  This allows a user to hook every call and
// skip the delay load helper entirely.
//
// dliNotify == {
//  dliStartProcessing |
//  dliNotePreLoadLibrary  |
//  dliNotePreGetProc |
//  dliNoteEndProcessing}
//  on this call.
//
ExternC
PfnDliHook   __pfnDliNotifyHook2;

// This is the failure hook, dliNotify = {dliFailLoadLib|dliFailGetProc}
ExternC
PfnDliHook   __pfnDliFailureHook2;

通知将 DelayLoadInfo 结构和通知值传递到挂钩函数。 此数据与延迟加载帮助程序例程使用的数据相同。 通知值将是结构和常量定义中定义的值之一。

失败挂钩

失败挂钩的启用方式与通知挂钩相同。 挂钩例程需要返回合适的值以便处理可以继续进行(HINSTANCEFARPROC),或返回 0 来指示应引发异常。

引用用户定义函数的指针变量为:

// This is the failure hook, dliNotify = {dliFailLoadLib|dliFailGetProc}
ExternC
PfnDliHook   __pfnDliFailureHook2;

DelayLoadInfo 结构包含详细报告此错误所需的所有相关数据,包括来自 GetLastError 的值。

如果通知是 dliFailLoadLib,挂钩函数可能返回:

  • 0(如果它无法处理失败)。

  • HMODULE(如果失败挂钩修复了问题并加载了库本身)。

如果通知是 dliFailGetProc,挂钩函数可能返回:

  • 0(如果它无法处理失败)。

  • 有效的过程地址(导入函数地址),前提是失败挂钩成功获取地址本身。

使用异常进行报告

如果处理错误时只需中止该过程,则只要用户代码可以处理异常,就不需要任何挂钩。

延迟加载异常代码

在延迟加载期间发生故障时,可能会引发结构化异常代码。 异常值是使用 VcppException 宏指定的:

//
// Exception information
//
#define FACILITY_VISUALCPP  ((LONG)0x6d)
#define VcppException(sev,err)  ((sev) | (FACILITY_VISUALCPP<<16) | err)

对于 LoadLibrary 失败,将引发标准 VcppException(ERROR_SEVERITY_ERROR, ERROR_MOD_NOT_FOUND)。 对于 GetProcAddress 失败,引发的错误为 VcppException(ERROR_SEVERITY_ERROR, ERROR_PROC_NOT_FOUND)。 异常会传递 DelayLoadInfo 结构的指针。 在 ExceptionInformation[0] 字段中,它位于由 GetExceptionInformationEXCEPTION_RECORD 结构检索的 LPDWORD 值中。

如果在 grAttrs 字段中设置了不正确的位,则会引发 ERROR_INVALID_PARAMETER 异常。 对于所有意向和目的,此异常都是致命的。

有关详细信息,请参阅结构和常量定义

另请参阅

链接器对延迟加载 DLL 的支持