3

I am compiling a program for windows.

I want it to check if foo.dll exists in the system, and if not, to print an error message and exit. Is it safe to do it like this:

  1. pass the /DELAYLOAD foo.dll flag to the linker;
  2. at the very beginning of the main(), manually call auto handle = LoadLibraryA("foo.dll") and check if handle is not NULL;
  3. if it's not NULL, continue to work;
  4. at the end of the main(), call FreeLibrary(handle)?

I am wondering if something will break due to mixing delayed loading and manually calling LoadLibraryA(). Also, if someone could suggest a simpler or more correct way to do what I want, I would appreciate it.

parean
  • 305
  • 3
  • 13
  • really you can handle error while resolving delay load api(s). if you use standard `__delayLoadHelper2` (from *delayimp.lib*) - you can implement `__pfnDliFailureHook2` or handle `VcppException(ERROR_SEVERITY_ERROR, ERROR_MOD_NOT_FOUND)` exception. but if you want stop work if *foo.dll* not found - why not normal link to it, without delayload ? you program simply not start and show error - *foo.dll* not found. use delayload have sense if you want work even in case *foo.dll* not present (or not export all api), but you somehow handle this case – RbMm Oct 12 '19 at 16:40
  • 2
    If you simply need to check for the existence and version info for a DLL, call `LoadLibraryExW(L"foo.dll", NULL, LOAD_LIBRARY_AS_DATAFILE | LOAD_LIBRARY_AS_IMAGE_RESOURCE)`. If it's already mapped as a module, it increases the reference count and returns a reference to it. Otherwise it maps the DLL as a data section. – Eryk Sun Oct 12 '19 at 16:41
  • @RbMm, delay loading can give a program the chance to modify the default loader search path via `SetDefaultDllDirectories` and `AddDllDirectory`, which improves security by omitting `PATH` and the working directory from the DLL search and by giving an application complete control over where to look for its dependencies. SxS activation contexts can achieve the same goal and more, but they're a good deal more complicated to configure. – Eryk Sun Oct 12 '19 at 16:44
  • @ErykSun yes, also if some dll rarely, not always used in normal execution path, we can want load it only when it was really need, but not unconditionally just. or if some api exist only in latest version of windows can handle this via delayload, etc. but if want just stop run application at very begin, if dll not found- more logical already use just import without delay. of course if not want do some job in case dll not exist – RbMm Oct 12 '19 at 16:50
  • @RbMm Maybe OP wants to have conditional code: one block that executes if the DLL can be loaded, one to run if it can't load. Your 'somehow handle this case' is exactly what the OP is trying to establish, I believe. – Adrian Mole Oct 12 '19 at 17:09
  • also as variant use [`ResolveDelayLoadsFromDll`](https://learn.microsoft.com/en-us/windows/win32/devnotes/resolvedelayloadsfromdll) but it was exist only from win 8.1 (or 8 ?) – RbMm Oct 12 '19 at 17:26
  • @RbMm Yes, in order to simply inform that there is no necessary dll, the normal link is enough, I understand that. But I want to display a more detailed error message and suggest launching the program with other parameters in restricted mode. – parean Oct 13 '19 at 13:57
  • @Oroffe - if you target windows 8.1+ (or may be 8 - i not check from which version exist `ResolveDelayLoadsFromDll`) you can use `ResolveDelayLoadsFromDll` api for resolve all import from it or show error. or you can handle errors during delayload and show error in this place and exit. of course also easy can write `ResolveDelayLoadsFromDll` yourself – RbMm Oct 13 '19 at 14:00

1 Answers1

1

If you are going to do this, you should call FreeLibrary() immediately after your 'test' call to LoadLibrary() - but certainly before you cause the auto-load by calling one of its routines! (That is, of course, assuming the call succeeded!) That way, there should be no problem with load clashes. (You could also make further tests, such as checking that all required routines are present, with calls to GetProcAddress().

Once you have verified that the DLL is present (and, obviously, loadable), you would then continue execution - the DLL will be loaded 'automatically' when your executable first calls one of its exported functions.

If you have the DLL already loaded (manually), then it will try to load itself twice into the same process (I think). This would cause problems, at some point, for sure.

PS: It's a good way to do the check, IMHO!! Please, do let us know how it fares.

EDIT: Following the (somewhat extensive) discussion in the comments, it is now clear to me that it is unnecessary to call FreeLibrary() - either when I suggested or, indeed, at the end of the program (as it will be unloaded then, anyway). But it's still a good solution!

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
  • *you should call FreeLibrary() immediately after your 'test' call* - why ? more logic really already not call `FreeLibrary` at all. *If you have the DLL already loaded (manually), then it will try to load itself twice into the same process* - this is wrong of course – RbMm Oct 12 '19 at 16:33
  • @RbMm What's wrong with calling `FreeLibrary()` after the 'test' has succeeded? Maybe, as Eryk says, it's not going to load twice if you don't, but isn't it good practice to "clean up" after your probe operations? (Note: I did write **I think** in my post!) – Adrian Mole Oct 12 '19 at 17:07
  • @Adrian call `FreeLibrary` not wrong (by self) but if we anyway plan use this library - for what unload it and then load again ? more logical not unload it already – RbMm Oct 12 '19 at 17:12
  • and because inside delayload (usually, always if we use standard handler) used dll name only (without path) - we got handle of already loaded *foo.dll* – RbMm Oct 12 '19 at 17:14
  • @RbMm You will **have** to load the library again through 'automatic load' if user wants to call functions exported/imported through the import library! Just using the `LoadLibrary()` call won't load the references to any (implicit) `__declspec(dllimport)` functions. – Adrian Mole Oct 12 '19 at 17:15
  • 1
    @Adrian - you mistake. not need any **again** load. *foo.dll* will be loaded **once**. and after we load it in begin of main - not need unload/re load it. you confuse load dll and resolve import from it – RbMm Oct 12 '19 at 17:17
  • @ErykSun I'm not saying you're wrong, but the documentation says the reference count is increased for "each call to LoadLibrary." How does this work for 'implicit' loads mingled with explicit LoadLibrary calls (especially when the implicit call comes second)? (I don't know!) See: https://stackoverflow.com/questions/3497516/does-loadlibrary-create-distinct-instances – Adrian Mole Oct 12 '19 at 18:05
  • @Adrian what is different between implicit and explicit loads ? anyway in both case `LdrLoadDll` internally called and reference count is increased if dll already loaded – RbMm Oct 12 '19 at 18:29
  • all will be ok here (if do not consider the rare and always difficult case when there are several versions of the *foo.dl*) we can once load it at behin and all. next call to `LoadLibrary` or it internals (like `LdrLoadDll`) simply increment dll reference count and return the base address of already loaded dll – RbMm Oct 12 '19 at 18:51
  • @RbMm I think you're probably right, but it's tricky! The comments by Remy Lebeau in this post are good, and satisfy my concerns about resolving the import functions: https://stackoverflow.com/questions/32486699/when-do-these-load-dlls-implicit-linking-vs-explicit-linking – Adrian Mole Oct 12 '19 at 19:07
  • @RbMm The key (which I didn't understand, and nobody here mentioned) was the special table of "STUB" pointers in the PE of delay-loaded executables. – Adrian Mole Oct 12 '19 at 19:09
  • 2
    @Adrian - exist 2 different things - load dll to memory (first step) and resolve import functions from this dll. i initial comment was about - if we already load *foo.dll* to memory - no sense unload it and load again. only this. however even if *foo.dll* exist may be some needed api it not exported (old version ? another dll) possible solution here not simply load dll but resolve all imports from it too at begin. in win 8+ we can use here [`ResolveDelayLoadsFromDll`](https://learn.microsoft.com/en-us/windows/win32/devnotes/resolvedelayloadsfromdll) api. – RbMm Oct 12 '19 at 19:21
  • After reading the answer and comments to it, as well as following the links from them, I realized that when mixing a delayed link and an explicit function call, nothing bad will happen. Thank you all very much! – parean Oct 13 '19 at 14:19
  • @Oroffe So, although I admit that my answer was 'wrong' - was it helpful? – Adrian Mole Oct 13 '19 at 17:42