1

I've dynamically linked libhunspell.dll (HunSpell) to my application. It works, but there is a dumb problem which I don't know why it happens.

Even before I use LoadLibrary("path\\to\\libhunspell.dll"); to load it and use it, on the start of the application it attempts to load the library by itself. If I place the libhunspell.dll into the path where my main executable resides, it can load it, otherwise it reports an error, immediately after starting the application - This application has failed to start because LIBHUNSPELL.DLL was not found. Re-installing the application may fix this problem. and the application doesn't start.

I would understand if the LoadLibrary would use invalid path but this happens as soon as the executable runs, even before the first statement in WINAPI _tWinMain(HINSTANCE, HINSTANCE, LPTSTR, int) executes (I've tried to place a breakpoint and it doesn't even reach it, so this happens before).

So, as a result, I must place libhunspell.dll in the same folder as my application executable, and not in the path I want.

This is probably easy to fix although I don't what to look for.

So the question is - how do I avoid it loading it immediately and have it wait until I use LoadLibrary call?

Here is how I linked if it can help:

1) compiled libhunspell.dll in Visual Studio 2015 (I used /MT option to link it statically so it doesn't have VC++ Redistributable as a dependency).

2) created import library (libhunspell.lib) using implib.exe -a -c -f libhunspell.lib libhunspell.dll

3) linked that to the source .cpp unit which is using it using #pragma comment(lib, "libhunspell.lib") (it is RAD Studio 2010 so the .lib is required unlike newer versions).

4) later in the same .cpp used LoadLibrary to load this library and used it.

Coder12345
  • 3,431
  • 3
  • 33
  • 73
  • 1
    You are going to have to do this by **not** using steps 2 and 3. That is going to produce linker errors, tells you what code you need to fix. No doubt painful, so go for the simple solution. – Hans Passant Aug 31 '18 at 22:43
  • 1
    Obviously you are loading the DLL "statically" too. That is what @HansPassant already said: you linked to the import lib. Don't do that if you want to load the DLL dynamically. – Rudy Velthuis Sep 01 '18 at 14:58
  • 1
    FWIW, for "static" loading of a DLL, the DLL must be found by the system. This means it must either be in the same directory as the calling application, or it must be on the system path (is same as PATH environment variable). See Microsoft's [Dynamic-Link Library Search Order](https://learn.microsoft.com/en-us/windows/desktop/dlls/dynamic-link-library-search-order) documentation. – Rudy Velthuis Sep 01 '18 at 15:01
  • I guess my real question is then - Is there a way to still use .lib (to avoid using `GetProcAddress` which has bugs) but specify the DLL path later in the program (not at compile time nor in the same directory as main executable or somewhere along the PATH)? It did work if I used delay-loading and `LoadLibrary` with my own path later. If what @RudyVelthuis states is the only option, then there are not many options to customize the DLL loading path except modifying system PATH (or from the same path as main exe). – Coder12345 Sep 01 '18 at 17:42
  • 1
    If GetProcAddress had bugs then many many people would have big problems. Anyway, you can use the import lib, but that is not as flexible as dynamic linking (LoadLibrary + GetProcAddress). But with "static" linking, your DLL must be in one of the locations MS tells you about. Delay-loading should not be coupled with dynamic loading. – Rudy Velthuis Sep 01 '18 at 17:53

2 Answers2

4

By linking in the import stubs (libhunspell.lib) the OS will load the DLL for you as it is now a static dependency.

One approach would be specify the library as a delayload dependency: /DELAYLOAD:libhunspell.lib via the linker options. You can then call LoadLibrary on the DLL.

The only other option is to stop including the .lib in the linker step, making it truly a dynamic dependency.

josh poley
  • 7,236
  • 1
  • 25
  • 25
  • By truly a dynamic dependency you mean using the `GetProcAddress` instead of the import lib? – Coder12345 Aug 31 '18 at 20:28
  • 1
    @Coder12345, correct, `LoadLibrary` + `GetProcAddress` is the standard way to do that (COM is another, but probably not what you are looking for here). – josh poley Aug 31 '18 at 22:41
  • 2
    If you use the linker's delay-load feature, DON'T call `LoadLibrary()` and `GetProcAddress()` manually. They will be called for you automatically by the delay-load framework when the DLL function(s) are called for the first time at runtime. Simply call the DLL function(s) normally AS-IF you were using static linking. The linker will hook up the necessary stubs to invoke delay loading. If you want to call `LoadLibrary()` and `GetProcAddress()` manually, DON'T use the delay-load feature. – Remy Lebeau Sep 01 '18 at 04:43
  • @RemyLebeau So it is not possible to customize the DLL loading path? I actually did that - .lib + delay-load + `LoadLibrary` with my own path to DLL (but not `GetProcAddress`). And that worked and loaded from my own path and when I wanted using `LoadLibrary`. So what I really want is to to set my own DLL loading path (as opposed to it being main exe directory), I don't really care if it is loaded automatically or delayed as long as it loads from my own path. – Coder12345 Sep 01 '18 at 17:55
  • 2
    @Coder12345 yes, it is possible to customize the loading path when using the delay-loader, via the [`__pfnDliNotifyHook`](http://docwiki.embarcadero.com/RADStudio/en/PfnDliNotifyHook,_pfnDliFailureHook) callback. When it triggers a `dliNotePreLoadLibrary` event, you can call `LoadLibrary()` yourself with whatever path you want. Or, using the `__pfnDliFailureHook` callback, when it triggers a `dliFailLoadLibrary` event, you can call `LoadLibrary()` with a custom path if the DLL could not be found on the standard search path. – Remy Lebeau Sep 01 '18 at 18:38
  • 2
    @Coder12345 speaking of which, you can alternatively tweak the standard search path to include your custom path by using [`SetDllDirectory()`](https://learn.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-setdlldirectoryw) or [`AddDllDirectory()`](https://learn.microsoft.com/en-us/windows/desktop/api/libloaderapi/nf-libloaderapi-adddlldirectory), then you don't need to call `LoadLibrary()` manually at all. – Remy Lebeau Sep 01 '18 at 18:40
1

I assume you did Add to project a *.lib file for your DLL. That is a kind of "static" linkage done in the App initialization (prior to your forms are created). So it has two disadvantages.

  1. You DLL must be in the same path as the Apps EXE file
  2. Sometimes DLL file name is locked (can not be changed)

The advantage is that you do not need to do any coding for the DLL loading as the VCL do it for you ... so your app should not contain the LoadLibrary,GetProcAddress calls you just include the *.h file with propper import declarations ...

For dynamic linkage you need to remove the *.lib from your project and use WinAPI LoadLibrary + GetProcAddress for loading your DLL as josh poley suggested. Here an example:

Beware there was/(is?) a bug in the GetProcAddress preventing from loading all the functions from your DLL in some cases. Especially if the DLL has old legacy mangling of names the count of functions is high and the DLL was created on compiler incompatible with the mangling in question.

Spektre
  • 49,595
  • 11
  • 110
  • 380
  • 1
    "*You **DLL** must be in the same path as the Apps **EXE** file*" - more accurately, it needs to be somewhere on the OS's DLL search path, which includes the EXE's folder as the first location that is searched. – Remy Lebeau Sep 04 '18 at 22:34