Programmieren mit C++

Allgemeines

Internationalisierung von Anwendungen

Unicode und MBCS

Wie können Anwendungen auf internationale Märkte unter Verwendung von UNICODE oder MBCS vorbereitet und bestehende Quellen portiert werden?

Frage

Damit Anwendungen auf breiter Front international eingesetzt werden können, ohne an sprachlichen Barrieren zu scheitern, ist es sinnvoll, Anwendungen Unicode- oder MBCS- fähig zu machen.

Lösung

Der Unicode-Zeichensatz besteht aus den sogenannten „Wide Char“-Zeichen, die mit zwei Byte pro Zeichen codiert werden und sämtliche verfügbare Zeichen aller Sprachen enthalten, einschließlich aller technischen Symbole sowie der speziellen Publishing-Zeichen.

Unicode

Der Multibyte-Zeichensatz (MBCS) codiert Zeichen je nach Bedarf mit 1 oder 2 Byte und kommt primär zum Einsatz für Zeichensätze, die eine große Anzahl unterschiedlicher Zeichen besitzen, wie beispielsweise die asiatischen Sprachen.

Welcher Zeichensatz Verwendung findet, hängt von der Sprache und dem Betriebssystem ab. Unicode erfordert mehr Speicher als MBCS, da jedes Zeichen mit 2 Bytes codiert ist. Darüber hinaus ist Unicode schneller als MBCS und der NT-Standard, so daß Strings, die nicht in Unicode vorliegen, für den Austausch mit dem Betriebssystem übersetzt werden müssen, was einigen Overhead mit sich bringt. Andererseits wird Unicode unter Windows 95 nicht unterstützt, so daß hier MBCS die bessere Wahl sein wird. Unter Windows CE hingegen müssen Anwendungen in Unicode compiliert werden.

MBCS oder Unicode einsetzen

Die einfachste und universellste Möglichkeit, Unicode und MBCS oder gar reines ASCII in Anwendungen einzusetzen, besteht darin, die generischen Textkonvertier-Makros von VC++ zu verwenden. Dadurch wird es möglich, über ein einfaches Define zwischen Unicode, MBCS und ASCII umzuschalten, ohne daß irgendwelche Codeänderungen anfallen.

MBCS

Zur Anwendung von MBCS oder Unicode muß eines der beiden Defines _MBCS oder _UNICODE im Projekt definiert werden. Für Unicode ist zusätzlich ein Einsprungpunktsymbol in den Projekteinstellungen als wWinMainCRTStartup zu definieren. Zu beachten ist dabei, daß stets nur eines der beiden Defines gesetzt sein darf. Werden sowohl _MBCS als auch _UNICODE definiert, sind die Ergebnisse unvorhersehbar.

Generisches Textmapping und portable Funktionen

Das generische Textmapping ersetzt die Standard-char- bzw. LPSTR-Typen durch die generischen Makros TCHAR bzw. LPTSTR. Die Makros nehmen eine automatische Umsetzung auf unterschiedliche Typen und Funktionen vor, abhängig davon, ob _UNICODE oder _MBCS oder keines von beiden definiert ist.

Am einfachsten läßt sich der TCHAR-Typ über die Klasse CString einbinden. Dies ist extrem flexibel und leistet die meiste Arbeit automatisch, ohne daß zusätzlicher Code erforderlich wird.

Im Zusammenhang mit dem generischen Zeichentyp existieren diverse generische Funktionen zur Stringmanipulation, die das Prefix „_tcs“ besitzen. So sollte beispielsweise anstelle der Funktion strrev() im Code _tcsrev() verwendet werden, was automatisch zu einem Textmapping auf den korrekten Zeichensatz führt, für den kompiliert wird, wie die nachfolgende Tabelle zeigt.

#define

Kompilierte Version

Beispiel

_UNICODE

Unicode (Wide Character)

_tcsrev wird zu _wcsrev

_MBCS

Multibyte-Zeichen (MBCS)

_tcsrev wird zu _mbsrev

kein Define

Singlebyte-Zeichen (SBCS /ASCII)

_tcsrev wird zu strrev

Jede str#-Funktion besitzt ein korrespondierendes tcs#-Pendant, das stattdessen verwendet werden sollte. Um einen Überblick über alle verfügbaren Mappings und Makros zu bekommen, sollte man einen Blick in die Headerdatei TCHAR.H werfen, die alle Definitionen enthält. Die VC++-Onlinehilfe verweist zusätzlich auf die jeweiligen portablen Funktionen.

Defines

Die str#-Funktionen dürfen nicht mit Unicode Strings eingesetzt werden, da Unicode Strings eingebettete Null-Bytes beinhalten können, die ein Zeichen codieren und nicht das Ende des Strings markieren.

Der nächste wichtige Punkt ist, daß jedes String-Literal von einem TEXT() oder _T() Makro umschlossen sein sollte. Dieses Makro stellt dem String-Literal ein „L“ voran, wenn es sich um ein Unicode-Kompilat handelt, während für MBCS oder ASCII das Literal unverändert bleibt. So wird aus _T("Hello") in

Ziel

Literal

MBCS/ASCII

"Hello"

Unicode

L"Hello"

Wird das Makro _T() beim Kompilieren für Unicode nicht eingesetzt, können entsprechende Compiler-Warnungen auftauchen.

Zu beachten ist ferner: ASCII und Unicode können im gleichen Programm vorkommen, jedoch nicht im gleichen String. Alle MFC-Funktionen mit Ausnahme der Member-Funktionen der Database-Klassen sind Unicode-fähig.

Konvertierung zwischen generischen Typen und ASCII

Visual C++ enthält eine Reihe von nützlichen Makros zur Konvertierung zwischen den unterschiedlichen Zeichenformaten. Die Grundform dieser Makros lautet X2Y(), wobei X das Quell- und Y das Zielformat ist. Mögliche Konvertierungen werden in der nachfolgenden Tabelle zusammengestellt:

Stringtype

Zeigertyp

Abkürzung

ASCII

LPSTR

A

WIDE

LPWSTR

W

OLE

LPOLESTR

OLE

Generic

LPTSTR

T

Const

 

C

Entsprechend dieser Tabelle konvertiert A2W() einen LPSTR in einen LPWSTR, während OLE2T einen LPOLESTR in einen LPTSTR umwandelt.

Zusätzlich stehen auch const-Formen, die mit einem „C“ notiert werden, zur Verfügung. So konvertiert beispielsweise A2CT() einen LPSTR nach LPCTSTR.

Sollen die genannten Konvertiermakros eingesetzt werden, muß das Makro USES_CONVERSION am Anfang des Funktionscodes eingesetzt werden.

Anmerkung

void dummy(LPSTR lpsz)
{
   USES_CONVERSION;
    ...
   LPTSTR szGeneric = A2T(lpsz)
   ...
}

Es sind allerdings zwei wichtige Punkte beim Einsatz der Konvertier-Makros zu beachten:

  • Zum einen sollten diese Makros nicht innerhalb einer häufig zu durchlaufenen Schleife untergebracht werden, da für jede Makro-Ausführung Speicher allokiert wird, so daß der Code der Schleife deutlich langsamer ausgeführt würde. Wenn Konvertierungen notwendig sind, sollten diese außerhalb einer Schleife erfolgen.
  • Das Resultat eines Makros sollte niemals direkt als Ergebnis einer Funktion returniert werden, es sei denn, der Rückgabewert und -typ impliziert, daß eine Kopie der Daten vor der Rückgabe erfolgt.

Speziell letzteres kann zu größeren Problemen führen, wenn es mißachtet wird. Wird beispielsweise von einer Funktion ein LPTSTR returniert, sollte dies nicht wie hier geschehen:

Beispiel

LPTSTR BadReturn(LPSTR lpsz)
{
  USES_CONVERSION;
  ...
  return A2T(lpsz);
}

Vielmehr sollte das Funktionsergebnis als CString zurückgeliefert werden, weil dies impliziert, daß zunächst eine Kopie des Strings erzeugt wird, bevor die Funktion zurückkehrt.

Falscher Code

CString GoodReturn(LPSTR lpsz)
{
  USES_CONVERSION;
  ...
  return A2T(lpsz);
}

Tips, Tricks und Fallen

Richtiger Code

Das Trace-Makro hat einige „Verwandte“, namentlich TRACE0, TRACE1, TRACE2 und TRACE3. Diese Makros erlauben das Spezifizieren eines Format-Strings wie im normalen TRACE-Makro und wahlweise 0, 1, 2 oder 3 Parameter, die nicht notwendigerweise erfordern, daß der Formst-String als Literal in ein _T()-Makro eingeschlossen werden muß. Die Anweisung

TRACE(_T("Dies ist Trace-Statement Nummer %d\n"), 1);

kann daher auch geschrieben werden als

TRACE1("Dies ist Trace_Statement Nummer %d\n", 1);

Das TRACE Statement

Wird Unicode in einer Anwendung eingesetzt und sollen Unicode-Strings im Debugger angezeigt werden, muß über den Befehl Tools | Options | Debug die Option

"Display Unicode Strings"

aktiviert werden.

Unicode-Strings im Debugger ansehen

Vorsicht ist geboten bei Operationen, die auf die Länge eines Strings abstellen bzw. von der Länge eines Strings abhängig sind. So liefert beispielsweise die Methode CString::GetLength() die Anzahl der Zeichen im String, nicht jedoch die Größe des Strings in Bytes.

Soll ein String auf ein CArchive-Objekt geschrieben werden, muß dazu die Anzahl der Zeichen mit der Anzahl der Bytes pro Zeichen multipliziert werden, um die Anzahl der zu schreibenden Bytes zu erhalten.

CString str = _T("Hello, World");
archive.Write( str, str.GetLength( ) * sizeof( TCHAR ) ); 

Stringlänge

Beim Schreiben von ASCII-Textdateien aus Unicode- oder MBCS-Anwendungen ist ebenfalls Vorsicht geboten. Die sicherste und einfachste Vorgehensweise, Textdateien zu schreiben, besteht im Einsatz der MFC-Klasse CStdioFile. Dann braucht man nur die CString-Klasse sowie die Memberfunktionen ReadString() und WriteString() einzusetzen und nichts sollte mehr schieflaufen. Wird jedoch anstelle von

CStdioFile file(...); 
CString str = _T("Ein Beispieltext"); 
file.WriteString(str); 

die Klasse CFile und ihre entsprechenden Methoden Read() und Write() eingesetzt

CFile file(...); 
CString str = _T("Ein Beispieltext"); 
file.Write( str, (str.GetLength()+1) * sizeof( TCHAR ) ); 

dann ergeben sich signifikante Unterschiede.

ASCII-Textdateien

Bedauerlicherweise unterstützen nicht alle Windows-Strukturen Textmapping. So verwendet z.B. die Struktur CHARFORMAT in RichEdit Controls mit Versionsnummern kleiner 2.0 char[] für das Feld szFaceName anstelle eines TCHAR, was erwartet wird. Man darf daher nicht blind "..." nach _T("...") mappen ohne vorher einen Check durchzuführen. In diesem Fall müßte man von TCHAR nach char konvertieren, bevor Daten auf das Feld szFaceName kopiert werden können.

Strukturen und Textmapping

Ein typischer Punkt, bei dem ASCII und Unicode in einer Anwendung gleichzeitig eingesetzt werden, ist das Kopieren von Text in die Zwischenablage. Dies liegt daran, daß das Zwischenablageformat CF_TEXT einen ASCII-Zeichensatz erwartet. Auf NT-Systemen existiert als zusätzliche Option das Format CF_UNICODETEXT, wenn Texte im Unicode-Zeichensatz in die Zwischenablage kopiert werden sollen. Auch hier muß ggf. eine Konvertierung erfolgen, bevor Text in die Zwischenablage kopiert oder von dort eingelesen wird.

Unicode MFC-Bibliotheken installieren

Die Unicode-Versionen der MFC-Bibliotheken werden nicht automatisch auf die Festplatte installiert, es sei denn, dies wird im Rahmen einer benutzerdefinierten Installation so spezifiziert. Für alle übrigen Installationsmethoden werden die Bibliotheken nicht kopiert.

Für den Versuch, eine Anwendung mit MFC Unicode zu kompilieren oder zu starten, ohne daß die MFC Unicode-Dateien vorliegen, erntet man Fehlermeldungen.

Text in Zwischen-ablage kopieren

Um die Unicode-Bibliothek nachträglich zu installieren, kann man wie folgt vorgehen:

  • Setup starten
  • benutzerdefinierte Installation wählen
  • alle Komponenten außer „Microsoft Foundation Class Libraries“ deaktivieren
  • auf den Detail-Button klicken und die Einzelbibliotheken markieren. Dabei ist sowohl „Static Library for Unicode“ als auch „Shared Library for Unicode“ auszuwählen

Anschließend kopiert das Setup die fehlenden Unicode-Bibliotheken auf die Festplatte.

Nachträgliche Installation





Sachgebiet


© 2009-2012 by Alojado Publishing. Alle Rechte vorbehalten. Ausgewiesene Marken gehören ihren jeweiligen Eigentümern.
Mit der Benutzung dieser Seite erkennen Sie die Nutzungsbedingungen und die Datenschutzerklärung an. Der Betreiber übernimmt keine Haftung für den Inhalt verlinkter externer Internetseiten.
Seite erzeugt 2012-05-20 03:27:28 von textarchiv.alojado.de