Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

possible to handle missing dll file gracefully in Delphi application?

Tags:

dll

delphi

Anyone know if its possible to gracefully test for and handle a missing .dll file in a Delphi application? For example, my code has this function declaration:

function KFUNC(Arg1, Arg2, Arg3, Arg4: DWord): longint stdcall; external 'KL2DLL32.DLL' name '_KFUNC@16';

...which of course requires the dll file KL2DLL32.DLL to be found on the system, otherwise my application wont start. Im wondering if theres some different way to code this, so my application could test for the dll file's existence, and then handle accordingly. Obviously, the goal would be have my application still start normally even if the dll file isn't there. Thanks.

like image 364
chiswilson Avatar asked Sep 28 '15 16:09

chiswilson


2 Answers

Your import results in the function being linked using what is known as load-time or implicit linking. That is the executable contains meta data that tells the OS loader to load the DLL, and then bind to the functions that you named. If this load-time linking process fails, then the executable cannot be loaded.

You have a couple of options to avoid load-time linking, and thereby allow your program to be resilient to linking failures.

Delay-loading the DLL

Add the delayed directive to your function import. The documentation says:

To postpone the loading of the library that contains the function to the moment the function is actually needed, append the delayed directive to the imported function:

function ExternalMethod(const SomeString: PChar): Integer; stdcall; 
  external 'cstyle.dll' delayed;

delayed ensures that the library that contains the imported function is not loaded at application startup, but rather when the first call to the function is made.

The documentation contains other useful topics that go into more detail, and cover how to handle errors:

  • http://docwiki.embarcadero.com/RADStudio/en/Libraries_and_Packages#Delayed_Loading
  • http://docwiki.embarcadero.com/CodeExamples/en/DelayedLoading_(Delphi)

Explicit loading and binding to the DLL

The delayed directive is merely a concise way to get the compiler to arrange explicit loading of your DLL. You can do the same yourself manually using LoadLibrary and GetProcAddress.

  1. Call LoadLibrary to load the DLL. Either supply a full path to the DLL, or just its name. In the latter case you rely on the DLL search order to locate the DLL. The call to LoadLibrary yields a module handle.
  2. Call GetProcAddress to obtain the address of a named function pointer. You must supply the module handle from step 1.
  3. Call the function pointer returned from step 2.
  4. When you no longer need to call the function, use FreeLibrary to unload the DLL.

At each step of the way you must check the function return values in case of error. How to handle errors is documented for each Win32 API function in the MSDN documentation (as linked to above). For example, if the DLL cannot be found, then LoadLibrary returns 0. You must detect that and deal with the consequences accordingly.

Discussion

Although the delayed directive is very convenient, I personally have never used it. In my experience, whenever I have needed to link explicitly I've always found that I needed some extra flexibility that is not offered by delayed. Perhaps my needs are special, but do not be surprised if you find yourself leaning towards explicit calls to LoadLibrary and GetProcAddress.

As an example, just today I found myself using LoadLibraryEx because I wanted to pass the LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR flag. That sort of fine grained control is not available when you use delayed.

like image 180
David Heffernan Avatar answered Oct 14 '22 07:10

David Heffernan


When the function is declared like that, there's nothing your program can do about a missing DLL. The OS attempts to resolve the imported DLL function before any bit of your program's code begins to execute, so there's no code you can write that would do anything about it.

As of Delphi 2010, though, you can change the function's declaration to use the new delayed loading feature. Add the delayed directive to the end of the declaration:

function KFUNC(Arg1, Arg2, Arg3, Arg4: DWord): longint stdcall;
  external 'KL2DLL32.DLL' name '_KFUNC@16' delayed;

If you're using an older Delphi version, then your only option is to load the DLL and the function at run time, and then handle the errors.

Another benefit of using delayed is that there are SetDliNotifyHook2 and SetDliFailureHook2 functions that allow you to assign hooks so you can handle run-time load notifications and failures, respectively.
So, if a given DLL, or even a given function, is not found at runtime, you can log an error, even substitute it with another DLL handle or function pointer to satisfy the load.

Both options are discussed in more detail in another question centering on using a DLL only when required.

like image 23
Rob Kennedy Avatar answered Oct 14 '22 05:10

Rob Kennedy