错误处理和通知
如果程序使用延迟加载的 DLL,则它必须可靠地处理错误,因为运行程序时发生的故障会导致未经处理的异常。 故障处理由两部分组成:通过挂钩进行恢复,以及通过异常进行报告。
有关 DLL 延迟加载错误处理和通知的详细信息,请参阅了解帮助程序函数。
有关挂钩函数的详细信息,请参阅结构和常量定义。
通过挂钩进行恢复
代码可能需要在出现故障时进行恢复,或者需要提供备用库或例程。 你可以为帮助程序函数提供一个可以提供替代代码的挂钩,或纠正这种情况。 挂钩例程需要返回合适的值以便处理可以继续进行(HINSTANCE
或 FARPROC
)。 否则它可能会返回 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
结构和通知值传递到挂钩函数。 此数据与延迟加载帮助程序例程使用的数据相同。 通知值将是结构和常量定义中定义的值之一。
失败挂钩
失败挂钩的启用方式与通知挂钩相同。 挂钩例程需要返回合适的值以便处理可以继续进行(HINSTANCE
或 FARPROC
),或返回 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]
字段中,它位于由 GetExceptionInformation
从 EXCEPTION_RECORD
结构检索的 LPDWORD
值中。
如果在 grAttrs
字段中设置了不正确的位,则会引发 ERROR_INVALID_PARAMETER
异常。 对于所有意向和目的,此异常都是致命的。
有关详细信息,请参阅结构和常量定义。