Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Re: DllMain is called when....

571 views
Skip to first unread message

Igor Tandetnik

unread,
Jul 29, 2004, 4:21:40 PM7/29/04
to
"Roger" <Ro...@discussions.microsoft.com> wrote in message
news:FEDE7415-C932-42CB...@microsoft.com
> 1. called with DLL_PROCESS_ATTACH
> 2. called with DLL_THREAD_ATTACH
> 3. called with DLL_THREAD_ATTACH
> 4. called with DLL_THREAD_DETACH
> 5. called with DLL_PROCESS_DETACH
>
> Most disconcerting is that the last DLL_PROCESS_DETACH is called
> before I call FreeLibrary.(!) Can anyone tell me:
>
> 1. Why there are calls with DLL_THREAD_ATTACH when I am not calling
> LoadLibrary from more than one thread, and additionally I'm not
> accessing the dll from more than the one object in which is it
> created

A newly created thread calls DllMain(DLL_THREAD_ATTACH) for every DLL
currently loaded into the process. This is completely unrelated to
whether this thread ever calls LoadLibrary, or ever referes to anything
from a particular DLL.

> 2. Why there are "unbalanced" calls with DLL_THREAD_ATTACH and
> DLL_THREAD_DETACH

OS makes no attempts to balance them. When a DLL is loaded, there may be
threads running already - they don't call DLL_THREAD_ATTACH. Only a new
thread started after the DLL is loaded calls DLL_THREAD_ATTACH.

By the same token, a thread terminating gracefully calls
DLL_THREAD_DETACH on all DLLs currently loaded into the process right
before terminating. When a DLL is unloaded, only DLL_PROCESS_DETACH is
called - you don't get DLL_THREAD_DETACH for every thread currently
running.

Thus, the following situations are possible:

a. A thread existed before the DLL was loaded, and exited while it was
loaded. Such a thread will execute DLL_PROCESS_DETACH but not
DLL_PROCESS_ATTACH

b. A thread was created while the DLL was loaded, then the DLL is
unloaded while this thread is still running. In this case you will have
DLL_PROCESS_ATTACH for this thread, but not DLL_PROCESS_DETACH.

> 3. Why the call with DLL_PROCESS_DETACH takes place before I call
> FreeLibrary

Are you sure? I find it hard to believe. Is there a possibility of a
reference counting error (FreeLibrary called more times than LoadLibrary
on the same DLL)?
--
With best wishes,
Igor Tandetnik

"For every complex problem, there is a solution that is simple, neat,
and wrong." H.L. Mencken


Ivan Brugiolo [MSFT]

unread,
Jul 29, 2004, 4:54:03 PM7/29/04
to
Is your object a global in a module ?
Does the dtor of your object gets called before ExitProcess()/exit() ?

I bet that the output below comes from the ProcessShutdown code-path.

--
This posting is provided "AS IS" with no warranties, and confers no rights.
Use of any included script samples are subject to the terms specified at
http://www.microsoft.com/info/cpyright.htm


"Roger" <Ro...@discussions.microsoft.com> wrote in message

news:FEDE7415-C932-42CB...@microsoft.com...
> I have a DLL that I am loading in the construcor of an object using
LoadLibrary, and freeing in the destructor using FreeLibrary. I find (by
setting breakpoints in the debugger), however, that the order in which
DLLMain is called is as follows:


>
> 1. called with DLL_PROCESS_ATTACH
> 2. called with DLL_THREAD_ATTACH
> 3. called with DLL_THREAD_ATTACH
> 4. called with DLL_THREAD_DETACH
> 5. called with DLL_PROCESS_DETACH
>
> Most disconcerting is that the last DLL_PROCESS_DETACH is called before I
call FreeLibrary.(!) Can anyone tell me:
>
> 1. Why there are calls with DLL_THREAD_ATTACH when I am not calling
LoadLibrary from more than one thread, and additionally I'm not accessing
the dll from more than the one object in which is it created

> 2. Why there are "unbalanced" calls with DLL_THREAD_ATTACH and
DLL_THREAD_DETACH

> 3. Why the call with DLL_PROCESS_DETACH takes place before I call
FreeLibrary
>

> I'm using VC++6.
>
> Thanks!


Roger

unread,
Jul 29, 2004, 6:04:27 PM7/29/04
to

"Igor Tandetnik" wrote:

I double-checked this as follows:
I have three breakpoints in the dll. One is at the DLL_PROCESS_ATTACH case, one is at the DLL_PROCESS_DETACH case, and one is at a function called Release (which will release some memory I allocated with new). What happens is that when I exit out of the exe, the DLL_PROCESS_DETACH breakpoint is hit first, THEN the breakpoint at Release is hit, which is something in itself I don't understand; why would the Release function call not generate a run-time error? I thought the last call to dll main with DLL_PROCESS_DETACH would indicate that the dll would be unloaded from memory?

Roger

unread,
Jul 29, 2004, 6:11:02 PM7/29/04
to
"Ivan Brugiolo [MSFT]" wrote:

> Is your object a global in a module ?
> Does the dtor of your object gets called before ExitProcess()/exit() ?
>
> I bet that the output below comes from the ProcessShutdown code-path.
>

The object is created with a "new" in an object in "theApp", and deleted in ExitInstance of a "Regular" DLL statically linked to an exe. So what I have is a regular DLL statically linked to an exe, and an object created in the statically linked DLL dynamically loads the (non MFC) DLL in question.

It seems like my destructor is not called before the ExitProcess, but when is ExitProcess called, and how could it be called before the destuctors?

Thanks

Igor Tandetnik

unread,
Jul 29, 2004, 6:29:38 PM7/29/04
to
"Roger" <Ro...@discussions.microsoft.com> wrote in message
news:E2549C04-89BC-49D9...@microsoft.com

> The object is created with a "new" in an object in "theApp", and
> deleted in ExitInstance of a "Regular" DLL statically linked to an
> exe.

ExitInstance is actually called from inside DllMain in MFC. Thus, your
object is deleted because the process is shutting down and unloading its
DLLs. The order of this unloading is unpredictable. That other DLL just
happens to get unloaded earlier.
--

Ivan Brugiolo [MSFT]

unread,
Jul 29, 2004, 7:05:39 PM7/29/04
to
Order of construction and destruction of global objects is
both undefined by C++ standard, and it's a tricky business
in general in the MS implementation of the C-Runtime.

For a given module made of single compilation units,
the order is guarneteed within the compilation unit, but not globally.
It's also guaranteed that the ctors of the global objects are
all called before main() is entered, and they are availalbe till exit() is
called.
The dtors will be called at the same time and
with the same guarantees of the atexit() functions.

For Dlls, the same rules apply.
The only difference being that DllMain(PROCESS_ATTACH)
takes the place of main() and DllMain(PROCESS_DETACH)
takes the place of exit();

Now, if your process is exiting via a non C-Rutnime mechanism,
then guarantees cannot be satisfied.

Practically speaking, in the current implementations,
when ExitProcess is called, the loaded modules are called
in reverse initialization order, with
DllMain(hInstance,PROCESS_DETACH, <something not NULL>).

In any case, if you are calling LoadLibrary from the ctor
of a global object in a DLL, then you are asking for trouble,
since calling LoadLibrary from _DllMainCrtStartup can alter the
initialization
order of existing module, and sometimes can lead to loader failures.
See MSDN remarks for details.

--
This posting is provided "AS IS" with no warranties, and confers no rights.
Use of any included script samples are subject to the terms specified at
http://www.microsoft.com/info/cpyright.htm


"Roger" <Ro...@discussions.microsoft.com> wrote in message

news:E2549C04-89BC-49D9...@microsoft.com...

Doug Harrison [MVP]

unread,
Jul 29, 2004, 8:19:05 PM7/29/04
to
Ivan Brugiolo [MSFT] wrote:

>Order of construction and destruction of global objects is
>both undefined by C++ standard, and it's a tricky business
>in general in the MS implementation of the C-Runtime.
>
>For a given module made of single compilation units,
>the order is guarneteed within the compilation unit, but not globally.
>It's also guaranteed that the ctors of the global objects are
>all called before main() is entered, and they are availalbe till exit() is
>called.
>The dtors will be called at the same time and
>with the same guarantees of the atexit() functions.
>
>For Dlls, the same rules apply.
>The only difference being that DllMain(PROCESS_ATTACH)
>takes the place of main() and DllMain(PROCESS_DETACH)
>takes the place of exit();

It's important to note that the C++ Standard defines the order of
destruction of static duration objects to be the reverse of the order of
their construction. These interleave with atexit calls, and in fact, you can
think of the final act in successful construction of a static duration
object as registering a call to the object's dtor with atexit. This applies
to local statics as well as globals. VC++ can deviate from this behavior
when both local statics and DLLs are involved. In brief, VC++ maintains a
separate atexit list for each module, which is processed during
DLL_PROCESS_DETACH, so the required destruction order can be violated for
local statics, which are only ever entered for destruction in their own
module's atexit list. I wrote about that in much more detail here and
suggested a way it could be fixed:

http://groups.google.com/groups?selm=r08j009jlkjck5gdedti38h66p0gfpbcf3%404ax.com

>Now, if your process is exiting via a non C-Rutnime mechanism,
>then guarantees cannot be satisfied.
>
>Practically speaking, in the current implementations,
>when ExitProcess is called, the loaded modules are called
>in reverse initialization order, with
>DllMain(hInstance,PROCESS_DETACH, <something not NULL>).
>
>In any case, if you are calling LoadLibrary from the ctor
>of a global object in a DLL, then you are asking for trouble,
>since calling LoadLibrary from _DllMainCrtStartup can alter the
>initialization
>order of existing module, and sometimes can lead to loader failures.
>See MSDN remarks for details.

Unfortunately, the problem described above makes it difficult to relax the
DllMain restrictions for DLL local statics initialized by a call from the
EXE, or in general after main() has been entered. (Think singletons for an
example of when local statics are useful.)

--
Doug Harrison
Microsoft MVP - Visual C++

Ivan Brugiolo [MSFT]

unread,
Jul 29, 2004, 9:28:48 PM7/29/04
to
Thanks Doug for the clarification.
I did not have time to go over the detils of the destruction order
(I gave that for granted that is was the reverse construction order)
and the relationship between ctros of globals, local statics, and atexit().
I know which global variable to look for in the
C-Runtime when I have to debug init-deinit problems,
and I thought this was not so relevant here today.

Calling LoadLibrary from _DllMainCrtStartup / DllMain
does not have adverse side effects in most of the cases.
But, I can build very simple cases where it does cause
calls into un-initialized modules.

--
This posting is provided "AS IS" with no warranties, and confers no rights.
Use of any included script samples are subject to the terms specified at
http://www.microsoft.com/info/cpyright.htm


"Doug Harrison [MVP]" <d...@mvps.org> wrote in message
news:g74jg01vrflg7g7gd...@4ax.com...

Roger

unread,
Jul 30, 2004, 10:41:01 AM7/30/04
to
I read about the potential problem with LoadLibrary being called from DllMain, so I avoided that by having the LoadLibrary called the first time the relevant Dll interface function is called.

The FreeLibrary call happens in the over-ridden CWinApp ExitInstance call, where I call delete on the Object in question. Shouldn't ExitInstance finish executing before dll's start unloading for some other reason? I don't have anything "fancy" as far as exiting via a non C-Rutnime mechanism.

Igor Tandetnik

unread,
Jul 30, 2004, 11:04:32 AM7/30/04
to
"Roger" <Ro...@discussions.microsoft.com> wrote in message
news:3236AEB3-8B9A-45A9...@microsoft.com

> The FreeLibrary call happens in the over-ridden CWinApp ExitInstance
> call, where I call delete on the Object in question. Shouldn't
> ExitInstance finish executing before dll's start unloading for some
> other reason?

ExitInstance is called _because_ the DLL starts unloading. It is called
from inside DllMain(DLL_PROCESS_DETACH).

Roger

unread,
Jul 30, 2004, 11:35:01 AM7/30/04
to
Okay, I see now. Is there any way to tell if the handle to a dll obtained with LoadLibrary is still valid, ie it's okay to call the dll functions and FreeLibrary for the dll? What I'm getting at is this:

I have an object that loads a DLL with LoadLibrary. The object may be deleted either in the normal course of the program, or at shutdown, and there may be multiple copies of it. So, logically it makes sens to me that I would call a shutdown function in the dll (that releases some memory) and then call FreeLibrary, but it looks like this could cause problems.

I got rid of a memory leak I had by checking in the dll's DllMain( PROCESS_DETACH) function that all the memory was freed, but now I have this situation where objects can call the DLL shutdown function and FreeLibrary() after the dll's DllMain( PROCESS_DETACH) has been called.

Thanks,

0 new messages