WinAPI: Win16
« | 22 Jul 2023 | »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.)