The solution is to bypass widget cleanup if the DLL_ PROCESS_ DETACH handler realizes that the process is terminating. But during process shutdown, the module loader needs to free all the things, and the outstanding LoadLibrary won’t prevent that from happening. If CONTOSO.DLL unloads via a runtime call to FreeLibrary, it will still be able to call into WIDGET.DLL because it hasn’t yet called FreeLibrary on WIDGET.DLL. Note that this problem occurs only at process shutdown. The loader sees no static dependency between CONTOSO.DLL and WIDGET.DLL, so the order in which they uninitialize is arbitrary.Īnd if the arbitrary decision ends up selecting WIDGET.DLL to uninitialize first, then you have a crash when CONTOSO.DLL tries to call into an already-uninitialized DLL. WIDGET.DLL was a dynamic dependency, the module loader doesn’t take it into account when calculating the order in which modules should be uninitlalized. In the CONTOSO.DLL module’s DLL_ PROCESS_ DETACH code, it checks² if there is a cached widget, and if so, destroys it. What happened is that CONTOSO.DLL initialized without WIDGET.DLL, and then later somebody needed a widget, so it loaded WIDGET.DLL and did some widget stuff, and then cached the widget so it wouldn’t have to go through all that nonsense again. However, the loader does not have insight into dynamically-created dependencies, and the DLLs may unload out of order. When the process shuts down, the loader uninitializes the DLLs in an order that tries¹ to preserve the static dependencies, so that a DLL waits until all its dependents are uninitialized before itself uninitializing. Switching the WIDGET.DLL to a /DELAYLOAD DLL removes this static dependency, and the loader isn’t around to help any more. When the WIDGET.DLL was a static dependency, the loader made a note to ensure that WIDGET.DLL was loaded and ready before calling CONTOSO.DLL‘s initialization function, and made sure that WIDGET.DLL remained valid until CONTOSO.DLL completed its uninitialization. This worked out great, except that sometimes their DLL crashed when shutting down. To improve DLL load time, they made WIDGET.DLL a delay-loaded DLL via the /DELAYLOAD linker option. A customer had a DLL, let’s call it CONTOSO.DLL, and that DLL linked to another DLL, let’s call it WIDGET.DLL.
0 Comments
Leave a Reply. |