Stringfunktionen richtig einsetzen
Stringfunktionen mit C++-Plattform-SDK
Seit geraumer Zeit bietet Microsoft das C++-Plattform-SDK kostenfrei zum Download an. Mit der aktuellen Version sind neue, sicherere String-Funktionen hinzugekommen. Wir zeigen, wie Sie diese Funktionen in Ihren Projekten einsetzen.

Seit geraumer Zeit bietet Microsoft das C++-Plattform-SDK kostenfrei zum Download an. Mit der aktuellen Version sind neue, sicherere String-Funktionen hinzugekommen. Der Artikel zeigt, wie Sie diese Funktionen in Ihren Projekten einsetzen.

String-Funktionen bergen immer ein Risiko: Überprüfen Sie nicht genau die Parameter, kann es schnell zum Buffer-Overflow (Puffer-Überlauf) kommen. Einige bösartige Programme nutzen diesen Overflow aus, um eigenen Programm-Code auszuführen.
Die neuen String-Funktionen verringern mit zwei Änderungen die Gefahr des Puffer-Überlaufs:
- Parameter legen die Größe des Ziel-Bereichs fest.
- Ein Rückgabewert vom Typ HRESULT protokolliert die Arbeitsweise.Für alle sicheren String-Funktionen gilt:
- Der Name der Funktion basiert auf einem definierten Schema.
- Es gilt grundlegend die gleiche Syntax.
- Es existiert eine Zeichen- und eine Byte-basierende Variante.
- Einige Funktionen erweitern den Umfang.

Durch diese Festlegungen können Sie die neuen Stringfunktionen leichter handhaben, als es vielleicht die langen Funktions-Namen vermuten lassen. Das Beispiel zeigt die Funktionen StringCchCopy bzw. StringCbCopy in alter wchar_t *wcscpy(wchar_t *dest, wchar_t *src) und neuer Syntax HRESULT StringCchCopy(LPTSTR pszDest, size_t cchDest, LPCTSTR pszSrc);.
Die Parameter bedeuten: pszDest: Pointer auf den Zielbereich, cchDst: Anzahl der Zeichen im Zielbereich und szSrc: Pointer auf die Quelle.

Der Name der Funktion beginnt mit String. Das anschließende Cch besagt, dass Sie die Größe des Zielbereichs in Zeichen angeben. Steht hier ein Cb, wird die Größe des Puffers in Bytes interpretiert. Die Größe des Zielbereichs übergeben Sie mit cchDest bzw. cbDest. Der Rückgabewert von Typ HRESULT gibt Auskunft über den Erfolg der Operation. Microsoft empfiehlt, dies mit den Makros SUCCEEDED oder FAILED auszuwerten.
Der Name der Byte basierenden Variante ist StringCbCopy. Die Parameter sind identisch zu der Zeichen orientierten Version, nur dass Sie an dieser Stelle mit Bytes und nicht mit Zeichen hantieren: HRESULT StringCbCopy (LPTSTR pszDest, size_t cbDest, LPCTSTR pszSrc);. Der Parameter cbDest zeigt die Anzahl der freien Bytes im Zielbereich.
TippDer Typ HRESULT ist je nach Architektur 32 oder 64 Bit breit. Werten Sie das Result mit den Makros SUCCEEDED oder FAILED aus, sind Sie auf der sicheren Seite.
Beachten Sie dazu den hilfreichen Hinweis: Das Visual C++ Express Projekt tststring zeigt an Hand von konkreten Beispielen den Umgang mit den neuen Funktionen. Die enthaltene Sourcedatei tststring.cpp ist ausführlich dokumentiert.
Das Auszug aus tststring.cpp zeigt, wie Sie mit der Funktion StringCchcopy umgehen, und wie Sie den Rückgabewert auswerten:
#define DEST_NUMBER_OF_WCHARS 100
LPCTSTR lpctstrSrc =
__TEXT("Teststring");
WCHAR
wcharDest[DEST_NUMBER_OF_WCHARS];
HRESULT hResult;
hResult = StringCchCopy(wcharDest,
DEST_NUMBER_OF_WCHARS, lpctstrSrc);
if(FAILED(hResult)) {
// Fehlerbehandlung
}
Artenreichtum
Windows unterstützt zwei grundlegende Arten von Strings: CHAR- und WCHAR-Strings.

Für jeden Typ existieren entsprechende Varianten. Funktionen, die mit ANSI-Zeichen (acht Bit breit) umgehen, kennzeichnet ein 'A', Funktionen zum Umgang mit UNICODE (Wide Char, 16 Bit breit) ein 'W' am Ende des Namens. Eigentlich existieren keine String- Funktionen ohne Erweiterung. Dahinter verbergen sich Defines auf Funktionen, die den Systemstandard der verwendeten Windows- Version unterstützen. Das ist bei allen Windows NT basierenden Systemen, wie beispielsweise Windows 2000 und Windows NT, der UNICODE-Standard (WCHAR - Wide String). Somit ergeben sich die drei unterschiedliche Varianten: StringCchCopy - Define auf den Systemstandard (TCHAR) StringCchCopyA - gültig für CHAR StringCchCopyW - gültig für WCHAR
Inklusive der drei nochmals Byte basierenden Varianten können Sie beispielsweise sechs Funktionen wählen, wenn Sie einen String von einem Puffer in einen anderen kopieren wollen. Im Normalfall brauchen Sie sich darüber keine Gedanken zu machen. Die Funktion ohne Erweiterung im Namen ist in der Regel richtig. Dieses Verfahren entspricht dem bis dahin eingesetzten TCHAR-Defines. Beachten Sie bitte: Wenn Sie in Ihrem Programm ASCII-Strings bearbeiten, müssen Sie die Variante mit der Erweiterung 'A' verwenden!

TippWenn Sie Daten zwischen UNICODE- (Wide- Char) und ANSI-Zeichensätzen konvertieren, dienen Ihnen die Funktionen WideCharToMultiByte und MultiByteToWideChar. Mehr Informationen dazu sind in der SDK-Hilfe zu finden.
Zeichensätze
Um Schriftzeichen darzustellen, kennt die Computerwelt zwei grundlegende Varianten. Zeichen der ersten Variante wie beispielsweise die ANSI-Zeichensätze werden mit acht, Zeichen der zweiten Variante mit 16 Bit kodiert. Die Zwischenlösung, Multibyte- Zeichensätze, die Zeichen mal mit acht, mal mit 16 Bit kodiert, erhöht den Verarbeitungsaufwand um ein Vielfaches und sind daher heute kaum mehr anzutreffen. Der ANSI-Zeichensatz wurde 1985 beim Erscheinen von MS Windows 1.0 als Standard- Zeichensatz eingeführt.

Die Kodierung basierte auf einem damals vorliegenden Entwurf von ANSI (American National Standard for Information Processing) und ISO, was letztendlich zum Namen führte. Die auf diese Weise maximal 256 darstellbaren Zeichen reichen aus internationaler Sicht nicht aus. Eine erste Abhilfe schaffte das, mit dem Erscheinen vom MS-DOS, 1987 eingeführte Konzept der Code-Seiten (code pages). Nach dem erwähnten Multibyte-Zeichensätzen wurde der UNICODE-Standard definiert. UNICODE-Zeichen sind prinzipiell 16 Bit breit. Die maximal denkbaren 65 536 Zuordnungen reichen für alle Zeichen dieser Welt aus - inklusive der 21 000 Ideogramme einiger asiatischer Länder. Somit ist UNICODE ein internationaler Standard.
Das geht auch besser
Viele Funktionen gibt es zusätzlich in einer erweiterten Variante, die Sie an der Zeichenfolge Ex im Namen erkennen. Das Ex hat nichts mit Freund oder Freundin zu tun, vielmehr geht es um extended - erweitert. Die erweiterte Variante von StringCchCopy ist also StringCchCopyEx:

HRESULT StringCchCopyEx(
LPTSTR pszDest, size_t cchDest,
LPCTSTR pszSrc,
LPTSTR *ppszDestEnd,
size_t *pcchRemaining,
DWORD dwFlags );
Die Parameter pszDest, cchDest (cbDest) und pszSrc entsprechen denen der normalen Funktion. Neu hinzu gekommen sind:
- ppszDestEnd: Adresse des Pointers, der auf das Ende des Strings im Zielbereich zeigt (auf die terminierende Null).
- pcchRemaining: Pointer auf eine Variable, die die Anzahl der noch verfügbaren Zeichen im Zielbereich enthält.
Die Byte-basierende Version heißt StringCb CopyEx:
HRESULT StringCbCopyEx(
LPTSTR pszDest, size_t cbDest,
LPCTSTR pszSrc,
LPTSTR *ppszDestEnd,
size_t *pcbRemaining,
DWORD dwFlags );
pcbRemaining ist der Pointer auf eine Variable, die die Anzahl der noch freien Bytes im Zielbereich enthält. Für alle Funktionen gilt: Überprüfen Sie immer den Rückgabewert! Verwenden Sie die Daten nur, wenn die Funktion fehlerfrei ausgeführtwurde. Auch die erweiterten Funktionen gibt es in zwei, vom Datentyp abhängigen Varianten: StringCbCopy- ExW und StringCbCopyExA. Die Variante ohne Erweiterung ist wiederum ein Define auf die Standard-String Funktion des Systems.
Die Länge der Kette
Auch die String-Länge bestimmen Sie mit neuen Funktionen. Dabei sind ein Parameter für die Länge desSpeicherbereichs und ein Rückgabewert hinzugekommen. Dieser gibt Auskunft, ob die Funktion erfolgreich arbeiten konnte.

Alte Funktionen konnten Konflikte im Speicher auslösen, wenn eine terminierende Null fehlte. Die Stringlänge konnte sich bis ins Unendliche ausweiten, was nachfolgende Programmteile stören konnte. Wenn Sie die maximal erwartete Länge angeben, verhindern Sie den Fehler. Wieder tritt die Funktion als Byteund als Zeichen-Variante auf. Dabei liefert die Funktion die String-Länge per Zeiger zurück, den Sie beim Aufruf übergeben. Der Wert gilt nur, wenn die Funktion fehlerfrei fährt.
Die Zeichen basierende Variante lautet HRESULT StringCchLength(LPCTSTR psz, size_t cchMax, size_t *pcch) mit den Parametern: psz - Pointer auf den String,

cchMax - Maximal erwartete Zeichen-Anzahl pcch - Zeiger auf die Variable für die ermittelte Zeichen-Anzahl.
Die Byte basierende Variante lautet HRESULT StringCbLength(LPCTSTR psz, size_t cbMax, size_t *pcb) mit den Parametern: cbMax - Maximal erwartete Byte-Anzahl und pcb - Zeiger auf die Variable für die ermittelte Byte-Anzahl.
Als Rückgabewerte können Sie S_OK erwarten. Dann ist die Länge des Strings inklusive der terminierenden Null kleiner oder gleich der in cchMax bzw. cbMax übergebenen Maximal- Länge. Beim Rückgabewert STRSAFE_E_INVALID_PARAMETER ist der übergebene Pointer NULL oder der übergebene Wert cchMax bzw. cbMax größer als der Maximalwert. Die Syntax für Zeichen basierte Ausdrücke lautet STRSAFE_MAX_CCH;, für Bytes STRSAFE_MAX_CCH * sizeof(TCHAR)). Es kann dabei auch sein, dass die ermittelte Stringlänge größer ist als der Maximalwert. Auch hier treten wieder drei, vom Datentyp abhängige Varianten auf:
StringCchLength - gültig für TCHAR StringCchLengthA - gültig für CHAR StringCchLengthW - gültig für WCHAR
Hinter der ersten Funktion verbirgt sich ein Define auf die Variante, welche mit dem Standard- String-Typ des Systems hantiert. Aber das ist ja alles schon vertraut. Somit ist ein Grundstein gelegt, sich mit den neuen Funktionen vertraut zu machen und damit Ihre alten Programme anzupassen.