Ein Stück Code in unserer Software behandelt Datenimport aus fremden Dateien. Nicht wenige alter DOS Programme erzeugen ja Daten im „OEM“-Zeichensatz (DBase etc.).
Wir haben in der Vergangenheit immer streng die T-Notation verwendet und eigentlich sollte dies einen vor vielen Überraschungen bewahren. Aber Pustekuchen.
Folgender Code sollte Daten aus einem OEM-Stream in einen CString umwandeln und eigentlich bin ich davon ausgegangen, dass das Ergebnis für Tabulatoren (‚\t‘) und Zeilenschaltungen („\r\n“) ergebnisneutral ist. D.h. in anderen Worten ich erwartete in dem String genauso viele Tabulatoren und Zeilenschaltungen vor wie nach der Konvertierung.
Die Umwandlung erfolgte mit solch einem Stück Code:
CString strText;
::OemToCharBuff(szText,CStrBuf(srText,len),len);
Sieht eigentlich harmlos aus. Allerdings musste ich feststellen, dass hier OemToCharBuffA und OemToCharBuffW ganz und gar nicht korrespondierende Ergebnisse liefern.
In der Unicode Version, also in der Version die OemToCharBuffW verwendet, wurden Tabulatoren zu 0x25cb L’○‘ wchar_t und Zeilenschaltungen zu 0x266a L’♪‘ wchar_t und 0x25d9 L’◙‘ wchar_t!
Führt man jedoch zuerst eine Kovertierung in den „ANSI/Windows/8bit“-Zeichensatz durch und konvertiert anschließenend diesen ANSI-String nach Unicode, dann ist alles gut und so wie man es erwartet.
Wer Lust hat das nachzubauen kann das mit dem folgenden Code. Wichtig sind eigentlich nicht die OEM-Umlaute sondern nur die Tab- und Zeilenschaltungen:
const char szText[] = "Dies ist ein OEM TestString\r\n"
"mit Zeilenschaltungen\tund Tabs\r\n"
"und Umlauten:\r\n"
"Ž=AE, ™=OE, š=šE\r\n"
"„=ae, ”=oe, =ue, á=ss";
const size_t len = sizeof(szText);
CStringW strOut1;
::OemToCharBuffW(szText,CStrBufW(strOut1,len),len);
CStringA strOut2;
::OemToCharBuffA(szText,CStrBufA(strOut2,len),len);
CStringW strOut3(strOut2);
_ASSERT(strOut1.Compare(strOut3)==0); // Das sollte eigentlich gleich sein
PS: Getestet habe ich das auf einem Windows 7 64bit und 32bit OS.
Siehe folgende Links zu den Begriffen OEM/ANSI:
http://blogs.msdn.com/b/oldnewthing/archive/2005/10/27/485595.aspx
http://blogs.msdn.com/b/oldnewthing/archive/2005/03/08/389527.aspx
Ich denke, dass OemToCharBuffW() intern
MultiByteToWideChar(CP_OEMCP, MB_USEGLYPHCHARS…) benutzt.
Also besser gleich selbst MultiByteToWideChar() ohne MB_USEGLYPHCHARS benutzen.