Capturing and gracefully handling delay-loaded DLL errors.

By: Vincent Drake

Abstract: Demonstrates creating a hook function to handle error messages caused by delay-load failures.


Question:

How do I capture error messages when a delay loaded DLL fails to load? As is, my program gives me a very unfriendly External Exception error. Placing a try/catch block around the function call does not capture the exception.

 
Answer:

C++Builder provides a mechanism for hooking error messages when a delay load dll fails. The subject is covered in the C++Builder documentation (under "delay load, notify hooks") and the MSDN (http://msdn.microsoft.com/library/en-us/vccore/html/vcconLinkerSupportForDelayedLoadingOfDLLs.asp?frame=true).

The basic ideas is that you overwrite a global function pointer with the address of your own error handling function. Within this handler, you have complete control over how the error propogates. With the right code, you could load another DLL and return a pointer to one of its functions, in effect, completely recovering from the error. In the example implementation below, I simply throw a VCL exception containing a more friendly error message.

Now the details. All of the structure and function definitions needed are declared in "delayimp.h", which you naturally must #include. The function pointer we'll be overwriting is declared as this:

extern DelayedLoadHook _EXPDATA __pfnDliFailureHook;

where DelayedLoadHook is defined as:

typedef FARPROC (WINAPI *DelayedLoadHook)( dliNotification dliNotify, DelayLoadInfo * pdli);

To properly overwrite this function pointer, we must properly declare our function to match this pointer's type:

FARPROC WINAPI MyLoadFailureHook(dliNotification dliNotify, DelayLoadInfo * pdli);

Then implment the function as you please. This example just throws an exception that your C++Builder code can catch:

FARPROC WINAPI MyLoadFailureHook(dliNotification dliNotify,DelayLoadInfo * pdli) { AnsiString errorstring = "Delay Load Errorn"; if ( dliNotify == dliFailLoadLibrary) errorstring += AnsiString("Unable to load library: ") + pdli->szDll; else if (dliNotify == dliFailGetProcAddress) errorstring += AnsiString("Unable to find procedure: ") + pdli->dlp.szProcName + " in " + pdli->szDll; throw Exception(errorstring); }

Somewhere in your applicaiton code, you must overwrite the hook pointer so that it works with your function instead of the default. In this example it's done in a button click where 'DoSomething' is the function inside the delay loaded DLL we're calling:

void __fastcall TForm1::Button1Click(TObject *Sender) { DelayedLoadHook delayold = __pfnDliFailureHook; __pfnDliFailureHook = MyLoadHook; DoSomething(StrToInt(Edit1->Text)); __pfnDliFailureHook = delayold; }

With the example code, a failure to load the DLL or call DoSomething will result in a VCL exception propgating up to the main try/catch block. You can, of course, place a try/catch block around the call to DoSomething if you'd like to catch it sooner.

 

Server Response from: ETNASC04