Re: The DLLMain...

From: Beth (BethStone21_at_hotmail.NOSPICEDHAM.com)
Date: 09/29/04


Date: Wed, 29 Sep 2004 08:01:46 GMT

Jim Carlock wrote:
> DllMain PROC STDCALL, handle:dword, dwReason:dword, dwReserved:dword
> MOV EAX,1
> RET
> DllMain ENDP
>
> Are all DLL main's configured like this?
> And that Mov EAX, 1 means what, that 1 is returned in the
> EAX register? And for what reason?

DllMain (which may be named anything, in fact...it is the fact that it's
the DLL's assigned "entry-point" that counts, not the name itself ;) is
called for four possible reasons, which can be read out of the "dwReason"
parameter sent to the procedure:

DLL_PROCESS_ATTACH (value: 1)

When a process first loads a DLL, DllMain is called with this "dwReason"
value...this gives the DLL a chance to globally initialise itself (e.g.
allocate resources, set up "window classes" or whatever ;)...the return
value (placed in EAX, as per usual "stdcall" conventions :) represents
whether initialisation was successful (TRUE: 1) or not (FALSE: 0)...

DLL_PROCESS_DETACH (value: 0)

When a process finally unloads the DLL, it's called with this "dwReason" to
simply give the DLL a chance to uninitialise...you know, deallocate
resources, delete "window classes" and, well, typically, the complete
opposite of the original global initialisation in DLL_PROCESS_ATTACH...the
return value (in EAX) is ignored in this case...

Using DLL_PROCESS_ATTACH and DLL_PROCESS_DETACH, the DLL has a chance to
globally initialise and uninitialise itself...so, if a DLL uses some global
data or resources throughout its operation, then it can just set it up in
DLL_PROCESS_ATTACH (and get rid of it in DLL_PROCESS_DETACH)...for example,
if a DLL has a bunch of its own "window classes", then it could register
those in DLL_PROCESS_ATTACH (and unregister them in
DLL_PROCESS_DETACH)...or, alternatively, if a DLL works with a particular
"log file" or something, it can open that file in DLL_PROCESS_ATTACH and
close it in DLL_PROCESS_DETACH and the other DLL procedures can just read
and write to the file...

[ DLL_PROCESS_DETACH is the equivalent of Windows 16-bit's "WEP"
procedure...if this means nothing to you, don't worry, as knowing about
Win16 is quite useless these days...but if someone did know about "WEP"
("Windows Exit Procedure"), it might be a helpful comparison to make,
perhaps ;) ]

The following "reasons" happen with multi-threaded applications (so, if you
only have one thread in your application, the DLL should never see these
"reasons" appear at all ;)...

DLL_THREAD_ATTACH (value: 2)

DllMain is called with this reason when the process starts up a new
thread...the purpose of this "reason" (and the corresponding
DLL_THREAD_DETACH) are much the same as before _except_ that these provide
"per thread" initialisation...a DLL designed for multi-threaded
applications can use this notification to set up any "per thread"
information and initialisation it needs...the return value, again, is
ignored...

[ Note that the notification only happens once a DLL is loaded...that is,
if an application had lots of threads running and _then_ it called
"LoadLibrary" to load in the DLL then those threads already running would
NOT call the DLL with DLL_THREAD_ATTACH ]

DLL_THREAD_DETACH (value: 3)

The corresponding "deinitialisation" reason for DLL_THREAD_DETACH...here is
where the "per thread" stuff set up in DLL_THREAD_ATTACH can be
uninitialised (it is more important to properly uninitialise here than
DLL_PROCESS_DETACH because the DLL is not necessarily unloaded - where the
OS does some automatic deallocation for any terminating process - and,
hence, not fully uninitialising what was initialised in DLL_THREAD_ATTACH
could lead to a "memory leak"...

[ Regardless, it is poor programming practice to rely on the OS to
uninitialise things when terminating, anyway...this exists not as a
programming convenience - an excuse not to properly uninitialise things -
but because the OS needs to ensure that such things as "resource leaks"
don't bring the entire system down...indeed, if programmers could be 100%
relied upon to do their uninitialising properly in all instances, then this
facility logically wouldn't be needed at all...in practice, programmers get
lazy or accidentally leave bugs in a program so the OS is forced to just
"make sure" that no "resource leaks" can gradually eat up or "lock out" all
system resources and bring the entire system down (a "reliability"
thing...no process should be allowed to ever bring down the entire
system...kind of unacceptable in a multi-tasking system to lose hours and
hours of very important unrecoverable work just because, ooh, the "clock"
application crashed...Microsoft finally learnt the importance of this only
recently, as any user of Win9x, staring at the 27th "blue screen of death"
that day, knows far too well...not that NT / 2K / XP are in any way perfect
on this score - I have seen the BSOD on these systems too - but at least,
this time, they actually _TRY_ to prevent such things happening ;)...it's a
"safety net", not a "feature"...you should climb down the ladder rather
than jump off the tightrope when you've finished your act, as even "safety
nets" can sometimes fail (and if you keep jumping up and down on the
"safety net", you increase your chances of hitting it in just such a way as
to make it fail ;)... ]

Also, related to the note in DLL_THREAD_ATTACH above, the DLL can actually
end up in certain circumstances being called with DLL_THREAD_DETACH for
those threads which never got initialised with DLL_THREAD_ATTACH above
(because the DLL was loaded explicitly with "LoadLibrary" _after_ these
threads were already started and threads are NOT "retro-actively" called
with DLL_THREAD_ATTACH when a DLL loads :)...something to note when writing
a DLL to work with multi-threaded applications that an application could
use "LoadLibrary" and end up calling DLL_THREAD_DETACH more times than it
called DLL_THREAD_ATTACH...in other words, don't blind uninitialise things
presuming every thread "must" have been initialised because it might not
have...

As for the other parameters, the first parameter is the DLL's "HINSTANCE"
(handle to instance)...and "dwReserved" is not currently defined for
anything ("for future expansion", I guess...and, as usual when Microsoft
add something like this "for future expansion" they, in fact, never expand
it in the future at all...Murphy's Law for you, though...if they didn't
"reserve" things for future expansion then _that's_ the occasion when they
truly need it ;)...

As noted, what defines something as "DllMain" is that it is the file's
assigned "entry-point", not its name...so it can actually be called
anything in the source code, so long as that is set up correctly (though
some tools naturally have a fixed entry-point - can't be specified to the
linker but uses a pre-defined name (like "main" or "DllEntryPoint" ;) to
locate the address, anyway...in those cases, it has to be called what the
tool expects so that it can be found by the tool...not a problem for MASM,
though, as the Microsoft linker allows the entry-point to be set explicitly
with a command-line option :)...and, unlike the entry-point of an
executable, it is the address of a _procedure_ that's the entry-point...as
shown above in the example, the procedure ends with a "RET" to return
control elsewhere (actually, "HLLisms" are being used here...it's actually
literally a "RET 12" instruction because, under STDCALL, the parameters are
cleaned up by the procedure itself and this version of "RET" removes the
specified number of bytes off the stack as it returns, cleaning up the
parameters from the stack as it does so...this is actually what MASM / TASM
generates when it sees the "RET" but it's all part of the "HLLism"...it
sees "proc stdcall" and knows to count up the parameter bytes and produce a
"RET nn" at the end where it sees "RET")...

Beth :)



Relevant Pages

  • Re: Embedd DLL into executable
    ... I don't see actually the reason to implement this technique. ... If some product requires having DLL, let it be in product's directory, ... copied by MSI installer. ... OP tries to avoid. ...
    (microsoft.public.win32.programmer.kernel)
  • Re: CVF DLL, problems reading binary file
    ... in Compaq Visual Fortran which I'm trying to convert to a DLL to be ... I don't see any reason why a .EXE program and a DLL, ... ones that check subscripts and subroutine argument matching. ...
    (comp.lang.fortran)
  • Re: D3 odbc and .Net, almost there!
    ... that he was passing values ByVal instead of ByRef in his VB.NET code, ... The DLL itself follows well known standards ... as the parameters are correct it will work because the standards for calling ... there is no reason that code shouldn't work properly. ...
    (comp.databases.pick)
  • Re: The DLLMain...
    ... When a process first loads a DLL, DllMain is called with this "dwReason" ... value...this gives the DLL a chance to globally initialise itself (e.g. ... globally initialise and uninitialise itself...so, if a DLL uses some global ... DllMain is called with this reason when the process starts up a new ...
    (alt.lang.asm)
  • Re: The event object gets signalled when an USB flash drive is inserted
    ... One reason of the strange behaviour may be that you're not allowed to ... In fact the things you're allowed to do in DllMain are very limited, ... which you call after the dll is loaded. ...
    (microsoft.public.vc.language)