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.

Die Manifest Pest…

Hier noch mal ein paar wirklich nette Sachen, die zeigen warum ich Manifeste mittlerweile  nicht mehr ganz so mag (um es wirklich vorsichtig auszudrücken):

Was passiert eigentlich wenn man ein internes UND ein externes Manifest zu einer DLL oder EXE hinzufügt?

  1. Auf einem XP Rechner wird der Lader das externe Manifest bevorzugen, das interne Manifest wird ignoriert.
  2. Auf einem Windows 2003 oder Vista Rechner wird das interne Manifest bevorzugt und das externe Manifest wird ignoriert.
  3. Handelt es sich um eine DLL die mit LoadLibrary geladen wird, dann wird niemals ein externes Manifest berücksichtigt, selbst wenn kein internes Manifest vorliegt. Hier verhalten sich XP, 2003 und Vista gleich.

Ein Grund mehr zu meiner Empfehlung: Verwende immer nur interne Manifeste!

Was man beim Umstieg von VSS auf TFS bedenken muss!

Ich plane in der nächsten Zeit Visual Source Safe (VSS) abzulösen. Da wir alle Hauptprojekte (bis auf einigen legacy Kram) auf VC++ 2005 bearbeiten und die entsprechenden Visual Studio Team System Lizenzen haben beabsichtigen wir auch den Team Foundation Server (TFS) einzusetzen.
Einen Team Foundation Server kaufen müssen wir auch nicht, denn wir haben nicht mehr als 5 Clients.

Vorteile:

  • Integration auch in ältere Systeme ist kein Problem! Für VC6, VB6, VS.NET 2003 gibt es entsprechende Plug-Ins! Das ist schon mal genial. Also können wir theoretisch sogar alle legacy Projekte konvertieren und mit den alten Tools weiterarbeiten.
  • Alles wird in einem SQL-Server gehalten. Endlich!
  • Die Projekt Dokumente sind gleich in dem entsprechenden Share Point mit gehalten (leider noch WSS 2.0)
  • Einchecken mehrere Dateien werden direkt zu einem Vorgang zusammengefasst.
  • Direktes Bug-Tracking und einfache Projektierung ist möglich ohne externe Hilfsmittel.

Beim evaluieren der Features habe ich mir schon die Hände gerieben. Allerdings gibt es da eine wirkliche fette Kröte die ich schlucken muss. Mein geliebtes „Sharing“ unter VSS gibt es unter dem Source Control System von TFS nicht ❗

Wir haben einiges an gemeinsamen Code, den wir nicht in Libraries oder DLLs zusammengefasst haben. Da sind einige nette Template Klassen etc., spezielle Header und andere nette Code-Snipplets. Alle diese Code Teile waren in einem kleinen Projekt zusammengefasst und jeder Entwickler konnte diese wahlfrei in sein Projekt sharen. Das ist und war extrem bequem. Mit dem Drawback, dass eine zentrale Code Änderung alle Projekte beeinflusst und evtl. bricht kann man leben.
Man muss einfach regelmäßig Ohrfeigen verteilen für Entwickler die nicht testen und Kerker bei Brot und Wasser androhen… 😉

Unter TFS ist es damit vorbei. Einziger Ausweg wie ich ihn aktuell sehe ist es, die entsprechenden Codeteile einzubranchen in die anderen Projkete. D.h. aber das man die aktuelle Version (und den evtl. gewünschten Fix) nur mit einem Merge bekommt, was wieder nicht unbedingt automatisch geht, man muss also Hand anlegen was man evtl. eben auch vergisst.

Mal sehen vielleicht fällt mir da noch was anderes ein. Anonsten sieht das ganze sehr vielversprechend aus.

Mein erster Codeproject Artikel…

Nun habe ich es endlich mal geschafft und meinen ersten kleinen Artikel auf Codeproject geschrieben:

http://www.codeproject.com/cpp/PrivateAssemblyProjects.asp

 Er schließt sich nahtlos an über alles das was ich hier achon über die Manifest-Hölle geschrieben haben…

EXCHANGE: Jeder hat Rechte auf jedes Postfach…

Upps. Wie das? Keiner hat es bemerkt aber seit ein paar „Monaten“ scheint jeder bei uns volle Rechte auf jedes Postfach zu haben! 😮

Das ist nicht im Sinne des Erfinders, ganz und gar nicht!

Also suchen… aber wo. Mit der Hife von Frank Carius (MVP für Exchange und Herr über die Seite MSXFAQ.DE – Franks MSExchangeFAQ) bin ich dem übel auf die Spur gekommen.

Bestandsaufnahme:
Bei den Rechten auf dem Server in meiner Administrativen Gruppe sehe ich das Jeder die Rechte Send As und Receive As hat. Das soll aber nicht sein. Und noch schlimmer: Die Rechte werden geerbt…
Also klicke ich mich immer höher in der Hierarchie und hoffe bald den entsprechenden Eintrag zu finden. Aber da ist kein Eintrag. Und auf der Ebene der Organisationseinheit finde ich keine Eigenschafts Seite mit Sicherheit.

Oh Mann! Wer weiß denn schon, dass man diese Seite in den Eigenschaften auf der Ebene der Organisations Einheit nur sieht wenn man in der HKCU einen bestimmten Eintrag macht? Wohl nur ein Exchange Guru und das bin ich wirklich nicht…
Also unter HKEY_CURRENT_USER\Software\Microsoft\Exchange\EXAdmin den DWORD Eintrag ShowSecurityPage anlegt und dort 1 eingetragen.

Siehe da. Nun habe ich auch auf der Ebene der Organisation unter den Eigenschaften eine Sicherheits Seite! Und was finden wir hier? Die entsprechenden gesetzten Rechte, die irgendein </zensiert>*$%§5!%2%“!</zensiert> dort gesetzt hat. Also aus dem Recht für Jeder zwei Häkchen entfernt.

Nun sollte man nicht ungeduldig werden. Erst nach 30 Minuten hatte Active Directory die Änderung durch und der Exchange verhielt sich jetzt wieder „Privatsphäre schützend“. 🙂

Automatsiches Update des Thunderbird unter Vista

Ich benutze bereits seit Jahren den Thunderbird als Newsreader, weil ich die Macken des Outlook Express einfach satt war. Zudem ist das Quoteverhalten des Thunderbird einfach und vorbildlich.

Jetzt wunderte ich mich in den letzten Tage, dass ich immer wieder gesagt bekommen habe, dass das neue Update 1.5.0.12 verfügabr ist, auf meinem XP SP2 Rechner war das Update im ersten Anlauf installiert. Aber immer, wenn ich das automatische Update gestartet habe auf meinem Vista Laptop, meinte er nur es würde installiert, wenn ich den Thunderbird neu starten würde. Aber Pustekuchen.

Nach dem dritten Fehlversuch habe ich mir gedacht:
Na Vista wird dem Thunderbird ja nicht einfach Zugriff geben und gefragt nach einem elevated Programmstart wurde ich auch nicht. Und die ACL für die Thunderbird Verzeichnisse sind auch nicht verändert.

  • Also einfach den Thunderbird mal als Admin gestartet.
  • Update ausgewählt.
  • Aufforderung zum Neustart abgewartet.
  • Thunderbird neu gestartet.
  • Bingo!

Hier sollten die Programmierer des Thunderbirds einfach mal den ShellExecute(Ex) mit einem RunAs Verb verwenden. Dann würde das Update auch wirklich automatisch durchgeführt, nach dem Elevate-Prompt.

Siehe dazu auch meinen Eintrag hier: http://blog.m-ri.de/index.php/2007/05/09/wie-einen-prozess-unter-vista-mit-gehobenen-rechten-starten/

Was UAC besser verdaulich gemacht hätte!

In den Einstellungen für einen Programm-Link fehlt die Einstellung Mit normalen Benutzerrechten starten. Von mir aus könnte diese Option auch in den Kompatibilitätseinstellungen drin sein.

Wie es die Option „Als Administrator ausführen“ gibt, fehlt eben das Gegenteil:
Führe das Programm als normaler Benutzer aus.

Was soll diese Option bewirken? Diese Option soll das Programm so starten, wie es unter Windows 2000 oder XP unter normalen Benutzerrechten (ohne Adminrechte) auch laufen würde.

D.h. es würde auch das Admin-Token nicht zugewisen bekommen und bei einem Zugriff schreibend auf HKLM oder C:\Program Files, würde das Programm einfach „Kein Zugriff“ gemeldet bekommen. UAC würde für diesen Fall einfach außen vor bleiben.

Alle Programme, die ich geschrieben habe, kommen damit klar. Eigentlich ärgerlich ist ja nur, dass diese Programme unter Vista nun auf einemal schreiben könne, auch wenn es eben dann nur im Virtualstore landet.

Tausende von Kompatibiltätsproblemen durch das stille Virtualisieren könnte man so umgehen.

Konfussion mit einem RTF Control und EM_STREAMIN/EM_STREAMOUT

Beim Umstellen eines größeren Projektes auf Unicode hatte ich beim Testen einen Crash. Nichts ungewöhnliches.

Es betraf das Ein- und Ausstreamen eines RTF Controls. Zum einen wurden RTF Daten bzw. auch Textdaten in einem Stream übergeben bzw. ausgelesen. Erste Überraschung EM_STREAMIN/OUT mit SF_TEXT will nie Unicode und liefert nie Unicode! Und hier war mein Denkfehler.

Ein Test zeigte folgendes Verhalten mit den unterschiedlichen Streammodes:

  • SF_RTF liefert/will immer einen 8bit Daten Stream, den man in einem CStringA Buffer leicht speichern kann.
  • SF_TEXT liefert/will auch immer einen 8bit Datenstream, ganz egal ob das Control CreateWindowW oder mit CreateWindowA erzeugt wurde.
  • SF_TEXT|SF_UNICODE liefert/will auch immer einen Unicode Datenstream, ganz egal ob das Control CreateWindowW oder mit CreateWindowA erzeugt wurde.

Auch etwas irritierend war für mich die Dokumentation, aber durch aus korrekt.
Die EditStreamCallBack Funktion bekommt laut Doku immer eine Byte Zahl für die Buffergröße mitgegeben. Und es sind wirklich immer Bytes! Keine Anzahl von Zeichen/Buchstaben. Wenn man also Daten mit SF_TEXT|SF_UNICODE gestreamed werden, dann bekommen wir die Anzahl der Bytes. Die Anzahl der Buchstaben ist natürlich nur halb so groß wie die Anzahl der übergebenen bzw. zu übergebenden Bytes.
Eine Warnung bzw. ein erklärender Hinweis wäre an dieser Stelle sicherich auch angebracht.

_MFC_NOFORCE_MANIFEST und _ATL_NOFORCE_MANIFEST

In meinem Blog habe ich bereits über Libraries und die Verwendug von _CRT_NOFORCE_MANIFEST geschrieben (siehe Link unten).

Wenn man nun eine Library erzeugt, die die MFC oder die ATL benutzt, sollte man sich auch noch der beiden Defines _MFC_NOFORCE_MANIFEST und _ATL_NOFORCE_MANIFEST bewusst sein. Diese beiden Defines verhindern, dass durch die Verwendung der ATL bzw. MFC Include-Dateien #pragma comment(linker,”/manifestdependency:..”) Statements erzeugt werden.

Werden diese Defines konsequent verwendet, dann hat der Benutzer der Library die volle Kontrolle welche CRT, MFC bzw. ATL Version angebunden wird.

Warum man sich mit diesen Defines beim erzeugen einer Library auseinenadersetzen sollte kann man in diesem Artikel nachlesen: Warum man seine Libraries mit _CRT_NOFORCE_MANIFEST erzeugen sollte!

❗ BTW: Durch diese Defines kann man allerdings nicht verhindern, dass überhaupt Manifest-Einträge erzeugt werden. Selbst wenn man sein Programm mit den entsprechenden Defines kompiliert. Die Objektdateien haben dann zwar keine Manifest-Einträge, aber spätestens in dem Moment, in dem man das Programm linkt werden aus der CRT, MFC bzw. ATL Libraries Objektdateien gezogen die wieder entsprechende #pragma comment(linker,”/manifestdependency:..”) Einträge haben. Entsprechend bekommt das Manifest Tool dann auch Futter. Dazu mehr in einem späteren Artikel 🙂

Wie findet die MFC 8.0 eigentlich die MFC80lll.DLL Dateien?

Es ist vielleicht schon machen aufgefallen, dass auch die sprachabhängigen Ressourcen-Dateien der MFC als Side by Side Assemblies installiert werden durch VCREDIST_X86.EXE.
Schaut man sich an wie ein Programm erzeugt wird, dass die MFC80.DLL/MFC80U.DLL verwendet, dann weiß man wie Manifeste aussehen die für das entsprechende Programm erzeugt werden. Wir finden im Allgemeinen drei Dependency-Einträge für die MFC, die CRT und die ComCtl32 DLL.

Erstaunlicherweise wird kein Eintrag für die Sprachdateien erzeugt und scheinbar auch nicht benötigt. Denn wenn man das Programm im Debugger auf einem Deutschen XP oder Vista startet sieht man in der Debug-Ausgabe, dass die entsprechende Datei MFC80DEU.DLL aus dem Side by Side Storage geladen wird.

Ein Blick in die MFC80(U).DLL zeigt auch ein Manifest. Dieses passt genau auf die Sprachdateien. Allerdings ist dieses Manifest nicht aktiv, denn es hat keine „gültige“ ID (1000), die das Betriebsystem veranlassen würde es zu berücksichtigen. Zudem hätte dieses Manifest auch nur einen Effekt, wenn die Sprach-DLLs implitzit geladen würden.
Diese Dateien werden aber explizit geladen. Der Code der dafür verantwortlich ist wird zuallererst in der DllMain und der Initialisierung der MFC80-DLL und ein zweites mal in CWinApp::InitInstance aufgerufen.

Der entsprechende Code dort, lädt das Manifest aus der DLL, erzeugt einen Aktivierungs-Kontext und führt dann den entsprechenden AfxLoadLibrary Aufruf durch. Anschließend wird der Kontext wieder zerstört. Wird die Sprach-DLL nicht gefunden wird ein zweites Mal versucht die Datei nun aus dem Programmverzeichnis bzw. über die normalen Suchpfade zu laden.

Diese Methode über das eingebaute Manifest lässt sich nicht aushebeln. Verwendet man selbst eine Technik, die die MFC-DLLs als private Assemblies lädt, dann versucht die MFC dennoch die Sprach-DLLs Side by Side zu laden. Das Ganze ist etwas ärgerlich, wenn man private Assemblies pur verwenden möchte. Einziger Trick wäre hier CWinApp::LoadAppLangResourceDLL zu überschreiben, aber das verhindert nicht den ersten Aufruf aus der DllMain der MFC-DLL heraus.

❗ Wer sich das Ganze genauer ansehen will, sollte sich die Datei appcore.cpp mal ansehen.
Entscheidend hier sind die Funktionen: CWinApp::LoadAppLangResourceDLL und AfxLoadLangResourceDLLEx.

❗ Schaut man sich die MFC-DLLs übrigends noch genauer an, dann stellt man fest, dass sie gar keine Manifeste enthalten. Und das ist auch gut so, sonst wäre es ja auch nicht möglich die MFC und die zugehörige CRT als Private Assembly zu laden. Die MFC verwendet also immer die CRT, die im Kontext der EXE geladen wird.