MFC 8.0 PDB Dateien mit und ohne Source Informationen

Gestern hat es mich überrascht, dass ich beim debuggen auf einmal nicht mehr in eine MFC Funktion mit der F11 Taste steppen konnte. Er sprang immer über diese Zeile. Selbst über die Assembler Ansicht war es nicht möglich die entsprechenden Source-Dateien der MFC im Debugger durch zu steppen.

❓ Eigentümlich. Ein kurzer Blick in die Debug Ausgabe und in die Liste der geladenen Module zeigte, dass die PDB Datei der MFC80UD.DLL aus meinem Symbol-Cache geladen wurden.
In der Modulliste stand als Info: Symbols loaded (source information stripped).
Ich verwende einen zentralen Symbol Cache auf unserem Entwicklungsserver und habe natürlich auch als http://msdl.microsoft.com/download/symbols als Quelle für unbekannte Symbole angegeben.

Scheinbar ist auf irgend einem Weg vom Symbolserver aus dem Netz eine Version in meinen Cache hineingelangt, die nicht alle Debug Informationen enthält. Also gerade die Informationen, die es mir erlauben durch den Sourcecode der MFC zu steppen.

Die entsprechende Version mit den Source Informationen befindet sich durch die Installation von Visual Studio im Verzeichnis C:\Windows\Symbols\dll. Auch in den Optionen für mein Visual Studio war zusätzlich natürlich korrekt auch C:\Windows\Symbols\dll als Pfad angegeben. Die dortigen PDB Dateien wurden jedoch offensichtlich ignoriert. Ein manuelles Nachladen aus diesem Verzeichnis half allerdings sofort.
Bei der nächsten Debug Session wurden jedoch wieder die Informationen aus dem Cache geladen.

Also was machen?

❗ Ich habe einfach die entsprechenden Symbol aus dem C:\Windows\Symbols\dll Verzeichnis in meinen Symbol Cache geladen. Das geht einfach mit dem entsprechenden symstore Befehl:

symstore add /r /f C:\Windows\Symbols\dll\*.* /s <My symbol store>

Und siehe da. Nun werden immer die richtigen PDB-Dateien geladen und Step-Into Befehl F11 verhält sich beim debuggen wieder wie gewohnt.

Bug in CPropertyPage und „Certified for Vista“

Der von mir gemeldete Bug Bug in der MFC71.DLL bzgl. CPropertySheet/CPropertyPage führt dazu, dass ein Programm, dass den genannten Voraussetzungen entspricht, bei den Test für „Certified for Vista“ durchfällt!

Der Application Verifier führt bei den Basic Test einen Page Boundary Check durch. Dabei werden alle Allokationen so durchgeführt, dass sie immer am Ende einer Memory Page erfolgen. Jeder Zugriff über die Grenze hinweg führt dann zu einem Crash.

Im realen Leben ist der Crash wirklich selten, aber mit dem Application Verifier kracht es sofort. Das Problem ist, dass dieser Basic Test Bestandteil von „Certified for Vista“ ist.

Die Folge man rasselt mit 100% Sicherheit durch!

Bug Report hier:
https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=270493

Dämlicherweise wurde auch dieser Bug von Microsoft als „Gelöst (Nicht reproduzierbar)“ markiert. Da kann man wirklich nurmit dem Kopf schütteln.

Und ein kleines Demo findet ihr hier:
Testprogramm für CPropertyPage Bug in MFC 7.1

Der Application Verifier, mein neuer Freund

Durch die Vista Zertifizierung habe ich den Application Verifier von Microsoft als neuen guten Freund kennengelernt. Warum? Schauen wir uns mal den nachfolgenden Code an:

{
  CImageList il;
  il.Create(IDB_IMAGELIST,16,0,RGB(255,0,255));
  m_lcList1.SetImageList(&il,LVSIL_SMALL);
  m_lcList2.SetImageList(&il,LVSIL_SMALL);
  il.Detach();
}

Dieser Code ist natürlich fehlerhaft! Allerdings nicht ersichtlich auf den ersten Blick. Er erzeugt korrekt eine Image List. Setzt diese in ein List View 1 und auch in ein List View 2. Sofern LVS_SHAREIMAGELISTS nicht gesetzt wird die Image List beim Zerstören des List Views 1 freigegeben. Aber eben auch noch mal wenn List View 2 zerstört wird! Nicht gut.

Oder noch ein übles Beispiel:

{
  CImageList il;
  il.Create(IDB_IMAGELIST,16,0,RGB(255,0,255));
  m_lcList.SetImageList(&il,LVSIL_SMALL);
  ImageList_Destroy(il.m_hImageList);
}

Er erzeugt auch korrekt eine Image List. Setzt diese in ein List View und zerstört dann einmal die Image List über das Handle und anschließend noch einmal durch den Destruktor von CImageList. Und ein drittes Mal wird die Image List beim zerstören des List Views freigegeben. Übel übel!

Was passiert wenn dieser Code ausgeführt wird? Nichts…
Natürlich würde keiner so etwas programmieren 😉 . Aber man kann sich vorstellen, dass so etwas ablauftechnisch und programmiertechnisch schon mal passieren kann.
Alles ist scheinbar in Ordnung, obwohl hier eine Zeitbombe tickt.

Wie kommt man einem solchen Bug auf die Spur?
Der Titel dieses Beitrages gibt die Antwort: Der Application Verifier.

Man installiert den Verifier und fügt die Anwendung zu den Verifier Einstellungen hinzu. Man belässt es bei den Default Einstellungen und ergänzt am Besten noch unter Miscellaneous die Checkboxen für DangerousAPIs und DirtyStacks.

Sobald man nun den obigen Code im Kontext eines Debuggers ausführt bekommt man eine Exception! Wow… und wenn man einen entsprechenden Symbolserver hat und den Stacktrace betrachtet sieht man

comctl32.dll!CImageListBase::IsValid()+0x2a bytes
comctl32.dll!_HIMAGELIST_QueryInterface@12()+0x29 bytes
comctl32.dll!_ImageList_Destroy@4()+0x19 bytes

Das Ausgabefenster des Debuggers zeigt zusätzlich:

03D8F964 : Invalid address causing the exception.
75C273A8 : Code address executing the invalid access.
0012F08C : Exception record.
0012F0A8 : Context record.

Aus den Informationen kann man sich leicht denken was hier faul ist, oder sein könnte. Es empfiehlz sich die Anwendung auch im Release Mode mit Debug-Infos zu erzeugen. Das sollte sowieso Standard sein!

Es lohnt sich seine Applikation mal mit dem Application Verifier auszuführen, wenn man mal gerade nichts zu tun hat und man wundert sich dann, an welchen Stellen einem sein – so gut programmierter oder gut gemeinter – Code um die Ohren fliegt :mrgreen:

Für wen Qulitätssicherung kein Fremdwort ist, der kommt an diesem sehr nützlichen Tool nicht vorbei. Der Application Verifier und weitere brauchbare Infos findet sich hier auf der entsprechenden Produktseite von Microsoft:
http://www.microsoft.com/technet/prodtechnol/windows/appcompatibility/appverifier.mspx

Visual Studio 2005 zeigt keine Symbole für die MFC80U/MFC80UD DLLs

Beim Testen auf meinem Laptop wundere ich mich, dass ich nicht mehr in die MFC80UD.DLL tracen kann. Die Routinen der MFC werden einfach übersprungen, auch, wenn ich mit der F11 einen Step Into machen möchte.
Was ist das? 😕

Ich habe auf meinem Rechner einen Symbolserver eingerichtet. Dazu habe ich eine Environment Variable definiert.
_NT_SYMBOL_PATH=symsrv*symsrv.dll*c:\Temp\localsymbols*http://msdl.microsoft.com/download/symbols

Soweit gut. Unter den Einstellungen im Visual Studio unter Tools -> Options -> Debugging -> Symbols, ist nichts eingetragen.
Das führt nun dazu, dass die Symbole aus dem Internet geladen werden. Dort sind jedoch für die MFC-DLLs keine Symbolinformationen vorhanden. Durch die Installation von Visual Studio befinden sich die Symboldateien unter C:\Windows\Symbols\DLL! Dieses Verzeichnis wird aber nicht mehr durchsucht.

Also einfach das Verzeichnis C:\Windows\Symbols\DLL in die Liste der Symbol-Verzeichnisse als erstes eingetragen und siehe da, alles funktioniert wieder wie gewünscht.

Es geht übrigens in VS 2005 noch eleganter als mit der _NT_SYMBOL_PATH Environment-Variable. Einfach in die Liste der Pfade http://msdl.microsoft.com/download/symbols, des schon erwähnten Dialoges, eintragen.
Wichtig ❗ Natürlich hinter dem Pfad C:\Windows\Symbols\DLL.
Auch das Verzeichnis für den lokalen Cache wird hier unter „Cache symbols from symbol server to this directory“ eingetragen.

Tipp: Sollte evtl. im Cache schon eine Version der MFC80 geladen worden sein, dann muss man diese Version evtl. manuell aus dem Cache entfernen, damit die Version aus C:\Windows\Symbols\DLL verwendet wird.

Voraussetzungen für Remote debugging mit MSVCMON (unmanaged)

Ich bin ein Fan von Remote Debugging!
Aber was benötigt man minimal für Remote Debugging auf dem Target auf dem man debuggen möchte (unmanaged Code)?
Das gesamte Remote Debugging Paket zu installieren ist aufwendig und verändert das Zielsystem. Wenn man auch noch bei einem Kunden vor Ort ist auch natürlich ein Eingriff, den man einem Admin erst mal erklären muss.
Geht es also auch mit weniger? JA…

Für VS.NET 2003 braucht man nicht viel, genau genommen 4 Dateien mit einem Datenvolumen von nicht mal 600kb (passt auf jede Diskette, sofern die noch einer benutzt ;-)) :

  • msvcmon.exe
  • msvcr71.dll
  • NatDbgDM.dll
  • NatDbgTLNet.dll

Wenn man nun auf Firewall Probleme verzichtet und Named Pipes als Transportschicht verwendet dann war es das schon. Dann muss man nur noch MSVCMON auf dem Target starten und es kann losgehen. Seit VS.NET 2003 startet MSVCMON ohne Angabe von Parametern mit named Pipes als Transportschicht. Unter Vista muss man nach meinen Erfahrungen immer den -u Parameter mit angeben wenn man Named Pipes verwendet (siehe hier).

Das einfachste ist es nun sich mit VS.NET 2003 remote auf einen laufenden Prozess zu attachen, den man debuggen möchte. Man öffnet dazu aus dem Menü Debug den Punkt Processes, wählt als Transportschicht Named Pipes und den Target PC. Die Liste der Prozesse füllt sich automatisch.
Nun nur noch den Prozess auswählen und auf Attach klicken. Sofern die PDB-Dateien übereinstimmen kann man sofort seine Breakpoints setzen und loslegen.
Das geht teilweise sogar noch, wenn eine UAE Meldung auf dem Monitor sichtbar ist.

Wer unbedingt TCP/IP als Transportschicht wählen will, der muss seit XP-SP2 einiges an der Firewall einstellen. Die zwei nachfolgenden Links geben entsprechende Hinweise:
http://support.microsoft.com/kb/833977/de
http://support.microsoft.com/kb/841177/en-us

Remote debugging mit MSVCMON im Pipe-Mode auf Vista

Man sollte meinen, dass MSVCMON (aus VS.NET 2003) im Pipe Modus unter dem selben User Account sofort funktionieren sollte. Aber dem ist nicht so.

Normalerweise melde ich mich an der Entwicklungsmaschine und dem Remote-PC auf dem gedebuggt werden soll mit dem selben Benutzernamen an. Wird MSVCMON nun ohne Parameter auf dem Remote Computer gestartet kann man normalerweise sofort eine Verbindung herstellen.
Nicht so unter Vista. Dort bekommt man beim Versuch eine Verbindung herzustellen sofort die Meldung: „Unable to connect to ‚DEV-VISTA‘. Zugriff verweigert“, oder auf einem englischen OS „access denied“.

Nach vielem hin und herspielen und dem Versuch MSVCMON im Administrator-Modus zu starten bin ich auf die Lösung gekommen.

Gibt man direkt noch einmal mit dem -u Parameter den gewünschten Usernamen an, dann erlaubt Vista auch den entsprechenden Zugriff.
Also so gestartet MSVCMON -u MeineDomain\MeinUserName hat man keine Probleme und man kann in gewohnter Weise eine Remote Debug-Session starten und auch unter Vista elementar einfach die Programme debuggen.

PS: Es geht in diesem Artikel natürlich um das Debuggen von nativen Programme, sprich unmanaged Code.

Steve McConnell: Code Complete – Deutsche AusgabeDer Second Edition

Dieses Buch ist das Beste was ich jemals zum Thema Softwarentwicklung gelesen habe. Zumindest für mich als aktiven Entwickler und Leiter einer Entwicklungsabteilung. Als Entwickler will man am liebsten programmieren, aber das Buch zeigt anschaulich und in einem netten Stil, dass man ohne ein gewisses Mass an Planung, Architektur und Regeln schnell ein Projekt gegen die Wand fährt. Eindringlich wird der gesamt wirtschaftliche Zusammenhang in einem Projekt beleuchtet und ich konnte bei vielen negativen Beispielen nur nicken, weil ich genau in solche Projekt-Fettnäpfchen mit „Erfolg“ rein getreten bin. Es zeigt wie man ein Projekt gut angeht, ohne Software-Entwurf-Overkill zu betreiben. Es gibt gute Tipps wie man sich selbst Coding-Richtlinien geben kann, die einen vor manchem Bug bewahren. Typische Fallen werden angesprochen und die Möglichkeiten sie zu umschiffen werden gezeigt. Es geht darum, bessere, fehlerfreiere, wartungsärmere, billigere Programme zu schreiben. Das Buch gibt viele Anregungen und Ideen, die auch selbst einem alten Hasen das Leben leichter machen können. Und das ganze ist so praxisnah, dass man viele Dinge sofort übernehmen kann. Die meisten Code Beispiele sind in C++ Code geschrieben, aber im Endeffekt ist es komplett programmiersprachenunabhängig. Das Buch ist einem guten humorvollem Stil geschrieben, der nicht langweilig wird. Wenn ich es nicht besser wüsste, würde ich fast sagen, das Buch wurde ursprünglich auf Deutsch geschrieben. Der Text liest sich flüssig ohne Haken. Auch der gut platzierte Humor ist erhalten geblieben. Ein absolutes Muss und den Preis ist es allemal wert.

5 Sterne

http://www.amazon.de/Code-Complete-Deutsche-AusgabeDer-Second/dp/386063593X