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

DllMain und was man so alles nicht darf

9 views
Skip to first unread message

Martin Richter [MVP]

unread,
Jun 9, 2006, 6:04:04 AM6/9/06
to
Hi!

Ich bin durch einen Thread hier mal wieder auf das leidige Thema
gestoßen was man alles so in der DllMain darf und was man eben nicht darf.

Nach nachlesen von einigen Artikeln war ich nun etwas erstaunt, dass
nicht einmal CRT Funktionen in DllMain aufgerufen werden dürfen, wenn
die CRT z.B. dynamisch gelinkt ist. Aber nun wird es ja heftig, wenn man
da mal länger drüber nachdenkt. Schon gar keine threadafinen Dinge darf
man machen, aber vieles in der CRT ist thread afin.
Siehe auch:
http://blogs.msdn.com/oleglv/archive/2003/10/28/56142.aspx

Also, da hat meine eine EXE, dazu eine DLL und die CRT ist dynamisch
gelinkt. Wenn die DLL jetzt z.B. eine regular DLL ist, dann wird
automatisch InitInstance() der MFC DLL aufgerufen. Das ganze im Kontext
der DllMain.
Frage: Warum knallt es nicht öfters, denn im DllMain Kontext werden
massenweise CRT aufrufe und Objekt Initialisierungen gemacht?

Haben wir alle immer nur Glück und auf irgendeinem mysteriösen Weg ist
die CRT-DLL immer schon durch die EXE geladen und initialisiert?

Und auch nur mal am Rande: Der Initcode der auch in DllMain der MFC
benutzt tonnenweise andere DLL's, wie z.B. die SHLWAPI, Thread afine CRT
Funktionen! Die user32.dll kommt hier sofort mehrfach ins Spiel, denn
jeder ressourcen Zugriff (LoadAccelerator etc.) ist in der user32.dll...
Und natürlich werden auch entsprechende Fensterklassen registriert etc.

Ist denn die user32.dll wirklich so tabu, wenn es heißt _*nur*_
Funktionen aus kernel32!
Mystisch, mystisch...

Ich glaube man sollte manchmal vorsichtiger sein, als wir es im
allgemeinen sind.

--
Martin Richter [MVP] WWJD
"In C we had to code our own bugs. In C++ we can inherit them."
FAQ : http://www.mpdvc.de
Samples: http://www.codeguru.com http://www.codeproject.com

Eberhard Schefold

unread,
Jun 9, 2006, 7:03:20 AM6/9/06
to
Martin Richter [MVP] schrieb/wrote:

> Ist denn die user32.dll wirklich so tabu, wenn es heißt _*nur*_
> Funktionen aus kernel32!
> Mystisch, mystisch...
>
> Ich glaube man sollte manchmal vorsichtiger sein, als wir es im
> allgemeinen sind.

Wenn ich dazu mal eine Schnurre beisteuern darf: Wir hatten spontan das
Problem, daß sich einige unserer COM-inproc-Server im
Installshield-Setup nicht mehr registrieren ließen (IS-Fehlermeldung).
Originellerweise immer zwei bis drei Stück, aber nicht immer dieselben.
Wenn man anschließend regsvr32 auf der Maschine manuell aufrief, gabs
kein Problem.

Nach längerem Hin und Her stellte sich folgendes als Problem heraus: Ein
Entwickler hatte Code in die DllMain hinzugefügt, welcher LoadLibrary
für eine Resource-DLL aufrief -- weiter nichts. Ging bei jedem Test auch
ohne Probleme. InstallShield startet jedoch anscheinend für die
Registrierung jedes Inproc-Servers einen eigenen Thread. Somit lief die
Initialisierung quasi-parallel, und nur unter diesem Streß traten dann
die Probleme auf. In einem Test-EXE ließ sich das reproduzieren.

Seit dieser Erfahrung nehme ich die diesbezüglichen Warnungen besonders
ernst.

Holger Gothan

unread,
Jun 9, 2006, 8:26:03 AM6/9/06
to
Hi,

natürlich gehören der DllMain-Problemklasse auch alle Konstruktoren
globaler Objekte an.

Ich habe eine Basisklasse, die Laden/Speichern implementiert.
Einige überall benötigte Einstellungen habe ich in einer globalen Variable
zusammengefaßt.

Irgendwann hatte ich angefangen, auf den <Eigene-Dateien>-Ordner
umzustellen.
Ging sogar eine ganze Zeit gut....

Tschüß, Holger.


Andre Stille [MVP]

unread,
Jun 9, 2006, 8:27:30 AM6/9/06
to
Hallo!

"Martin Richter [MVP]" <martin....@mvps.org> schrieb im Newsbeitrag
news:e6bo3j...@news.grutzeck.de...


>
> Also, da hat meine eine EXE, dazu eine DLL und die CRT ist dynamisch
> gelinkt. Wenn die DLL jetzt z.B. eine regular DLL ist, dann wird
> automatisch InitInstance() der MFC DLL aufgerufen. Das ganze im Kontext
> der DllMain.
> Frage: Warum knallt es nicht öfters, denn im DllMain Kontext werden
> massenweise CRT aufrufe und Objekt Initialisierungen gemacht?
>

Also mal nachgelesen:

Der Loader besitzt eine Liste, in der die Reihenfolge der DLLMain-
Funktionen vermerkt ist. Bei jedem Laden einer DLL packt der Loader
zuerst die importierten DLLs in diese Liste, danach die DLL selbst.
Anschliessend ruft er anhand dieser Liste die DllMain-Funktionen auf,
dieser Prozess ist noch durch ein Synchronisationsobjekt geschützt,
so daß während eines DllMain keine andere DllMain-Funktion aufgerufen
wird.

Das heisst nun:
1) Alle Initialisierungsfunktionen der importierten DLLs sind durchlaufen.
2) Alle Initialisierungsfunktionen von DLLs, die aufgrund von LoadLibrary
geladen werden, werden nicht durchlaufen, es sei denn 1) hat dafür schon
gesorgt.

Somit sind zwei Sachen tabu:
1) Das direkte oder indirekte Aufrufen von LoadLibrary auf DLLs, die nicht
zu den importierten DLLs gehören.
2) Das Aufrufen von Funktionen, die beim statischen Linken gegen die
CRT-Bibliothek den CRT-Heap oder ähnliche CRT-Strukturen benutzen, da
diese erst durch die Applikation initialisiert werden.

Das Problem ist nun, daß man einer Funktion nicht ansehen kann, ob sie
LoadLibrary aufruft. Wenn man alle Kernel32-Funktionen aufrufen darf,
bedeutet dies im Umkehrschluss, dass es garantiert ist, das sie die
Finger von LoadLibrary lassen.

MfG
Andre Stille


Eberhard Schefold

unread,
Jun 9, 2006, 8:47:55 AM6/9/06
to
Andre Stille [MVP] schrieb/wrote:

> Das Problem ist nun, daß man einer Funktion nicht ansehen kann, ob sie
> LoadLibrary aufruft. Wenn man alle Kernel32-Funktionen aufrufen darf,
> bedeutet dies im Umkehrschluss, dass es garantiert ist, das sie die
> Finger von LoadLibrary lassen.

Daß man ungestraft sämtliche Kernel32-Funktionen aufrufen darf, kann
kann ich der Doku zu DllMain allerdings nicht entnehmen, insbesondere
nach folgendem Satz

[...] Therefore, the entry-point function can call functions in
Kernel32.dll that do not load other DLLs.

Suggeriert für mich, daß es durchaus Kernel32-Funktionen gibt, die ein
LoadLibrary auslösen. Welche das sind, darf der geneigte Entwickler wohl
selbst erraten.

Andre Stille [MVP]

unread,
Jun 9, 2006, 9:07:44 AM6/9/06
to
Hallo!

"Eberhard Schefold" <eberhard...@de.bosch.com> schrieb im Newsbeitrag
news:e6bqls$j4p$1...@ns2.fe.internet.bosch.com...


>
> Daß man ungestraft sämtliche Kernel32-Funktionen aufrufen darf, kann kann
> ich der Doku zu DllMain allerdings nicht entnehmen, insbesondere nach
> folgendem Satz
>
> [...] Therefore, the entry-point function can call functions in
> Kernel32.dll that do not load other DLLs.
>
> Suggeriert für mich, daß es durchaus Kernel32-Funktionen gibt, die ein
> LoadLibrary auslösen. Welche das sind, darf der geneigte Entwickler wohl
> selbst erraten.

Hmm... MSDN Feb 2003 (oder haben die da mitlerweile wieder was am Text
verändert?)

Dort gibts erst die Warnung, dass man von LoadLibrary/LoadLibraryEx/
FreeLibrary die Finger lassen soll und danach gibts folgenden Satz:

It is safe to call other functions in Kernel32.dll, because this DLL
is guaranteed to be loaded in the process address space when the entry-
point function is called.

Daraus schliesse ich, dass alle anderen Kernel32.dll-Funktionen ebenfalls
die Finger von LoadLibrary/LoadLibraryEx/FreeLibrary lassen, ansonsten
dürften sie nicht sicher sein.

MfG
Andre Stille


Eberhard Schefold

unread,
Jun 9, 2006, 9:19:36 AM6/9/06
to
Andre Stille [MVP] schrieb/wrote:

> Hmm... MSDN Feb 2003 (oder haben die da mitlerweile wieder was am Text
> verändert?)

War aus der Januar '06, und ist derzeit auch online.

Ich will nicht ganz ausschließen, daß mit den angedeuteten Funktionen
nur LoadLibrary und LoadLibraryEx gemeint sind. Die Formulierung ist
ziemlich unscharf.

Stefan Kuhr

unread,
Jun 12, 2006, 3:26:49 PM6/12/06
to
Hallo beisammen,

"Andre Stille [MVP]" wrote:
>
> <snip>


> Das Problem ist nun, daß man einer Funktion nicht ansehen kann, ob sie
> LoadLibrary aufruft. Wenn man alle Kernel32-Funktionen aufrufen darf,
> bedeutet dies im Umkehrschluss, dass es garantiert ist, das sie die
> Finger von LoadLibrary lassen.
>

Wuerde irgendeine Funktion in kernel32.dll einen LoadLibrary machen auf
eine andere DLL als ntdll.dll, dann muesste ich ernsthaft an jeglichen
Software-Engineering-Faehigkeiten der Jungs bei MS zweifeln. Das waere
ein klarer Verstoss gegen jede Art von sauberem Layering beim
API-Design. Ich erwarte, dass wenn jemand so eine Idee hat, er am "Layer
Court" scheitert
(http://blogs.msdn.com/larryosterman/archive/2005/08/23/455193.aspx).

Oder habe ich das Thema der Diskussion missverstanden?

--
S

Jochen Kalmbach [MVP]

unread,
Jun 12, 2006, 4:19:40 PM6/12/06
to
Hallo Stefan!

> Wuerde irgendeine Funktion in kernel32.dll einen LoadLibrary machen auf
> eine andere DLL als ntdll.dll, dann muesste ich ernsthaft an jeglichen
> Software-Engineering-Faehigkeiten der Jungs bei MS zweifeln. Das waere
> ein klarer Verstoss gegen jede Art von sauberem Layering beim
> API-Design. Ich erwarte, dass wenn jemand so eine Idee hat, er am "Layer
> Court" scheitert
> (http://blogs.msdn.com/larryosterman/archive/2005/08/23/455193.aspx).

Ja. Da geb ich Dir vollkommen recht!

Eigentlich gehört der Satz aus der Doku gestrichen, da sich sonst MS
selber eingestehen würde, dass sie in kernel32.dll vollkommen unsauber
arbeiten...

<quote="http://msdn.microsoft.com/library/en-us/dllproc/base/dllmain.asp">
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.

</quote>

Hab mal ein Feedbak an MS geschickt...

--
Greetings
Jochen

My blog about Win32 and .NET
http://blog.kalmbachnet.de/

Martin Richter [MVP]

unread,
Jun 13, 2006, 2:53:26 AM6/13/06
to
Hallo Andre!

> Somit sind zwei Sachen tabu:
> 1) Das direkte oder indirekte Aufrufen von LoadLibrary auf DLLs, die nicht
> zu den importierten DLLs gehören.
> 2) Das Aufrufen von Funktionen, die beim statischen Linken gegen die
> CRT-Bibliothek den CRT-Heap oder ähnliche CRT-Strukturen benutzen, da
> diese erst durch die Applikation initialisiert werden.

Punkt 2 verstehe ich nicht ganz. Du meinst Callbacks in die eigene
Applikation aus einer DllMain, wenn die CRT statisch gelinkt wird.
Denn bei dynmisch gelinkter CRT erfolgt der Init des Heaps IMHO ja in
DllMain der CRT.

Machen wir es doch mal umgekehrt. Was ist sicher:
Sicher ist auch die Nutzung von user32 Funktionen wenn meine DLL und die
user32 DLL implizit geladen werden.

Gemein wird es ja nur (nach meinem Verstehen) dadurch, wenn eine
Funktion, die ich per LoadLibrary/GetProcAddr eine Funktion in DllMain
nutzen möchte.
Solange ich Funktionen aus anderen DLLs nutze, die implizit geladen
werden, und die wiederum keine Abhängigkeit von meiner DLL haben
(user32, etc.) wird deren DllMain immer zuerst aufgerufen.

Andre Stille [MVP]

unread,
Jun 13, 2006, 5:33:56 AM6/13/06
to
Hallo!

"Martin Richter [MVP]" <martin....@mvps.org> schrieb im Newsbeitrag

news:e6lue7...@news.grutzeck.de...


>
> Punkt 2 verstehe ich nicht ganz. Du meinst Callbacks in die eigene
> Applikation aus einer DllMain, wenn die CRT statisch gelinkt wird.
> Denn bei dynmisch gelinkter CRT erfolgt der Init des Heaps IMHO ja in
> DllMain der CRT.

z.B. malloc bei statischem Linken gegen die CRT.

>
> Machen wir es doch mal umgekehrt. Was ist sicher:
> Sicher ist auch die Nutzung von user32 Funktionen wenn meine DLL und die
> user32 DLL implizit geladen werden.

Nein, das reicht nicht. user32.dll importiert LoadLibraryA, LoadLibraryW
sowie LoadLibraryExW aus der kernel32.dll. Somit gibt es Funktionen in
der user32.dll, die andere dlls laden.

Und es kommt noch schlimmer:
Die user32.dll hat Imports auf 4 andere DLLs, die per delay-load geladen
werden und wieder einen Rattenschwanz von DLLs nach sich ziehen.

Öffne die user32.dll mal mit dem Dependency-Walker, da tauchen gut 100
DLLs als delay-load auf und da delay-load immer LoadLibrary/GetProcAddress
bedeutet, passiert genau das, was nicht sein soll.

So erzeugt z.B. jeder Zugriff der user32.dll auf die Registry ein
LoadLibrary auf advapi32.dll und schon hat man das Problem mit
Dutzenden von nicht-initialisierten dlls.

MfG
Andre Stille


Martin Richter [MVP]

unread,
Jun 13, 2006, 7:07:08 AM6/13/06
to
Hallo Andre!

> Und es kommt noch schlimmer:

Jo! Schlimmer kann es bald nicht mehr kommen.
Da muss man ja wirklich froh sein, dass einige DLLs die mit der MFC
geschrieben wurden überhaupt funktionieren...

Danke für Deine Ausführungen!

Anton Huber

unread,
Jun 14, 2006, 2:57:24 AM6/14/06
to
"Martin Richter [MVP]" threw this exception:

Hallo!

>Ich bin durch einen Thread hier mal wieder auf das leidige Thema
>gestoßen was man alles so in der DllMain darf und was man eben nicht darf.

Was bleibt da eigentlich noch übrig?
Ich ärgere mich zwar eher immer über ProcessDetach ...

Letztlich müsste man bei jeder dll eine eigene
Init und Shutdown-Routine einfügen. Nur, für was
gibts dann überhaupt DllMain?

Oder stolpere ich da gerade über meine eigene
Unfähigkeit?!?


Gruss
Anton

--
Aber wieso? Gestern ging's doch noch!

Jochen Kalmbach [MVP]

unread,
Jun 17, 2006, 2:34:57 AM6/17/06
to

> Hab mal ein Feedbak an MS geschickt...

Hier die Antwort von MS:

Thanks for the feedback. There has been a lot of demand (within and
outside Microsoft) for a comprehensive list of safe functions, but there
isn’t one.

We did recently post a paper that goes into more detail:
http://www.microsoft.com/whdc/driver/kernel/DLL_bestprac.mspx.

I’ll remove this sentence and add a pointer this paper.
["Therefore, the entry-point function can call functions in Kernel32.dll
that do not load other DLLs."]

0 new messages