This is a common problem that I don't think is particularly specific to Delphi programmers. If you have code that calls LoadLibrary
in an initialization section, or indeed FreeLibrary
in a finalization section then that code is not safe to be used in a library.
Note that I am not familiar with all the libraries that you have mentioned, and am not at all confirming that they all have initialization
section code that is not safe for use in a library. I think that's something for you to confirm–I'd like to stick to the concepts in this answer rather than comment on specific Delphi libraries.
I would say though that calls to GetModuleHandle
and GetProcAddress
are fine from DllMain
. I say that because you mention GetProcAddress
specifically. It's absolutely fine to obtain a module handle by, for instance, calling GetModuleHandle
, and then to obtain a function address by calling GetProcAddress
. So if some of the suspect libraries do that, and do not call LoadLibrary
, then they might be alright.
Anyway, subject to the provisos above, you need to arrange that any code that will be called from DllMain
that contravenes the rules laid down by Microsoft is called at a safe time instead, and not from DllMain
. Unfortunately those rules are hazy at best. Microsoft say the following in the DllMain
documentation:
The entry-point function should perform only simple initialization or
termination tasks. It must not call the LoadLibrary or LoadLibraryEx
function (or a function that calls these functions), because this may
create dependency loops in the DLL load order. This can result in a
DLL being used before the system has executed its initialization code.
Similarly, the entry-point function must not call the FreeLibrary
function (or a function that calls FreeLibrary) during process
termination, because this can result in a DLL being used after the
system has executed its termination code.
Because Kernel32.dll is guaranteed to be loaded in the process address
space when the entry-point function is called, calling functions in
Kernel32.dll does not result in the DLL being used before its
initialization code has been executed. Therefore, the entry-point
function can call functions in Kernel32.dll that do not load other
DLLs. For example, DllMain can create synchronization objects such as
critical sections and mutexes, and use TLS. Unfortunately, there is
not a comprehensive list of safe functions in Kernel32.dll.
The final paragraph leaves you with little guidance. To be confident that your library will be robust you will need to do something along the following lines:
- Arrange that each initialization section of any unit whose source code you control registers with a central registry an initialization and finalization procedure.
- In an executable project you call the initialization procedures when they are registered, and call the finalization procedures in reverse order when the program terminates.
- In a library project you postpone the calling of these initialization and finalization procedures. Export a pair of functions from the DLL that the consumer of the DLL can call to request that these initialization and finalization procedures are called.
This is the approach that I have taken with my libraries and it has served me well for many years.
This approach involves quite a lot of work, and has the downside that you are modifying third party libraries. However, if those libraries will not work correctly when used as delivered, what alternative do you have?
Perhaps in slower time you could contact the developers of any libraries that you believe are not compatible with use in a library. Try to persuade them to change their code such that it is compatible with use in a library. As you can see from Remy's comment to your question, it's entirely possible that the library developers may be unaware of the issue, and very willing to make changes.
GetModuleHandle()
andGetProcAddress()
are safe to use during DLL initialization, per the document you linked to. Indy dynamically loads several DLLs at runtime, but none of them should have been loaded in anyinitialization
sections, they are loaded dynamically on an as-needed basis when actually calling DLL functions. But I just found 1 DLL (zlib) that is being loaded in aninitialization
section, so I have fixed that now. – AgricultureGetModuleHandle
? It says "This “loader lock” is taken any time a library is loaded but also when functions like GetModuleHandle() or GetModuleFileName() are used." – Segalman