Error handling and notification

If your program uses delay-loaded DLLs, it must handle errors robustly, since failures that occur while the program is running will result in unhandled exceptions. Failure handling is composed of two portions: Recovery through a hook, and reporting via an exception.

For more information on DLL delay load error handling and notification, see Understand the helper function.

For more information on hook functions, see Structure and constant definitions.

Recovery through a hook

Your code may need to recover on a failure, or to provide an alternate library or routine. You can provide a hook to the helper function that can supply the alternative code, or remedy the situation. The hook routine needs to return a suitable value, so that processing can continue (an HINSTANCE or FARPROC). Or, it can return 0 to indicate that an exception should be thrown. It could also throw its own exception or longjmp out of the hook. There are notification hooks and failure hooks. The same routine may be used for both.

Notification hooks

The delay load notification hooks are called just before the following actions are taken in the helper routine:

  • The stored handle to the library is checked to see if it has already been loaded.

  • LoadLibrary is called to attempt the load of the DLL.

  • GetProcAddress is called to attempt to get the address of the procedure.

  • Return to the delay import load thunk.

The notification hook is enabled:

  • By supplying a new definition of the pointer __pfnDliNotifyHook2 that's initialized to point to your own function that receives the notifications.

    -or-

  • By setting the pointer __pfnDliNotifyHook2 to your hook function before any calls to the DLL that the program is delay loading.

If the notification is dliStartProcessing, the hook function can return:

  • NULL

    The default helper handles the loading of the DLL. It's useful to call just for informational purposes.

  • a function pointer

    Bypass the default delay-load handling. It lets you supply your own load handler.

If the notification is dliNotePreLoadLibrary, the hook function can return:

  • 0, if it just wants informational notifications.

  • The HMODULE for the loaded DLL, if it loaded the DLL itself.

If the notification is dliNotePreGetProcAddress, the hook function can return:

  • 0, if it just wants informational notifications.

  • The imported function's address, if the hook function gets the address itself.

If the notification is dliNoteEndProcessing, the hook function's return value is ignored.

If this pointer is initialized (nonzero), the delay load helper invokes the function at certain notification points throughout its execution. The function pointer has the following definition:

// 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;

The notifications pass in a DelayLoadInfo structure to the hook function along with the notification value. This data is identical to the data used by the delay load helper routine. The notification value will be one of the values defined in Structure and constant definitions.

Failure hooks

The failure hook is enabled in the same manner as the notification hook. The hook routine needs to return a suitable value so that processing can continue (an HINSTANCE or FARPROC), or 0 to indicate that an exception should be thrown.

The pointer variable that refers to the user-defined function is:

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

The DelayLoadInfo structure contains all the pertinent data necessary for detailed reporting of the error, including the value from GetLastError.

If the notification is dliFailLoadLib, the hook function can return:

  • 0, if it can't handle the failure.

  • An HMODULE, if the failure hook fixed the problem and loaded the library itself.

If the notification is dliFailGetProc, the hook function can return:

  • 0, if it can't handle the failure.

  • A valid proc address (import function address), if the failure hook succeeded in getting the address itself.

Report by using an exception

If all that's required to handle the error is to abort the procedure, no hook is necessary, as long as the user code can handle the exception.

Delay load exception codes

Structured exception codes can be raised when failures occur during a delayed load. The exception values are specified by using a VcppException macro:

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

For a LoadLibrary failure, the standard VcppException(ERROR_SEVERITY_ERROR, ERROR_MOD_NOT_FOUND) is thrown. For a GetProcAddress failure, the error thrown is VcppException(ERROR_SEVERITY_ERROR, ERROR_PROC_NOT_FOUND). The exception passes a pointer to a DelayLoadInfo structure. It's in the LPDWORD value retrieved by GetExceptionInformation from the EXCEPTION_RECORD structure, in the ExceptionInformation[0] field.

If the incorrect bits are set in the grAttrs field, the exception ERROR_INVALID_PARAMETER is thrown. This exception is, for all intents and purposes, fatal.

For more information, see Structure and constant definitions.

See also

Linker support for delay-loaded DLLs