...someplace, where there isn't any trouble? Do you suppose there is such a place, Toto?

Die Antwort auf die Frage "Warum hat CWnd keine virtual functions für Windows Messages?"

Um auf die Antwort auf diese Frage zu kommen, muß man historisch schon etwas bewandert sein. So muß man wissen, daß die ersten Versionen der MFC natürlich nicht für Win32 geschrieben waren sondern für Win16, also für dasjenige Programmiermodell, das die Ausführung von kooperativen Anwendungen unter Windows 3.1++ gestattete. Solcherlei Binaries hatten denselben Aufbau wie die Ende der Achtziger und Anfang bis Mitte der Neunziger stark verbreiteten MS-DOS Binaries. MS-DOS war ein OS, das den Intel 8086/80286 benötigte und in dessen "Real Mode" lief, weshalb der Speicher in sogenannten Segmenten verwaltet wurde, von denen ein jedes 64 kByte groß war. Geringer Speicherbedarf war Trumpf, denn wenn eine Anwendung wenig Speicher benötigte, konnte sie selber mehr Daten verwalten und ließ auch zu, daß so Goodies wie TSR-Programme oder Netzwerkstacks (gasp!) nebenherliefen. Eine andere Eigenheit des Real Mode war, daß Adressen entweder 16 bit breit (NEAR Pointer) sein konnten oder 32 bit breit (FAR Pointer), und die Adressarithmetik, die für FAR Pointer notwendig war, etwas Performance kostete. Also wollte a jeda seine Programme so schreiben, daß möglichst NEAR Pointer verwendet wurden und daß die logischen Segmente für Code, Daten und Stack möglichst klein sein konnten und am Besten in ein- und daselbe physikalische Segment passten.

Durch die damals vorherrschenden Entwicklungswerkzeuge Microsoft C, Microsoft Quick C, Borland Turbo Pascal und Borland Turbo C, später dann MS Visual C 1.0/1.5/1.51/1.52 und Borland C in verschiedenen Versionen, wurde diese segmentierte Architektur durch sogenannte Speichermodelle unterstützt. Durch die Wahl des Speichermodells legte man fest, wie die logischen Segmente auf die physikalischen Segmente verteilt wurden und wie deswegen für die einzelnen Zugriffe die Pointer aussahen, NEAR oder FAR. Dabei gab es folgende Auswahl:

  • Tiny: Code und Daten werden über NEAR Pointer adddressiert und sind in einem einzigen physikalischen Segment kombiniert. Mit dem exe2bin-Programm kann man eine .com Datei aus der exe erstellen, das Binary ist kleiner als 64kByte und läuft nur unter DOS, nicht unter Windows.
  • Small: Code und Daten werden über NEAR Pointer adddressiert und sind in je einem physikalischen Segment. Das Binary läuft unter DOS und Windows.
  • Medium: Code wird über FAR Pointer und Daten werden über NEAR Pointer adddressiert. Das Binary läuft unter DOS und Windows.
  • Compact: Code wird über NEAR Pointer und Daten werden über FAR Pointer adddressiert. Das Binary läuft unter DOS und Windows.
  • Large: Code und Daten werden über FAR Pointer adddressiert. Das Binary läuft unter DOS und Windows.
  • Huge: Code und Daten werden über FAR Pointer adddressiert. Zusätzlich können Large Pointer verwendet werden, die auf einen zusammenhängenden Speicherbereich > 64kByte zeigen, aber sehr teure Zeigerarithmetik bedingen. Das Binary läuft unter DOS und Windows.

Und was hat das jetzt mit den virtuellen Methoden von MFCs CWnd-Klasse zu tun? Ganz einfach: Jede Klasse, die virtuelle Methoden definiert oder redefiniert, impliziert damit eine Tabelle von Funktionszeigern, die sogenannte vtable, die irgendwo im Adressraum des Prozesses zugänglich sein muß. Wenn nun die Basisklasse aller Fenster Hunderte von virtuellen Methoden hätte, hätte das für alle abgeleitete Klassen natürlich auch riesenhafte vtables zur Folge, wenn diese eine virtuelle Methode definieren oder redefinieren. In letzter Konsequenz bedeutet das dann, daß man mit der Wahl des Speichermodells auf Large beschränkt ist, und das wollte man bei MS offensichtlich nicht, weshalb man die platzsparenderen Message Maps statt virtueller Methoden für Messagehandler einführte. Und so kam es, daß eine vom damaligen "App Wizard" erzeugte Standardapplikation mit full-blown Document View-Model die Speichermodelle Medium und Large verwenden konnte. Frameworks, die Message-Handler mit virtuellen Methoden abbildeten, waren auf Large beschränkt.

So, und nun braucht es auch nimmer viel, um die Frage nach den virtuellen Methoden für Property Sheets und Property Pages zu beantworten: Sie wurden eigentlich erst so richtig in Win32 etabliert und kamen erst mit der letzten Version der MFC, der Version 2.52b, als Backport in die Win16-Version der MFC. Und viele virtuellen Methoden sind außerdem ohnehin nur für den Wizard-Mode von Property Sheets erforderlich, der für die Win16-Version nie angeboten oder portiert wurde.

Abhängigkeiten von PE Binaries grafisch darstellen

Letzte Woche habe ich beschrieben, wie man aus .dsw-Dateien die Projektabhängigleiten grafisch darstellen kann. Der Code für dieses kleine Projekt kann aber nur die Abhängigkeiten eines Projekts darstellen, wie von den Entwicklern einmal so festgelegt wurde. Das Problem ist aber, daß sich mit der Zeit üblicherweise die Abhängigkeiten ändern, etwa durch Ergänzung neuer header oder libraries. Gerade wenn man so gefährliche Pseudo-Zeitsparfeatures von VC wie

#pragma comment (lib)

verwendet, passiert sowas schnell 'mal. Mit Glück wird dann ein einzelnes Projekt in einem dsw-File im Rahmen eines Komplettbuilds dann allenfalls über die alphabetische Reihenfolge korrekt gebuildet. Das ist für einen daily build zwar noch akzeptabel, aber für einen Maintenance-Entwickler nicht gerade das "warm and fuzzy feeling", das er braucht, um zu wissen, daß er keinen Rebuild von Allem (wie im daily build) für eine unschuldige kleine Änderung in einem Source File braucht.

Also, was kann man tun, wenn die Abhängigkeiten in einem .dsw-File mit 139 dsp-Files drin hoffnungslos verwahrlost sind und hinten und vorne nicht stimmen? Ganz einfach: Die Abhängigkeiten über einen korrekten daily build bestimmen. Die Abhängigkeiten sind nämlich - da staunt der Fachmann und der Laie wundert sich - just kidding! - natürlich auch in den gebuildeten Dateien versteckt. Schließlich muß ja der Loader von NT auch wissen, gegen welche Dateien ein PE-Binary implizit linkt. Diese Informationen kann man sich mit Hilfe eines der Schweizer Offiziersmesser einer jeden Visual C-Installation beschaffen, mit dumpbin.exe.

So kann man sich mit

dumpbin /imports dateiname

Die Importe der Datei dateiname anzeigen lassen und so ist in den letzten beiden Tagen am Feierabend und in der Mittagspause ein Tool entstanden namens bindep.exe, das man hier runterladen kann. Es geht davon aus, daß dumpbin über die Environment-Variable PATH gefunden wird und ruft für ein per Kommandozeile anzugebendes Verzeichnis für eine beliebige Anzahl von Wildcard-Patterns den dumpbin auf. Das Outputfile ist auch über die Kommandozeile anzugeben und ist natürlich ein dot file, das man dann dem dot-tool aus den Graphviz-Tools von AT&T einfüttern kann, so wie in meinem Posting von letzter Woche beschrieben. Man ruft das Tool also beispielsweise so auf:

bindep c:\windows c:\temp\hudel.dot /r *.exe *.dll

Diese Kommandozeile startet dieses Tool und läßt es rekursiv für alle EXE- und DLL-Dateien in c:\windows das dot file c:\temp\hudel.dot erzeugen. Dabei werden diejenigen Dateien, die per Delayload gelinkt werden, mit einer grauen Verbindungslinie gemalt, ansonsten in Schwarz. Das Ergebnis schaut dann beispielsweise für ein XP-System so aus.

Weil das Ganze aber natürlich sehr unübersichtlich werden kann, besteht auch die Möglichkeit, einzelne Dateien von der Betrachtung auszuschließen. Zu diesem Zweck legt man eine Textdatei an und gibt auf jeder Zeile den Namen einer auszuschließenden Datei an. Diese Datei nennt man excfile.txt und legt sie in dasselbe Verzeichnis wie die Datei bindep.exe. Für das eigene Softwareprojekt schreibt man also da die üblichen Verdächtigen wie kernel32.dll, advapi32.dll, user32.dll, etc.. und sieht dann nur noch die Abhängigkeiten seiner eigenen Exe-Dateien und DLLs untereinander. Für mein allseits beliebtes SUperior SU kommt dann beispielsweise sowas raus:


(Ein Klick öffnet das Ganze als pdf)

Wenn man nun noch einzelne Verzeichnisse vom Durchsuchen ausschließen will, macht man dasselbe wie mit der Datei excfile.txt aber nennt die Datei excdirs.txt und schreibt da die absoluten Pfade für nicht zu untersuchende Directories rein.

DISCLAIMER: Der Code in dem hier verlinkten Projekt ist lausig, schlimm und schlecht. Es ist eine üble Melange aus K&R-C, MFC, nicht abgefangenen Fehlern und Exceptions. Das ist nicht die Art und Weise wie ich Code schreibe, wenn man mich dafür bezahlt. An dieser Stelle halte ich mich an meinen treuen, braven Depp-uty der hier sagen würde: Der Proof-Of-Concept reicht!

Hinweis: Wer wirklich das Ganze für eine Windows-Installation ausprobieren will, sollte eine halbwegs neue Version von dumpbin verwenden, beispielsweise den von Visual Studio 2005. Der dumpbin von VC6 hat Probleme mit Binaries von neueren OS-Versionen.

Warum hat CWnd keine virtual functions für Windows Messages?

Diese Frage mußte ich mir 1994 mal stellen lassen bei meinem allerersten Arbeitgeber als Entwickler, als es darum ging, verschiedene plattformunabhängige Windowing-Framework Class Libraries zu vergleichen und diese dann wiederum mit den MFC in punkto Leistungsfähigkeit zu vergleichen. Ich wußte darauf nur mit einem Achselzucken zu antworten und daß die MFC halt eben diese ominösen Message Maps verwenden um ein CWnd (genauer gesagt: ein CCmdTarget) auf eine Windows Message vom Typ WM_SCHLAGMICHTOT reagieren zu lassen. Aber warum denn nur? War es nicht ein Zeichen von Objektorientierung, daß die plattformunabhängigen Klassenbibliotheken ihrem CWnd-Pendant für diese Zwecke jeweils eine virtuelle Methode spendierten, die man in einer abgeleiteten Klasse nur redefinieren mußte um einen Message-Handler zu haben?

Und später, als in MFC die Property Sheets und Property Pages zu den MFC hinzukamen, warum haben die Leute im MFC-Team dann plötzlich die Kehrtwende eingeleitet und für jeden Pups, der über WM_NOTIFY an der Property Page ankam, eine virtuelle Methode eingeführt, statt die traditionellen Message Maps zu verwenden?

Die Antworten gebe ich jetzt erstmal nicht, aber wer eine Vermutung hat, wie man diese Fragen korrekt beantworten könnte, ist hiermit eingeladen, einen Kommentar abzugeben.

Projektabhängigkeiten in VS6 dsw-files darstellen

Projektabhängigkeiten in VC6 Workspace Files (dsw files) sind mitunter nicht so einfach zu erkennen. Wenn die Zahl der dsp files darin dann so in die Hunderte zu gehen scheint wie bei einem besonders prominenten dsw file bei επτ€σ, wird's Zeit, das Ganze aufzubrechen, so daß es einzelne Personen vielleicht wieder 'mal überblicken können. Drum war die Überlegung von SNB, Tommy de Markolf und mir, das Ganze erstmal graphisch überblicken zu wollen um dann den Split zu machen, wenn Muster von Abhängigkeiten zu erkennen sind und Redundanzen beseitigt sind. Ein erster Schritt dazu ist dieses Tool, mit dem man sich ein .dot file erzeugen kann wie folgt:

dswdep dswfile c:\temp\mydot.dot

Dieser Aufruf in einer Konsole erzeugt aus dem .dsw file "dswfile" ein dot-file c:\temp\mydot.dot.

Das File c:\temp\mydot.dot kann man dann dem tool dot.exe aus dem AT&T graphviz package wie folgt einfüttern:

dot -Tpng c:\temp\mydot.dot -o c:\temp\mydot.png

Damit wird nun ein png file unter c:\temp\mydot.png erzeugt, das die Abhängigkeiten der einzelnen dsp files im dsw file das dem dswdep tool als erstem Parameter übergeben wurde, sehr schön graphisch darstellt.

Für mein allseits immer wieder gern gesehenes SUperior SU wird damit beispielsweise folgende Graphik erzeugt (macht nur wirklich Sinn jetzt, auf das Bild zu klicken und in großer Auflösung zu betrachten, ehrlich!):

Da man ja bei so einem Projekt normalerweise immer ein "top-level" dsp hat, wie bei mir das "allbuild", macht es wahrscheinlich am meisten Sinn, die Grafik darzustellen ohne das top-level dsp file (denn das wird ja durch's Aufsplitten überflüssig), aber dazu bin ich jetzt zu müde, vielleicht liefere ich das hier noch nach.

Woher kommt eigentlich der Begriff "Booten"?

Wo ich schon beim schlaumeiern bin jetzt noch die Erklärung des Begriffs "Booten". Das kommt vom englischen "Bootstraps", also den "Stiefelriemen" oder "Stiefelschlaufen", nicht zu velwechsern mit den "Bootlaces", den Schnürsenkeln. Als ich noch ein junger (!) Mensch war und studierte, hatte ich den ehrenwerten Professor Baeger ohne ä und mit ae (den besten Prof, den ich überhaupt je hatte), der seine Studenten über die Etymologie (ετυμολογία für den Löselic) dieses Begriffs aufklärte, uns aber gleichzeitig anhielt, den deutschen Begriff "Urladeprogramm" statt "Bootstrapper" zu verwenden.

Was hat nun das "Urladeprogramm" oder das "Bootstrap Program" mit irgendwelchen Stiefelriemen zu tun? Ganz einfach: Im Deutschen gibt es ja die Sagen vom Münchhausen und eine dieser Sagen handelt davon, wie sich Münchhausen an den eigenen Haaren aus dem Sumpf zieht. Eine ähnliche Sage gibt es im Englischen Sprachraum (oder im Amerikanischen? SBryant to the rescue?), wo ein Männchen zum Mond will und deswegen immer wieder seine Stiefelriemen solange aneinander bindet, wieder aufbindet und wieder aneinander bindet und daran währenddessen hochklettert, bis es am Ende beim Mond angelangt ist. Die Wikipedia hingegen sagt schlicht dazu, daß in der amerikanischen Fassung des Münchhausen er sich an den Stiefelschlaufen aus dem Sumpf zieht. Was auch immer richtig ist, man bekommt jetzt vielleicht so eine Ahnung woher der Begriff kommt.

Grob vereinfacht ist also der Bootstrapper ein simples Programm, das sehr viel kompliziertere Dinge, wie etwa den Start eines so komplexen Programms wie eines Betriebssystems gestattet, indem es sich irgendwie magisch selbst hochzieht, oder jedenfalls so ähnlich. Mit der Zeit ist das Ganze dann vom "Bootstrapping Programm" zum "Bootstrapper" und schliesslich zum Verb "Booten" verballhornt worden und da ist es wo wir heute stehen.

<< 1 ... 33 34 35 36 37 38 39 40 41 42 43 ... 46 >>