Debugging the problem, I found that there were still references to the external DLL, and that the CPU view was showing me that the EXE was repeatedly trying to enter a critical section, but not being able to do so. If you look at the destruction code for TDataModule, you'll see that it tries to lock access to the DataModule. Well, if you rely on Delphi to get rid of internally stored interface references when they go out of scope, it will be too late to reclaim the external DLL. The solution is as follows:
Create an overridden BeforeDestruction method in the main RDM. In that RDM, set all of your ChildRDM interface references to nil in order to explicitly release the COM objects. Then, make a call to CoFreeUnusedLibraries (found in ActiveX.pas). This will release the DLL, if the COM subsystem says it's OK to do so. However, I found that even that was enough in all cases, so add another call to CoFreeUnusedLibraries in the main RDM's finalization section. See the code snippet here:
procedure TMainRDM.BeforeDestruction; begin fSharedDataRdm := nil; fChildRdm := nil; CoFreeUnusedLibraries; inherited; end; initialization TComponentFactory.Create(ComServer, TMainRDM, Class_MainRDM, ciMultiInstance, tmApartment); finalization CoFreeUnusedLibraries; end.
After doing this, I was not able to get the app server to hang in memory again.
Disclaimer The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.