Ich habe in der letzten Zeit einige COM-PlugIns und Service Komponenten entwickelt. Alles Teile von anderen Diensten und TSPs (Tapi Service Provider). D.h. alles ohne UI. Die ganze Maschinerie, die ich hierzu verwendete befand sich auf einem Windows 2003 R2 Server. Aufgrund bestimmter Hardware war ein virtueller Server zum Testen nicht drin.
Macht ja nix. Man kann ja auch mit Remote Desktop auf dem Server vom eigenen Platz aus arbeiten, ohne deshalb im klimatisierten und immer zu kaltem und außerdem viel zu lautem Serverraum zu arbeiten…
Ziemlich schnell nervte mich gleich ein bestimmtes Problem. Ein Service mit einer meiner Komponenten stand auf einmal. Ich habe mich mit dem Debugger remote attached und merkte mehr oder weniger schnell, dass ein bestimmter Thread (von 67) auf einen ASSERT gelaufen war. Dämlicher Weise hatte der nun kein DebugBreak ausgelöst. Genaugenommen stand der Thread in einer MessageBox mit dem ASSERT Fenster, dass jeder kennt.
Da ich aber per Remote Session mit dem Server verbunden war sah ich diese nicht. Wäre ich am primären Monitor angemeldet gewesen, hätte mich die MessageBox erreicht, dafür trifft die CRT Vorsorge.
Dämlich! Mir wäre sogar ein Crash (mit Minidump natürlich) lieber gewesen. So stand der Service blockierte noch drei andere Sachen und es dauerte doch einige Zeit bis ich diesen stehenden Service als Ursache ausmachen konnte. Wäre der Service gecrasht hätte ich es in Sekunden mitbekommen.
OK! Wie gestalte ich das System nun um, dass ein ASSERT immer einen DebugBreak auslöst und keine MessageBox, die sowieso keiner zu sehen bekommt?
Das würde einen Minidump schreiben und wenn ich mit dem Debugger verbunden wäre, würde es sofort das System an der entsprechenden Stelle stoppen. Die MessageBox mit dem ASSERT brauche ich nicht.
Ein wenig Lesen in der CRT Doku schadet nicht. Also hier die Lösung:
Schritt 1: Wir verhindern, dass die entsprechende MessageBox erscheint und stellen entsprechend ein, dass der ASSERT in der Debug Ausgabe mit protokolliert wird. Und wenn man es hat auch noch will, zusätzlich in einer Protokolldatei.
_CrtSetReportMode(_CRT_ASSERT,_CRTDBG_MODE_DEBUG/*|_CRTDBG_MODE_FILE*/);
Schritt 2: Nun brauchen wir noch einen DebugBreak, der immer ausgelöst wird. Auch das ist kein Problem. Wir benutzen den Debug Report Hook:
_CrtSetReportHook2(_CRT_RPTHOOK_INSTALL, MyDebugHook);
MyDebugHook ist nun nichts weiter als eine kleine Funktion die nur eins enthält: den Aufruf der Funktion DebugBreak();.
So ausgestattet lassen sich Services im Debugmode weitaus besser entwickeln. Jetzt sorgen Sie wenigstens für einen anständigen Crash (natürlich mit Dump), wenn es ASSERTet…
Hallo Herr Richter,
durch Zufall bin ich auf Ihren Blog gestoßen.
Sie habe sehr viele interessante Informationen.
Beim ausprobieren des Tipps „Debugging und ASSERTs in Services“
fiel mir ein kleiner Fehler auf:
Die Zeile:
_CrtSetReportHook2(_CRT_ASSERT, MyDebugHook);
müsste eigentlich so aussehen:
_CrtSetReportHook2(_CRT_RPTHOOK_INSTALL, MyDebugHook);
Ich freue mich auf wietere Tipps, mit freundlichen Grüßen
Peter Trzeszkowski
Herzlichen Dank für die Korrektur, das ist wirklich ein Fehler. Ich habe meinen Artikel korrigiert…