WinAPI: Win16

Windows ist abwärtskompatibel. Alte APIs bleiben bestehen, es kommen nur neue dazu.

So heißt es. Also ist Win95 einfach ein Win3x + Erweiterungen. … zumindest glaubte ich das bisher.


Obwohl mein erstes Windows noch zur 3.x Generation gehörte, habe ich nur ein bisschen Visual Basic darauf ausprogrammiert.
C und C++ programmierte ich erst später und nur gegen das Win32 API.

Doch mein Glaube war stark, dass ich somit auch “Win16” beherrschen würde, weil dieses in Win32 aufgegangen ist, quasi nach der Formel:
Win32 = Win16 + Threads

Ich hätte gar nicht falscher liegen können, wenn man von einigen wenigen Details zur Fenstergestaltung absieht.

OpenWATCOM, “der Compiler” des 20. Jahrhunderts kann auch heute noch Win16 Binaries erzeugen.
Und somit wandert auch diese Plattform langsam ins GATE Projekt

Andere APIs

So wie im POSIX Code häufig ein open() der Anfang von allem ist, so sieht man im WinAPI Code stets ein CreateFile(). Doch Win16 hatte eine solche Funktion nicht, auch kein ReadFile() oder WriteFile() und so stand ich vor der ersten großen Frage:

Soll ich jetzt alle Codestellen per #ifdef WIN16 mit einer zweiten Implementierung beglücken, wenn die Win32 Funktion nicht in Win16 verfügbar ist.

Jein ist die Antwort.

Es gibt schon solche Stellen, die vollständig neu geschrieben werden müssen. Ein Beispiel wären “Prozesse”, die in Win16 nur “Tasks” mit Task-ID waren und technisch mit Win32 Prozessen nichts zu tun haben.

Doch bei häufig genutzten APIs wie beim Dateisystem wählte ich einen anderen Ansatz: Die Win32 API wird einfach mit Win16 Funktionen emuliert.

Win32 Win16
CreateFile OpenFile
ReadFile _lread
WriteFile _lwrite
CloseHandle _lclose
DeleteFile OpenFile(OF_DELETE)
FindFirstFile opendir
SetFilePointer _llseek
GetFileSize _llseek

und so weiter.

Dadurch lassen sich dann bereits fertige Codes sofort kompilieren, einzige Herausforderung ist, dass ich das Verhalten auch ausreichend genau nachbauen muss.

Keine Fehler

Viele Win16 APIs haben keine Rückgabewerte, während ihre Win32 Nachfolger diese sehr wohl nutzen um Erfolg von Misserfolg zu unterscheiden. Hier bleibt mir dann doch nur ein #ifdef um den Rückgabewerte in Win16 zu ignorieren.

Außerdem gab es kein GetLastError() und SerLastError().
Zumindest das kann man mit einer globalen Variable selbst leicht nachbauen.

Keine DIBSection

Interessant ist, wie häufig ich in Win32 DIBSections eingesetzt habe um dann direkt auf dem Grafikpuffer Pixel nachzubearbeiten.
Tja, da macht einem Win16 auch einen Strich durch die Rechnung: Man kann dort nicht selbst einen Puffer allokieren und an eine Bitmap ausleihen.

Einzige Alternative ist, einen Pixelpuffer zu befüllen und dann an CreateDIBitmap zu übergeben. Dann kann man zwar nicht mehr direkt auf die Pixel in der Bitmap zugreifen, aber man kann zumindest immer wieder neue Bitmaps mit fertigen Bildern erstellen.

UI Flags

Doch das schlimmste sind fehlende Features bei UI Controls. Das Konzept von Fenstern, Buttons, Labels und Textfeldern ist von Win16 direkt auf Win32 übernommen worden. Doch Win32 hat zahlreiche Flags und Details angefügt, die ich inzwischen aktiv eingesetzt hatte.

Um jetzt daraus wieder Win16-kompatiblen Code machen zu können, muss ich entweder auf einige Features verzichten, oder das Control im Win32 Stil unter Win16 nachimplementieren.

Bei erweiterten Controls wie Treeview und Listview ist das ohnehin notwendig, da es die unter Win16 nicht gab.

Fazit

Alles in allem ist das zwar eine interessante Erfahrung, aber ich habe total unterschätzt wie unterschiedlich Win16 von Win32 war und ist.

Umgekehrt kann man auch sagen, wie sehr ausgereift Win32 im Gegensatz zu Win16 ist, denn hier zeigt sich erst, mit wie wenig seltsamen APIs damals Anfang der 90er gearbeitet werden musste.
(Und trotzdem kamen einige große Apps dabei heraus.)