1 Theorie |
Datentypen | |
Text | |
Übersicht | |
Beispiel | |
Präprozessor-Konstanten | |
Laufzeit-Bibliothek und Compiler-Optionen | |
Manifest | |
Icon und Eigenschaften | |
C-Laufzeitbibliothek und Windows API |
In Visual Studio werden eine ganze Reihe von Datentypen für die Windows-Programmierung definiert.
Eine Web-Suche nach
"Windows Data Types" site:microsoft.com
führt zu den MSDN-Seiten mit einer Auflistung der Datentypen.
Der Datentyp char sollte unter Windows nur für englischsprachige Texte benutzt werden, genauer gesagt: nur für Texte, die ausschließlich Zeichen aus dem ASCII-Zeichensatz enthalten.
Für Internationalisierung (also Texte in anderen Sprachen) wird
der Datentyp wchar_t empfohlen. Dieser ist unter Windows 16 Bit
breit, kann auf anderen Systemen aber auch 32 Bit breit sein.
Für bessere Lesbarkeit kann anstelle von wchar_t unter Windows auch
WCHAR geschrieben werden.
Mit wchar_t können UNICODE-Zeichen verwendet werden. Der UNICODE-Zeichensatz 〈1〉 verwendet je nach Version 32 oder
24 Bit, was ausreichend ist, sämtliche Zeichen aller weltweit
verwendeten Schriften darzustellen. Auf Systemen mit 32 Bit breiten
wchar_t werden die UNICODE-Zeichen direkt verwendet. Unter Windows
und auf anderen Systemen mit nur 16 Bit breitem wchar_t-Datentyp
wird die Codierung UTF-16 〈2〉 verwendet.
Der Datentyp TCHAR wird entweder auf WCHAR oder char gemappt, je nachdem, ob die Präprozessor-Konstante _UNICODE definiert ist oder nicht.
const char test_string_8_bit[] = { "Dies ist ein Test" }; const WCHAR test_string_16_bit[] = { L"Dies ist ein Test." }; const TCHAR test_string_flexible[] = { _T("Dies ist ein Test.") };
Für Funktionen, die Strings als Argumente erhalten oder als Ergebnisse liefern, existieren meist drei Versionen:
Wollen Sie eine Erweiterungs-DLL für Windows erstellen, muss
_UNICODE auf 1 definiert werden.
Besser ist es, von vornherein WCHAR bzw. wchar_t und die
zugehörigen Funktionen zu verwenden.
Die Dokumentationen sind bezüglich des Namens der zu
definierenden Konstante nicht eindeutig, die meisten Textstellen
bezeichnen sie als _UNICODE, andere als UNICODE (ohne führenden
Unterstrich).
In der Datei tchar.h wird _UNICODE verwendet. Möglicherweise
resultiert der fehlende führende Unterstrich an manchen Stellen aus
der automatischen Übersetzung. Im Zweifelsfall sollte die
englischsprachige Originaldokumentation anstelle der deutschen
Übersetzung konsultiert werden.
#include <io.h>
#include <fcntl.h>
#include <stdio.h>
int main(void)
{
int oldmode;
/* Auf internationalen Mode (16-Bit-Text) umschalten, alten Mode
sichern.
*/
oldmode = _setmode(_fileno(stdout), _O_U16TEXT);
/* Byte-Order-Marker als erstes Zeichen ausgeben.
Fuer Text-Dateien immer.
Fuer stdout nur wenn Umleitung in Datei erfolgt, nicht bei
Ausgabe ins Terminal.
*/
if (0 == _isatty(_fileno(stdout))) { fputwc(0xFEFF, stdout); }
/* Nicht-ASCII-Zeichen duerfen nicht im Quelltext vorkommen, auch
nicht in Strings oder Kommentaren. Deshalb numerische Angabe.
Das %lc ist der Platzhalter fuer einen wide (long) character.
UNICODE-Zeichen koennen auch mit Backslash-u und 16-Bit-Hexwert
in den String geschrieben werden. Dabei müssen exakt 4
Hexadezimalstellen verwendet werden.
Fuer UNICODE-Zeichen oberhalb von 0xFFFF kann Backslash-U
(hier mit grossem U) verwendet werden, darauf muessen exakt
8 Hexadezimalstellen folgen.
*/
fwprintf(stdout, L"Im Sommer haben wir bis zu 35%lcC.\n", 0x00B0 );
fputws(L"Im Winter bis -20\u00B0C.\n", stdout);
/* Sicherstellen, dass alles ausgegeben wurde.
*/
fflush(stdout);
/* Falls stderr benutzt wurde, auch dieses flushen.
*/
/* Urspruenglichen Mode wiederherstellen.
*/
_setmode(_fileno(stdout), oldmode);
return 0;
}
Das Beispiel verdeutlicht folgende Sachverhalte:
Eine ganze Reihe von Präprozessor-Konstanten bzw. -Makros ist bereits vordefiniert. Hier eine kurze Auswahl, eine vollständige Liste kann der Online-Hilfe von Visual Studio entnommen werden:
Die Laufzeit-Bibliothek ist in verschiedenen Versionen
verfügbar.
Eine erste Unterscheidung ist, ob eine Debug- oder Release-Version
erstellt werden soll. Eine Debug-Version enthält neben dem
ausführbaren Programm noch Zusatzinformationen, um das Programm im
Debugger laufen zu lassen sowie zusätzliche Tests auf
Programmierfehler. Eine Release-Version enthält diese
Zusatzinformationen nicht und ist somit deutlich kleiner und
schneller.
Die nächste Unterscheidung besteht darin, ob die
Laufzeit-Bibliothek statisch zum Programm gelinkt wird oder nur ein
Stub (kleiner Schnipsel), der bei der Programmausführung eine DLL
hinzulädt.
Die dritte Unterscheidung fragt, ob das Programm
multithreading-fähig sein soll oder nicht.
Bereits beim Compilieren eines Quelltextmodules wird festgelegt,
welche Version der Laufzeitbibliothek zum entsprechenden
Objektmodul später hinzugelinkt werden muss.
Alle Quelltextmodule eines Projektes müssen diesbezüglich mit
derselben Einstellung compiliert werden, andernfalls werden zwei
unterschiedliche Versionen der Laufzeitbibliothek hinzugelinkt, was
beim Linker-Lauf zu einem Fehler führt. Bei der Arbeit mit Visual
Studio ist dies normalerweise kein Problem, mit dem Menüpunkt
ERSTELLEN | |
→ | Projektmappe neu erstellen |
wird automatisch dieselbe Laufzeit-Bibliothek-Version für alle Module verwendet.
Beim manuellen Compilieren oder Compilieren über makefile müssen alle Objektmodule (auch die in evtl. beteiligten Bibliotheken) mit denselben Einstellungen übersetzt sein.
Compiler-Option | Bibliotheks-Datei | Einstellungen |
---|---|---|
/ML | LIBC.LIB | statisch, Release, Single-Threading |
/MLd | LIBCD.LIB | statisch, Debug, Single-Threading |
/MT | LIBCMD.LIB | statisch, Release, Multi-Threading |
/MTd | LIBCMTD.LIB | statisch, Debug, Multi-Threading |
/MD | MSVCRT.LIB | DLL, Release |
/MDd | MSVCRTD.LIB | DLL, Debug |
Wollen Sie eine Erweiterungs-DLL für Windows erstellen, muss /MD verwendet werden.
Moderne Versionen der Laufzeitbibliothek erfordern ein Manifest
für jede *.exe-Datei und jede *.dll-Datei, die die
Laufzeitbibliothek verwendet.
Im wesentlichen steht im Manifest, ob die Anwendung mit den
Berechtigungen des Elternprozesses gestartet wird oder ob die
Anwendung immer mit Administratorrechten laufen soll.
Das Manifest kann als separate Datei *.exe.manifest bzw. *.dll.manifest für die Datei *.exe bzw. *.dll vorliegen oder bereits in die *.exe- bzw. *.dll-Datei eingebettet sein.
Visual Studio bettet standardmäßig das Manifest gleich in die *.exe-Datei ein.
Beim manuellen Linken oder im Build-Prozess mit nmake empfehle
ich, für den Linker die Option "/manifest" mit anzugeben.
Damit legt der Linker eine *.exe.manifest-Datei mit an, wenn *.exe
erstellt wird.
Mit dem Programm mt.exe kann die *.exe.manifest-Datei in die
*.exe-Datei eingebettet werden, bevor die *.exe-Datei benutzt oder
weitergegeben wird.
Eine Icon-Datei (*.ico) enthält mehrere Bitmap-Graphiken in
verschiedenen Auflösungen und Farbtiefen.
Windows benutzt Icons z.B. im Oberbalken eines Fensters, für Menüs
und für die Darstellung im Windows-Explorer. Je nach
Bildschirmgröße und Einstellungen des Nutzers wird die jeweils am
besten passende Bitmap-Graphik automatisch ausgewählt.
Markiert man eine *.exe-Datei im Windows-Explorer und wählt aus
dem Kontextmenü (rechte Maustaste) den Eintrag Eigenschaften,
werden Detailinformationen angezeigt.
Für unser Beispiel ex001 haben wir keine besonderen Vorkehrungen
getroffen, die *.exe-Datei ex001.exe wird im Windows-Explorer mit
einem Standard-Icon angezeigt.
Auch bei den Eigenschaften ist nicht allzu viel zu erfahren.
Wir erstellen zunächst eine Icon-Datei testicon.ico (hier mehrere konzentrische Kreise) und schreiben anschließend eine Datei winex001.rc:
aaaaa ICON testicon.ico #include <windows.h> VS_VERSION_INFO VERSIONINFO FILEVERSION 1,2,3,4 PRODUCTVERSION 5,6,7,8 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L #else FILEFLAGS 0x0L #endif FILEOS 0x4L FILETYPE 0x1L FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904b0" BEGIN VALUE "Comments", "Test-Programm, erstes Beispiel\0" VALUE "CompanyName", "HS Schmalkalden\0" VALUE "FileDescription", "Test-Programm\0" VALUE "FileVersion", "1.2.3.4\0" VALUE "InternalName", "winex001\0" VALUE "LegalCopyright", "Copyright 2012-2014 © HS Schmalkalden\0" VALUE "ProductName", "Test-Programm\0" VALUE "ProductVersion", "5.6.7.8\0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1200 END END
Die ICON-Zeile fügt ein Icon zum Programm hinzu, intern wird der Name "aaaaa" für dieses Icon verwendet. Der Windows-Explorer benutzt das Icon mit dem lexikographisch kleinsten internen Namen.
In den VALUE-Zeilen werden verschiedene Informationen
festgelegt. Zu beachten ist, dass jeder Text-Wert mit \0
abgeschlossen werden muss.
Als Wert kann hier eine String-Folge angegeben werden. Zwei
aufeinanderfolgende Nullbytes markieren das Ende der String-Folge.
Ein Nullbyte wird ganz normal vom Ende des literalen Strings
erzeugt, ein zusätzliches Nullbyte fügen wir explizit ein.
Die VALUE-Werte für "FileVersion" bzw. "ProductVersion" müssen zu den Zahlenfolgen in "FILEVERSION" bzw. "PRODUCTVERSION" korrespondieren.
Das Format der *.rc-Datei wird hier nicht vollständig beschrieben, per Web-Suche können hierzu detaillierte Informationen gefunden werden.
Wir fügen die Datei winex001.rc dem Projekt als Ressourcendatei hinzu und erstellen das Projekt.
Im Windows-Explorer wird jetzt unser Icon für die Darstellung der Datei winex001.exe benutzt.
In den Eigenschaften werden jetzt Texte entsprechend der *.rc-Datei angezeigt.
Die C-Laufzeitbibliothek (CRT, C runtime library) enthält u.a. Programmcode sowohl für die folgenden Zwecke:
Die Funktionen bieten ein gewisses Abstraktionslevel. Beispielsweise steht für Ein- und Ausgaben in Dateien der Datentyp FILE mit den zugehörigen Funktionen zur Verfügung, unabhängig davon, welches Dateisystem benutzt wird.
Je nach verwendetem Computer stehen unterhalb dieser
Abstraktionsebene systemspezifische Programmierschnittstellen (API,
application programming interface) zur Verfügung.
Unter Windows ist dies die Windows API.
Auf Nicht-Windows-Systemen können Programme, die die CRT
verwenden, problemlos ausgeführt werden. Arbeiten die Systeme mit
dynamischen Bibliotheken (shared libraries), werden die
*.so-Dateien bei der Betriebssystem-Installation mit
installiert.
Somit können auch Betriebssystem-Komponenten und Daemons die CRT
benutzen.
Unter Windows werden die *.lib- und *.dll-Dateien der CRT
entweder bei der Installation von Visual Studio mit installiert
oder können als gesonderter Download von Microsoft bezogen werden
(Visual Studio Redistributables). Verwendet ein Programm
DLL-Versionen der CRT, müssen die passenden CRT-DLLs auf jedem PC
installiert werden, auf dem das Program laufen soll.
Fehlen die CRT-DLLs beim Start eines normalen Anwendungsprogrammes,
zeigt Windows eine entsprechende Fehlermeldung an.
Fehlen DLLs, die von Systemkomponenten benötigt werden, ist mit
größeren Problemen zu rechnen.
Für bestimmte Zwecke - z.B. Windows-Erweiterungs-DLLs,
Systemdienste, Treiber... - wird die Verwendung der CRT nicht
empfohlen, hier sollten nur Funktionen der Windows API benutzt
werden.
1 | http://www.unicode.org |
2 | http://de.wikipedia.org/wiki/UTF-16 |