WindowsCharactersAndStrings
Jump to navigation
Jump to search
This page contains German notes about the character and string handling on Windows.
TODO: Translate and beautify this page.
Referencen
- http://www.codeproject.com/KB/string/cppstringguide1.aspx (The Complete Guide to C++ Strings, Part I - Win32 Character Encodings)
- http://www.codeproject.com/KB/string/cppstringguide2.aspx (The Complete Guide to C++ Strings, Part II - String Wrapper Classes)
- http://msdn.microsoft.com/en-us/library/ms235631.aspx (How to: Convert Between Various String Types)
Summary
- Es gibt 3 unterschiedliche Character Types
- SBCS = Single-Byte Character Set
- MBCS = Multibyte Character Set
- Unicode
- Das Windows API unterscheidet dagegen nur 2 Varianten: ANSI vs. Unicode
- Viele Funktionen werden mittels Preprocessor entweder auf eine "W" oder eine "A" Variante umdefiniert, z.B.
SetDlgItemTextW SetDlgItemTextA$
- Die "W" Variante verarbeitet Unicode (W=Wide)
- Die "A" Variante verarbeitet SBCS und MBCS (A=ANSI)
- Als Entwickler verwendet man den Funktionsnamen ohne Suffix, dieser wird dann basierend auf dem Preprocessor Makro UNICODE umdefiniert
- Das Makro wird automatisch definiert, wenn man in den VC++ Projektsettings Unicode w‰hlt
- TCHAR
- Dieser Datentyp (typedef) verwendet den Character Type, der in den Projektsettings gew‰hlt wurde
- Hier geht es im wesentlichen um die Unterscheidung zwischen Unicode/Non-Unicode
- _T()
- Dieses Preprocessor Makro definiert String Literals von dem Character Type, der in den Projektsettings gew‰hlt wurde
- Hier geht es im wesentlichen um die Unterscheidung zwischen Unicode/Non-Unicode
- _tcsXXX()
- Diese Preprocessor Makros werden zu den Funktionen aufgelˆst, die zu den Projektsettings passen
- Hier wird zwischen Unicode/MBCS/SBCS unterschieden; Beispiel:
- Unicode = wcsrchr()
- MBCS = _mbsrchr()
- SBCS = strrchr()
- Preprocessor Makros
- UNICODE = Applikation verwendet Unicode API; TCHAR verwendet Wide Characters
- _MBCS = Applikation verwendet ANSI API; TCHAR verwendet normale Characters; Multibyte Support ist eingeschaltet
- Weder UNICODE noch _MBCS = Applikation verwendet ANSI API; TCHAR verwendet normale Characters; Multibyte Support ist ausgeschaltet
Multibyte character sets (MBCS)
- http://msdn.microsoft.com/en-us/library/5z097dxa.aspx (Support for Multibyte Character Sets (MBCSs))
- http://msdn.microsoft.com/en-us/library/4bb3e64h.aspx (Single-Byte and Multibyte Character Sets)
- http://msdn.microsoft.com/en-us/library/zz3x65c7.aspx (MBCS Programming Tips)
- Streng genommen wird der Begriff "Character Set" von Microsoft in diesem Kontext falsch verwendet
- Die Definition eines Character Sets sagt nichts dar¸ber aus, wieviele Bytes f¸r dessen Abbildung benˆtigt werden
- Die Anzahl Bytes wird erst durch das Encoding bestimmt, das f¸r die Abbildung des Character Sets verwendet wird
- Aus diesem Grund verwende ich ab jetzt den Begriff "MBCS Encoding"
- MBCS Encodings werden verwendet, um Zeichens‰tze abzubilden, die nicht Platz innerhalb eines einzelnen Bytes (8 Bit) haben
- Zeichen in einem MBCS kˆnnen 1 oder 2 Byte lang sein, MBCS Strings bestehen also aus Zeichen unterschiedlicher L‰nge
- Besteht ein Zeichen aus 2 Bytes, so hat das erste Byte (das "Lead Byte") einen speziellen Wert, um anzuzeigen, dass noch ein zweites Zeichen folgt
- Die verwendete Codepage bestimmt, welche Werte aus einem Byte ein Lead Byte machen
- Unterschiedliche Codepages kˆnnen unterschiedliche Wertebereiche definieren
- DBCS
- DBCS = Double-Byte Character Set
- DBCS ist in der Theori nur ein mˆglicher Typ von MBCS
- In der Praxis wird DBCS aber offenbar h‰ufig als Synonym zu MBCS verwendet, da es unter Windows kein MBCS gibt, das Characters >2 Bytes hat
- Welches Encoding effektiv verwendet wird, xxx
- Um eine Applikation mit MBCS Support zu builden, muss das Preprocessor Makro _MBCS definiert sein
- Die Single-Byte Funktionen strcpy(), sprintf(), etc. haben entsprechende MBCS Pendants: _mbscpy() etc.
Unicode
- Funktionen: http://msdn.microsoft.com/en-us/library/cc500321.aspx
- Unicode im allgemeinen: http://msdn.microsoft.com/en-us/library/dd374081.aspx
- Grunds‰tzliche Infos zur Organisation von Unicode Codepoints
- Referenzen
- Alle im Unicode mˆglichen Codepoints sind auf 17 verschiedenen Ebenen (Planes) verteilt
- Jede Ebene/Plane hat 65535 Zeichen
- Um einen Codepoint eindeutig zu identifizieren, wird ihm ein Hex-Pr‰fix [0-9A-F] vorangestellt
- U+24321 befindet sich in Plane 2
- U+F4321 befindet sich in Plane 15
- U+104321 befindet sich in Plane 16 (Pr‰fix wird hier zweistellig)
- U+4321 befindet sich in Plane 0 (Pr‰fix wird weggelassen, Plane ist implizit 0)
- Die erste Ebene (Plane 0) ist die sogenannte "Basic Multilingual Plane" (BMP)
- Die nachfolgenden Ebenen werden zusammengefasst unter dem Begriff "Supplementary Planes"
- Die BMP Codepoints im Bereich U+D800 bis U+DFFF stellen keine Zeichen dar, sondern sind reserviert f¸r UTF-16, um sogenannte "surrogate pairs" zu bilden; siehe UTF-16 weiter unten
- UTF-16 Informationen
- Referenzen
- UTF-16 ist ein Unicode Character Encoding; es lˆst das ‰ltere Encoding UCS-2 ab
- UCS-2 verwendete Zeichen mit einer fixen L‰nge von 2 Bytes und konnte deshalb keine Zeichen jenseits der BMP abbilden
- Der Wert eines UCS-2 Zeichens entsprach einfach dem Codepoint des Zeichens
- UTF-16 ist im Gegensatz zu UCS-2 in der Lage, den gesamten Unicode Zeichenbereich abzudecken
- Die Zeichen der BMP werden wie in UCS-2 einfach mit dem Wert ihres Codepoint abgebildet
- Die Zeichen jenseits der BMP wird mit Hilfe von sogenannten "Surrogate Pairs" abgebildet
- Um Surrogate Pairs zu bilden werden die Codepoints U+D800..U+DFFF aus der BMP verwendet
- Diese Codepoints sind keine Zeichen sondern werden vom Unicode-Standard explizit f¸r den UTF-16 Support reserviert
- Hat der erste 16-Bit Wert eines UTF-16 Zeichens den Wert eines dieser speziellen Codepoints, so wird ein zweiter 16-Bit Wert angeh‰ngt, um das endg¸ltige Zeichen zu bilden
- Die beiden 16-Bit Werte zusammen werden als "Surrogate Pair" bezeichnet
- Die Reihenfolge der Bytes innerhalb eines 16-Bit Werts h‰ngt von der Endianness der Maschine ab, auf dem die Verarbeitung stattfindet
- Um explizit anzuzeigen, welche Endianness eine Zeichenfolge verwendet, kann die Zeichenfolge mit einer Byte Order Mark (BOM) eingeleitet werden
- Seit Windows NT verwendet Windows intern nur noch Unicode (native Unicode encoding)
- Unicode Zeichen sind dabei 16 Bit breite Zeichen, das Encoding ist UTF-16, die Byte Order ist per Default Little-Endian
- 16-Bit Werte vs. "Zeichen"
- Wo nicht explizit etwas anderes beschrieben ist arbeiten Funktionen wie z.B. strlen() mit der Anzahl 16-Bit Werte
- Einerseits w‰re das Ber¸cksichtigen von Surrogate Pairs vermutlich zu aufw‰ndig
- Andererseits gibt es den Begriff "Zeichen" (bzw. "Character") im traditionellen Sinn im Unicode Standard gar nicht, da Codepoints auch unsichtbare und kombinierbare "Zeichen" umfassen
- Wenn also die Dokumentation von strlen() etc. von "Zeichen" spricht, kommt das tendentiell aus der ASCII-ƒra, wo ein "Zeichen" immer auch gleichzeitig 1 Byte entsprach; in UTF-16 wird dagegen mit 2-Byte Einheiten gearbeitet
- Um eine Applikation mit Unicode Support zu builden, muss das Preprocessor Makro _UNICODE definiert sein
TCHAR, OLECHAR, BSTR, VARIANT, CString
TCHAR
- http://msdn.microsoft.com/en-us/library/cc842072.aspx
- Der Artikel verwendet die Bezeichnungen ANSI und DBCS, meint damit aber eigentlich SBCS und MBCS
- TCHAR repr‰sentiert ein Zeichen, entweder ein SBCS, MBCS oder Unicode Zeichen
- F¸r SBCS und MBCS ist TCHAR ein typedef auf char
_T()
- Ein Makro, um String Literals vom Character Type unabh‰ngig zu machen
- Wird das Projekt mit Unicode Support kompiliert, so ist _T("foo") definiert als L"foo"
- Ohne Unicode Support: _T("foo") -> "foo"
- Alternativen zu _T() sind: TEXT(), _TEXT() und __TEXT()
OLECHAR und OLESTR
- OLECHAR wird in Automation Interfaces (COM) verwendet
- Normalerweise ist OLECHAR definiert als wchar_t, d.h. Unicode
- Historisch kˆnnte man auch char daraus machen, in der Praxis wird das aber nicht mehr gemacht
- OLECHAR wird f¸r die Definition von BSTR verwendet
- OLESTR() ist ein Makro, um String Literals vom Type OLECHAR zu erhalten (faktisch wird einfach der Prefix L"" hinzugef¸gt)
BSTR
- http://msdn.microsoft.com/en-us/library/ms221069.aspx
- http://www.codeproject.com/KB/string/bstrsproject1.aspx (Mini-Projekt mit Beispielen zum Konvertieren zwischen String-Typen)
- BSTR = Basic String, oder Binary String
- Dieser Datentyp wird von/f¸r COM verwendet; BSTR muss in Interfaces verwendet werden, auf die von Visual Basic oder Java Applets aus zugegriffen wird
- Der Inhalt eines BSTR setzt sich folgendermassen zusammen
- Length Prefix, 4-Byte Integer = Anzahl Bytes nach dem Prefix und ohne Terminator
- String = Soviele Bytes wie im Length Prefix angegeben. Encoding = Unicode (d.h. UTF-16 bzw. wide/double-byte characters)
- Terminator = 2 Null Characters (0x00)
- Ein BSTR ist ein *Pointer*, der auf das erste Zeichen des Strings zeigt, *nicht* auf den Length Prefix
- Ein Grund f¸r diese Idiotie ist, dass man auf diese Art ein BSTR an eine Funktion ¸bergeben kann, die ein LPCOLESTR oder LPCWSTR Argument hat
- Das umgekehrte funktioniert dagegen nat¸rlich nicht: Wird ein LPCWSTR an eine Funktion ¸bergeben, die ein BSTR will, fehlt der Length Prefix
- Die Rettung ist der Wrapper _bstr_t, siehe weiter unten
- BSTR Objekte m¸ssen mit Hilfe von speziellen Memory Allocation Funktionen angelegt und zerstˆrt werden
- Zum Beispiel
BSTR foo = SysAllocString(L"bar"); SysFreeString(foo);
- Das folgende Beispiel kompiliert zwar, funktioniert zur Laufzeit aber *nicht* richtig!!!!!!!
BSTR foo = L"bar";
- Dementsprechend kˆnnen einer Funktion, die ein BSTR Argument hat, auch nicht einfach normale Unicode (wchar) Strings ¸bergeben werden!!!
- BSTR ist definiert wie folgt
typedef OLECHAR FAR * BSTR; (in OLEAuto.h) typedef WCHAR OLECHAR; (in WTypes.h)
- _bstr_t
- http://msdn.microsoft.com/en-us/library/zthfhkd6.aspx
- Die Klasse _bstr_t ist ein Wrapper um einen BSTR String
- Die Klasse hat mehrere Constructors, mit denen auf einfache Weise andere String-Typen konvertiert werden kˆnnen (Multibyte, Unicode, Variant)
- Die Klasse implementiert Reference Counting, um bei Kopien nicht immer den vollen String kopieren zu m¸ssen
- Die Klasse ist ein "sauberer" Wrapper, d.h. es wird kein Pointer auf den internen BSTR heraugegeben
- Jedenfalls offiziell
- Inoffiziell sind auch Operatoren implementiert, die non-const Pointer liefern
- Diese d¸rfen aber nicht verwendet werden
- Das bedeutet, dass _bstr_t nicht verwendet werden kann, um ein [out] Argument in einem COM Aufruf zu "beliefern"
- CComBSTR
- Ebenfalls ein Wrapper f¸r BSTR, diesmal aber von ATL geliefert
- CComBSTR gibt Zugriff auf den gekapselten BSTR, man kann also ein [out] Argument in einem COM Aufruf "beliefern"
VARIANT
- Dies ist eine Wrapper Klasse f¸r verschiedene Datentypen
- Enth‰lt VARIANT einen String, so wird der String als BSTR gespeichert
- _variant_t: Wrapper um VARIANT
- Um an den BSTR Wert zu gelangen, kann das Property "bstrVal" verwendet werden: variantVariable.bstrVal
- CComVariant: Noch ein Wrapper um VARIANT, diesmal aber von ATL geliefert
- COleVariant: Noch ein ATL Wrapper um VARIANT, diesmal aber mit Constructor, der ein CString Objekt akzeptiert (und sonst ein bisschen anders ist)
CString
- http://msdn.microsoft.com/en-us/library/ms174288.aspx (Using CString)
- CString ist der MFC-Wrapper f¸r TCHAR Strings
- CString enth‰lt Support f¸r alle Character Types (SBCS, MBCS, Unicode)
- CString definiert einige Konversions-Operatoren, weshalb man die Klasse ziemlich freiz¸gig einsetzen kann:
CString myString = "foo"; const char* pch = myString; LPCSTR pch = static_cast<LPCSTR>(myString); // sollte man nat¸rlich nicht benutzen, auch wenn hier ein Operator existiert LPCSTR pch = myString; // viel schˆner: hier sieht man sofort, dass irgendein Konversions-Operator am Werk sein muss char ch1 = myString[1]; TCHAR ch2 = myString[1]; BSTR bs1 = myString.AllocSysString(); // anschliessend SysFreeString(bs1) BSTR bs2; myString.SetSysString(&bs2); // anschliessend SysFreeString(bs2)
- Um _bstr_t in einen CString zu konvertieren
_bstr_t bString = "foo"; CString cString1 = static_cast<LPCSTR>(bString); CString cString2 = static_cast<const TCHAR*>(bString); // etwas nachhaltiger, da bei Unicode Support automatisch wchar verwendet wird CString cString3 = bString.GetBSTR(); // eher nicht verwenden, da man sonst in den Wrapper _bstr_t hineinschaut
- Es wird empfohlen, f¸r Funktionsparameter den Type LPCSTR zu w‰hlen, weil man dann alle Arten von Strings ¸bergeben kann (literal string, CString, TCHAR array, etc.)
- Ich bin nicht ganz dieser Meinung - wichtiger f¸r mich ist das Kriterium, ob man in der Schnittstelle einen MFC Type haben will oder nicht
- LPCSTR finde ich weiterhin ¸bel, weil man darauf keine Forward Declaration machen kann, da es sich um einen typedef handelt.
LPCTSTR etc.
- LPCTSTR ist der folgende typedef (wobei CONST ein Makro auf const ist, und CHAR ein typedef von char):
typedef CONST CHAR *LPCSTR, *PCSTR;
- Es existieren eine Unmenge weiterer typedefs und Preprocessor Makros
Conversion between Unicode and MBCS
ATL Conversion Macros
- http://msdn.microsoft.com/en-us/library/87zae4a3.aspx
- Die Makros kˆnnen auch in Nicht-ATL Projekten verwendet werden, da sie keine Abh‰ngigkeiten vom Rest von ATL haben (z.B. keine globale Variable _Module)
- Es gibt alte und neue Makros
- Alt: ATL 3.0
- Neu: ATL 7.0
- Die neuen Makros sind vorzuziehen; eine ‹bersichtstabelle auf der oben referenzierten Seite zeigt die Vorteile
- Um die Makros zu verwenden
- Include
#include <atlbase.h> // evt. nur bei ATL 7.0 Makros noetig? #include <atlconv.h>
- Bei den alten ATL 3.0 Makros muss am Anfang der Funktion, die konvertieren will, zus‰tzlich das folgende Makro auf einer eigenen Zeile erscheinen
USES_CONVERSION
- Ist der Ziel-Type = BSTR, sollte CComBSTR f¸r die Konversion verwendet werden
- Der Speicher f¸r den Ziel-String wird automatisch angelegt und auch wieder freigegeben
- Bei den alten ATL 3.0 Makros passiert das erst am Ende der Funktion
- Bei den neuen ATL 7.0 Makros bereits, wenn die angelegte Variable out-of-scope geht
- Beispiel neue ATL 7.0 Makros
- CA2CT destinationString(sourceString); --> konvertiert von Ansi nach TCHAR
- Bei den ATL 7.0 Makros sorgt der Cast Operator jeweils daf¸r, dass der Ziel-String den gew¸nschten Typ hat
- Beispiele alte ATL 3.0 Makros
- LPCTSTR destinationString = A2T(sourceString);
- W2A(L"foo") = Konvertiert von Unicode nach MBCS
- T2CW("foo") = Konvertiert von TCHAR nach Constant Unicode
- Die Makros nutzen folgende Nomenklatur
- A = ANSI, ein MBCS String bzw. char*
- W = Wide, ein Unicode String bzw. wchar_t*
- T = TCHAR*
- OLE = OLECHAR* (in der Praxis gleichbedeutend mit "W")
- BSTR = BSTR (nur als Ziel-Type einsetzbar)
- C = Der String Type ist const
TODO: Gibt es noch andere Mˆglichkeiten?