Programmieren mit der Windows-API

Hintergrund

Frage:  Warum sollte man Programme "zu Fuß" mit der Windows-API schreiben, wenn doch so viele schöne Frameworks und GUI-Toolkits zur Verfügung stehen?
Antwort:  Weil Programme mit der Windows-API sau klein sind und keine bzw. kaum externe Abhängigkeiten besitzen. Und natürlich: weil's geht. :-)

Windows-Programme mit der Win16-API bzw. Win32-API zu schreiben hat etwas ursprüngliches. Man bekommt genau das, was man hinschreibt, und keine versteckte Funktionalität. Das hat zwar den Preis, dass man etwas mehr Code schreiben muss, aber den Lohn, dass man genau begreift, warum und wie etwas funktioniert. Weiterhin ist das Programm auf allen Windows-Systemen lauffähig, ohne z.B. irgendeine VBRUN300.DLL mitliefern zu müssen.
Auf dieser Seite geht es primär um die Verwendung der Win16-API unter Windows 3.1. Am Ende wird aber auch gezeigt, wie man das entstandene Programm auf Win32 portiert und unter Windows 10 laufen lassen kann. Das geht nämlich mit überraschend wenig Aufwand!

Exkurs: Grundlagen der Windows-API

Charles Petzold schreibt in "Programming Windows 3.1" schon im ersten Kapitel:

Windows has the reputation of being easy for users but difficult for programmers. If you have no prior experience with programming for a graphical user interface, you should be warned right now that you will encounter some very strange concepts. Almost every programmer who begins writing code for Windows must go through some mental reorientation to assimilate these concepts.

Gemeint ist damit sicher auch die Richtungsumkehr im Programmfluss. In der "traditionellen" Programmierung bindet man fremde Bibliotheken ein und ruft Funktionen daraus auf, kontrolliert aber selbst den Programmablauf. Bei Windows-Programmen bereitet man ein paar Datenstrukturen vor und tritt dann in die Message Loop ein, eine meist sehr kurze Schleife die bis zum Ende des Programms läuft und nicht viel mehr (sichtbares) macht, als Nachrichten aus einer Warteschlange abzuholen und in eine andere Funktion abzugeben:

while (GetMessage(&msg, NULL, 0, 0))
{
        TranslateMessage(&msg);
        DispatchMessage(&msg);
}

Der restliche Zauber basiert weitgehend auf Callback-Funktionen und Window Messages. Inzwischen ist das unter Programmierern ein alter Hut. Quasi alle Frameworks funktionieren so, dass der Anwender-Code bestimmte Schnittstellen bereitstellt und das System sie aufruft. Aber ich kann mir gut vorstellen, dass das 1985 vielen Programmierern ein Stirnrunzeln entlockt hat, vielleicht sogar Gedanken wie "ist das wirklich eine gute Idee?".

Natürlich gibt es auch "normale" Funktionen, wie zum Beispiel zum Bearbeiten von Dateien, Zeichnen geometrischer Figuren, zur Verwaltung von Speicher und Timern, Laden von Icons, Kopieren und Einfügen mittels der Zwischenablage, usw. Aber viele zentrale Mechanismen werden eben vom System getrieben. Ein sehr schönes Beispiel ist das Zeichnen; man wird vergebens eine Funktion suchen um direkt etwas auf den Bildschirm zu zeichnen. Stattdessen markiert man einen Teil oder die gesamte Client-Fläche des Fensters als ungültig und wird daraufhin vom System gebeten, diese durch Zeichnen wieder gültig zu machen (Nachricht WM_PAINT). Das muss auch so sein, denn die Notwendigkeit, einen Teil des Fensters wieder mit gültigem Inhalt zu füllen, kann teilweise nur vom System ermittelt werden, beispielsweise wenn das Fenster verdeckt war und nun wieder zum Vorschein kommt, oder die Größe des sichtbaren Bereichs verändert wird.

Zur Laufzeit werden Nachrichten an Fenster gesendet, entweder als synchroner Aufruf mit Rückgabewerten (SendMessage) oder asynchron über die bereits erwähnte Warteschlange (PostMessage). Dabei können zwei Parameter mitgegeben werden, einer vom Typ WPARAM (16 Bit) und einer vom Typ LPARAM (32 Bit). Der Rückgabewert bei SendMessage ist vom Typ LRESULT (32 Bit). Über diese Nachrichten werden Ereignisse wie das Betätigen von Buttons, Tastendrücke oder auch Änderungen der Fenstergröße und Fensterposition mitgeteilt. In der Win16-API findet man ca. 130 dieser Window Messages, in der Win32-API rund 250.

Eine Entwicklungsumgebung für Windows 3.1

Eine heutzutage nicht zu unterschätzende Herausforderung besteht darin, eine für die Entwicklung von Win16-Programmen geeignete Entwicklungsumgebung zu finden. Dazu benötigt man

  1. Ein Windows 3.1 SDK (Software Development Kit), das die Header-Dateien und Bibliotheken beinhaltet
  2. Einen C-Compiler der 16-Bit-Code erzeugen kann
  3. Einen Linker der alles zusammenfügen kann

Auf winworldpc.com kann man ein passendes Windows SDK finden. Man wird aber auch feststellen, dass die Bibliotheken in einem altertümlichen Format vorliegen, das aktuelle C-Compiler wie der GCC nicht mehr verstehen. Man kann nun natürlich zu einer "zeitgemäßen" Entwicklungsumgebung greifen, wie z.B. Borland C++ 3.x oder 4.x (beide ebenfalls auf winworldpc.com zu finden). Oder man greift zu Open Watcom, einer Open-Source-Weiterführung der kommerziellen Watcom-Entwicklungsumgebung. Diesen Weg habe ich beschritten und auf folgender Unterseite beschrieben:

Als Test-Programm verwende ich HELLOWIN.C, eine geringfügig überarbeitete Version der HELLOWIN.C aus "Programming Windows 3.1". Wenn dieses erfolgreich übersetzt wurde, dann sieht es ungefähr so aus:

HELLOWIN.EXE unter Windows 3.1
HELLOWIN.EXE unter Windows 3.1

Der Text erscheint immer in der Mitte des Fensters, wobei das weniger der Verdienst des Programms ist, als ein Ergebnis der cleveren Kombination aus den Fensterklassen-Stilen CS_HREDRAW und CS_VREDRAW, der Behandlung von WM_PAINT und den Fähigkeiten der Funktion DrawText, die u.a. Text zentriert in einem gegebenen Rechteck ausgeben kann.

Ein richtiges Projekt mit der Windows-API

In "Programming Windows 3.1" erwähnt Charles Petzold, dass seine erste richtige Windows-Anwendung ein Hexdump-Programm war. Die Idee hat mir eigentlich ganz gut gefallen, aber ich wollte noch einen Schritt weiter gehen und einen richtigen Hexeditor entwickeln.

Die Anwendung wird hierbei zunächst mit Open Watcom unter Windows 3.1 implementiert. Anschließend wird aufgezeigt, welche Änderungen notwendig sind, um das Projekt mit XYZ unter Windows 10 als 32-Bit-Anwendung zu übersetzen.

Weitere hilfreiche und informative Links


Zurück zur Hauptseite