From: Pete Gontier on
The response below prompted me to do some investigation by which I solved
the problem. Thanks, Code Jockey!

The documentation for GdiplusStartup and GdiplusShutdown says you may not
call either function during DllMain, although it seems nothing bad happens
if you call the former during DllMain. What the documentation for these
calls doesn't explain is that the DllMain you write is not the DllMain it's
talking about. In fact, the real DllMain is in the C runtime library, and it
spoofs the messages before they get to your DllMain. Among other things the
C runtime library does during DllMain is construct and destruct global
objects. If one of these globals happens to have a constructor which calls
GdiplusStartup and a destructor which calls GdiplusShutdown, the DLL will
block endlessly in GdiplusShutdown waiting for a thread created by
GdiplusStartup to exit. The thread will never exit because only one thread
at a time can run while a DLL is being torn down. This apparently isn't a
problem for a regular application (EXE), presumably because its C runtime
library works differently. (I ran into this problem while converting an
application to a panel.)

To solve this problem, I moved the object in question onto the stack. This
should work regardless of whether the code is compiled into an application
or a DLL. It means I might not use memory in the most efficient possible way
because I'm unable to create some GDI+ objects globally, but the alternative
was much worse. :-)


On 7/20/06 10:28 AM, in article ebRdrICrGHA.4424(a)TK2MSFTNGP05.phx.gbl, "Code
Jockey" <gimme(a)the.keyboard> wrote:

> During process shutdown, global static objects with destructors that wait
> for threads to exit can cause an application to hang, because only one
> thread at a time is allowed to run at that time.
>
> Problems like this can be caused by an architecture where DllMain() calls
> functions that cause other modules to get loaded, or create threads, and
> wait for threads at unload time. If the constructors of global static
> objects do similar things, then the same problems can result, because these
> actually run before DllMain(). .
>
> Modules that cause this kind of dead-lock can be loaded and unloaded
> dynamically during normal app runtime conditions and avoid the hang.
>
>
>
> "Pete Gontier" <kokorozashi(a)discussions.microsoft.com> wrote in message
> news:C0E42FBB.1CA6D%kokorozashi(a)discussions.microsoft.com...
>> On the advice of some folks off-list, I downloaded, installed, and futzed
>> around with SysInternals Process Explorer, which in turn recommended I
>> download the Microsoft driver debugging toolkit, which I did. I can now
> look
>> at some data about about the problem thread. Pressing the Stack button
>> produces this:
>>
>> ntoskrnl.exe!ExReleaseResourceLite+0x206
>> ntoskrnl.exe!KeFlushQueuedDpcs+0x1fde
>> ntoskrnl.exe!KeFlushQueuedDpcs+0x314c
>> ntoskrnl.exe!IoCheckFunctionAccess+0x2f855
>> ntoskrnl.exe!RtlInsertElementGenericTableAvl+0x2b0
>> ntoskrnl.exe!Kei386EoiHelper+0x1d9
>> !DllMain+0x11b
>> !_DllMainCRTStartup+0xbb
>> ntdll.dll!LdrInitializeThunk+0x29
>> ntdll.dll!CsrNewThread+0xeb
>> ntdll.dll!RtlUnicodeStringToInteger+0x17d
>> ntdll.dll!KiUserApcDispatcher+0x7
>>
>> I took this stack snapshot when the thread in question was stopped at a
>> breakpoint in my panel DLL having been sent the DLL_THREAD_ATTACH message.
> I
>> don't know if this info is helpful to anyone, but I thought I'd pass it
>> along just in case.
>>
>> There's also a highly attractive Module button, which for some threads
>> produces the name of a DLL, presumably the DLL which created the thread
> (or
>> perhaps the name of the DLL which contains the thread entry proc, which
>> might be good enough). Unfortunately, for the problem thread, this button
>> reports only that the module could not be located. Talk about building me
> up
>> just to knock me down! :-)
>>
>>
>> On 7/19/06 4:42 PM, in article
>> C0E4118A.1CA4F%kokorozashi(a)discussions.microsoft.com, "Pete Gontier"
>> <kokorozashi(a)discussions.microsoft.com> wrote:
>>
>>> I'm trying to build a control panel DLL. Everything goes swimmingly
> until
>>> it's time to close. It seems some part of the close cannot complete even
>>> after my panel receives the appropriate messages (CPL_EXIT and
>>> DLL_PROCESS_DETACH among others). "rundll32.exe" just hangs around,
>>> presumably waiting for something else to happen.
>>>
>>> From deep inside my spooky, mysterious, cavernous mind, I got an
> intuition
>>> that the "something else" might be a thread which has not yet exited.
> Sure
>>> enough, my DllMain function does receive a DLL_THREAD_ATTACH message but
> no
>>> corresponding DLL_THREAD_DETACH message. My control panel does not
> create
>>> any threads explicitly. Even more puzzling is that other control panels
> to
>>> which I have the source -- and in fact that I myself wrote -- do not
> cause
>>> any such thread to be created and do not have this problem, and I have
> been
>>> unable to determine what this new panel is doing differently.
>>>
>>> The thread gets created under two circumstances I've found so far.
>>>
>>> I first noticed this problem when I added a manifest resource with ID
> 123 to
>>> make the common controls DLL version 6 available to my panel. So the
> thread
>>> might be created by this DLL or some DLL on which it depends. The
> question
>>> is why? My other panels have an almost identical manifest resource
>>> (differing only in the name of the assembly identity and its
> description).
>>> If I renumber this resource to something other than 123, I don't have
> this
>>> thread problem (and my program fails when it tries to send a message to
> a
>>> tool tip window, so I know the common controls DLL has not loaded).
>>>
>>> I've also noticed a problem thread being created when I explicitly load
>>> "Msftedit.dll", the rich text control DLL, with a call to LoadLibrary. I
> do
>>> this in several other panels without a thread being created.
>>>
>>> Either one these circumstances leads to the creation of a problem
> thread. I
>>> don't know that it's the same thread in both cases, but the fact that I
> get
>>> only one problem thread when I both have a manifest and load the rich
> text
>>> DLL suggests to me that the thread is being created by s