1

We hit a case where it would be the best solution for us to put a FreeLibrary call into DllMain / DLL_PROCESS_DETACH.

Of course, you must not do that:

It is not safe to call FreeLibrary from DllMain.

The use case is that we have a situation like this:

(unknown client dll or exe) links dynamically or statically to -> 
      -> DLL_1, loads dynamically -> DLL_x

DLL_1 should load DLL_x transparently wrt. to it's client code, and it should to load DLL_x dynamically. Now, the loading can be done lazily, so that the LoadLibrary call needn't reside in the DLL_PROCESS_ATTACH part of DLL_1.

But once the client is done with DLL_1, when/before DLL_1 is unloaded from the process, it should also unload (== FreeLibrary) DLL_x.

Is there any way to do this without an explicit DLL_1/Uninitialize function that must be called by the client?

I'll note:

  • DllMain, and thus also any C++ global static destructor cannot be used.
  • Is there any other callback mechanism in either kernel32/ntdll or maybe in the shared MS CRT to make this happen?
  • Are there other patterns to make this usecase work?
Martin Ba
  • 37,187
  • 33
  • 183
  • 337
  • `It is not safe to call FreeLibrary from DllMain` - need understand **why** this. only *because this can result in a DLL being used after the system has executed its termination code.* - if you exactly know that you and nobody else not more use this DLL (after call `FreeLibrary`) this is safe and normal call `FreeLibrary` from DllMain – RbMm Jul 19 '17 at 13:06
  • @RbMm - no. It is unsafe because it is *documented to be* unsafe. The loader doesn't support it. I accept that it may work in quite some circumstances, but it still may blow up in some corner case. [For example](http://blogs.msdn.com/b/oleglv/archive/2003/10/28/56142.aspx). – Martin Ba Jul 20 '17 at 07:17
  • your example for `LoadLibrary`. here problem not in `LoadLibrary` call but in call function from just loaded DLL. `LoadLibrary` not guaranteed (*when it called recursive in loader path*) that DLL entry point will be called before `LoadLibrary` return. this is source of all errors - use DLL before it initialize itself. but situation with `FreeLibrary` is not simetric. the DLL uninitialization will be not called until last `FreeLibrary`. if you have handle to loaded DLL - you have 100% guarantee that it uninitialization code will be not called until you yourself call `FreeLibrary` explicit. – RbMm Jul 20 '17 at 09:06
  • even if you call `FreeLibrary` from loader path (`DllMain`) we can be sure that `DLL_X` yet not (and can not) be in unloading process, because it hold on your reference. so call any functions form dll before call `FreeLibrary` for this dll is safe. and i not view how call `FreeLibrary` even from `DllMain` can cause problem. if you can lead example of exactly this - will be interesting look on it. – RbMm Jul 20 '17 at 09:06
  • @RbMm - I do not doubt that it often works in practice. I will just quote [another answer](https://stackoverflow.com/a/4406638/321013): ""It's not that the loader lock does anything to stop DllMain from calling LoadLibrary or even that the loader lock itself makes such a call unsafe. It is instead that by retaining the loader lock, **NTDLL *trusts* DllMain not to call LoadLibrary.**"" – Martin Ba Jul 20 '17 at 10:11
  • even LoadLibrary is safe as separate call from DllMain. and some windows system binaries do this (say user32.dll load imm32.dll from dllmain). error is when called functions from dll before it initialized. but your question was about FreeLibrary. how i say situation here is not symmetric compare LoadLibrary. if you have concrete example or knowledge when FreeLibrary can cause problem is call it from DllMain - be interesting look on this. My interest here is solely in the deep understanding of what is happening – RbMm Jul 20 '17 at 10:27
  • Geoff Chappell is correct in own answer. problem with `LoadLibrary` in next - we usual assume that we can use (call functions from DLL) after loaded DLL. but this is not (always) true if we call `LoadLibrary` in DllMain. with FreeLibrary another situation - correct call functions from dll before FreeLibrary. it deinitialization begin only after last call to FreeLibrary. and i not view how call FreeLibrary form DllMain can cause problem. all posts about this - only reference to msdn - not do this because not do. but i not view any real example/explaniation how can this produce error – RbMm Jul 20 '17 at 10:52
  • I'll note that [mgrier has an excellent series about that stuff](https://blogs.msdn.microsoft.com/mgrier/) from back 2005. @RbMm - your comments are appreciated. I guess it's just a mess. "... this series should make you feel **very very nervous** indeed if you have code in your `DLL_PROCESS_ATTACH` that's doing much more than calling `InitializeCriticalSection()`. ..." :-) – Martin Ba Jul 20 '17 at 12:54
  • again here nothing about `FreeLibrary` exactly in `DllMain` no any example (like your first for call function from loaded but uninitialized dll) how concrete this can produce error. for me windows not black box, and i have very deep knowledge how loader work also. i not view base for nervous because understand what happens internal ) however for me really just interesting example of FreeLibrary call in DllMain which cause error. in this blog also nothing concrete. even visa versa - *n fact I can't recall a single instance of FreeLibrary() inducing a DLL_PROCESS_DETACH [basic] ordering issue.* – RbMm Jul 20 '17 at 13:07

1 Answers1

1

The correct approach is an explicit Uninitialize function in DLL_1.

However, if you can't do that, you can work around the problem by launching a helper thread to do the unload for you. If you want to play it safe, launch the thread at the same time you load DLL_x and have it wait on an event object. (For the record, though, it is generally considered safe to launch a thread from DllMain so long as you respect the fact that it won't start up until DllMain has exited.)

Obviously, the helper thread's code can't be in DLL_1. If you can modify DLL_x you can put it there. If not, you'll need a helper DLL. In either case, the DLL containing the helper thread's code can safely self-unload using the FreeLibraryAndExitThread function.

Harry Johnston
  • 35,639
  • 6
  • 68
  • 158
  • Neat. So I could write a helper-Dll, and *that* one would load DLL_x from a helper thread and then wait to be signalled. DLL_1 *could* then signal the helper-Dll from its `DllMain` (because that's allowed) and the helper thread in the helper-Dll would terminate itself (and if it's the last helper thread, the count drops to zero and the helper-Dll is unloaded). One could even general-purpose this :-) – Martin Ba Jul 20 '17 at 07:24