I have an application which load a DLL that makes use of Delphi GDI+ Library. This application hangs when it unload the DLL (Calling FreeLibrary).
I tracked the issue down to GdiPlus.pas unit finalization section which calls GdiPlusShutdown which never returns.
How to avoid this deadlock?
The documentation for the
GdiplusStartup
function says this:By compiling this Delphi GdiPlus library into a DLL you are breaking this rule for both
GdiplusStartup
andGdiplusShutdown
. These functions are called in unitinitialization
andfinalization
sections, respectively. And for library projects, code in theinitialization
andfinalization
sections of a unit is executed fromDllMain
.It seems that the GdiPlus library that you use was never intended to be used from a library. But as a general rule, when writing library code, you should be aware of the restrictions around
DllMain
and make sure that code that you place ininitialization
andfinalization
sections respects that. I think that this GdiPlus library fails in that regard.By way of contrast, have a look at the code in the Delphi RTL's
WinApi.GDIPOBJ
unit:This code respects the rules by making sure that it does not call
GdiplusStartup
andGdiplusShutdown
fromDllMain
. Instead it leaves the onus on the author of any library that usesWinApi.GDIPOBJ
to make sure thatGdiplusStartup
andGdiplusShutdown
are called at appropriate times.If I were you I would pick one of the three bullet point options listed above. The third of these options is not very practical, but the first two are good choices. Were it me, I would opt for the first option and modify the
initialization
andfinalization
code in yourGdiPlus
library to look more like that found inWinApi.GDIPOBJ
.GdiPlusShutdown (and GdiPlusStartup btw) cannot be called from DllMain but DllMain is called by Windows and Delphi runtime when FreeLibrary is called: Delphi call the finalization section of all units used by the DLL and GdiPlus finalization section calls GdiPlusShutdown (This is perfectly OK when used from an executable). Similar behavior with initialization section.
I have fixed the issue by adding a test for IsLibrary in both initialization and finalization sections to avoid calling the offending functions, also added two public procedures InitializeForDll and FinalizeForDll. With those little changes, the DLL is able to export functions calling InitializeForDll and FinalizeForDll. Those exported function have to be called by the hosting application right after loading the DLL and just before unloading the DLL.
Here are the changes I made to GdiPlus.pas:
In the interface section:
In the implementation section:
Also updated the initialization and finalization sections like this:
In the DLL, I exported those functions:
Initialize and Finalize are called by the hosting application right after calling LoadLibrary and just before calling FreeLibrary (Or whatever will load/unload the DLL).
I hope this will help others. btw: Thanks to Eric Bilsen for providing Delphi GdiPlus Library