Typy danych
W podobny sposób (tj. za pomocą
#define) WinAPI wprowadza typy danych do obsługi Unikodu. Mamy więc typ
WCHAR, który reprezentuje pojedynczy znak i jest odpowiednikiem typu
wchar_t (lub ewentualnie
unsigned short, jeśli platforma nie obsługuje
wchar_t). Mamy również
LPWSTR i
LPCWSTR, czyli odpowiedniki
char* i
const char*. "Typów" takich jest więcej, ale myślę, że nie ma co zaprzątać sobie nimi głowy – to wszystko i tak tylko makra.
Neutralne stringi
Jak zapewne wiesz, podając tekst w cudzysłowie musisz go poprzedzić prefiksem
L, aby został potraktowany jako Unicode. Wtedy jednak z kolei kod zawierający takie stringi nie skompiluje się w projekcie skonfigurowanym na użycie ANSI. Tymczasem dobrze by było (zwłaszcza, gdy piszemy bibliotekę albo innego rodzaju kawałek kodu, który ma być używany przez innych), gdyby kompilował się zarówno dla ANSI, jak i Unicode. Można to łatwo osiągnąć przy pomocy makra
TEXT (lub
__TEXT). Przykład dla funkcji
MessageBox:
MessageBox( hWnd, TEXT( "Wyświetlam się i w Unicode, i bez niego." ), NULL, MB_ICONINFORMATION );
Tak więc makro
TEXT "neutralizuje" nam stringi, makro
MessageBox sprawia, że to samo dzieje się z funkcjami, a co z tekstem umieszczonym w zmiennych? Zadbano i o to – istnieje typ
TCHAR (oraz pochodne:
LPTSTR, LPTCSTR itd.), które to typy w zależności od symbolu
UNICODE "zamieniają się" w
CHAR albo
WCHAR (lub pochodne). Na przykład:
Konwersja
Ponieważ programistów Microsoftu (i nie tylko ;-)) cechuje ogromne lenistwo, nie wszystkie funkcje w WinAPI są zaimplementowane zarówno w wersji ANSI jak i Unicode. W WinAPI znajdziemy funkcje mające tylko swój odpowiednik ANSI lub funkcje mające tylko odpowiednik Unicode, np.
ReadDirectoryChangesW nie ma swojego odpowiednika w ANSI. Poza tym dołączając do naszego programu różne biblioteki też zapewne natkniemy się na sytuację, gdy jedna lub kilka z nich nie jest napisana w sposób "neutralny znakowo".
Mieszanie tych dwóch standardów jest więc trudne do uniknięcia, a wiąże się z koniecznością konwersji znaków. Na szczęście w WinAPI znajdziemy funkcje, które taką konwersję nam umożliwią. Nie są one może zbyt wygodne (jak całe WinAPI zresztą), ale to zawsze coś :-). Funkcje te to
MultiByteToWideChar i
WideCharToMultiByte. Na początek omówimy tę pierwszą:
Składnia:
MultiByteToWideChar( UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr,
int cbMultiByte, LPWSTR lpWideCharStr, int cchWideChar )
Przyjrzyjmy się teraz bliżej tej funkcji. Zwraca ona liczbę znaków, które zapisała do bufora. Pierwszy argument powinien zawierać stałą oznaczającą stronę kodową. Dostępne stałe zostały zebrane w tabeli:
Najczęściej konwertujemy tekst w aktualnej stronie kodowej Windowsa, więc jeśli nie za bardzo orientujesz się w stronach kodowych, po prostu przyjmij, że ma tam być
CP_ACP. Kolejny argument to flagi (
dwFlags). Flagi te nie są zbyt istotne, więc możemy w tym parametrze wstawić
0. Następny argument to string ANSI, który ma być przekonwertowany. Czwarty argument to rozmiar tego stringa.
Jeśli w funkcji MultiByteToWideChar podamy rozmiar stringa w parametrze cbMultiByte, to funkcja nie dopisze znaku zerowego (NULL) w wynikowym stringu Unicode. Rozwiązaniem tego problemu jest podanie wartości -1. |
Kolejny parametr to bufor Unicode, oraz jego długość (nie rozmiar!). Tym sposobem przebrnęliśmy przez funkcję
MultiByteToWideChar konwertującą znaki ANSI na Unicode. Oto przykład jej użycia:
wchar_t WideChar[ 100 ];
if( !MultiByteToWideChar( CP_ACP, 0, "Zażółć gęślą jaźń", - 1, WideChar, 100 ) )
{
MessageBoxW( hwnd, L"Nie można przekonwertować napisu!", L"Straszny błąd", 0 );
}
else
{
MessageBoxW( hwnd, WideChar, L"Konwersja OK", 0 );
}
Funkcja
MultiByteToWideChar zwraca
0, jeżeli wystąpił jakiś błąd. Jeśli akurat nie wystąpił, to w tablicy
WideChar znajdzie się nasz napis przekowertowany na Unicode.
Czasem zachodzi też potrzeba konwersji w drugą stronę – string Unicode na ANSI. Do tego służy funkcja
WideCharToMultiByte. Jest ona nieco bardziej złożona, ponieważ siłą rzeczy taka konwersja wiąże się z potencjalną utratą danych (w standardzie Unicode jeden string może zawierać nawet kilkadziesiąt tysięcy różnych znaków, string ANSI – tylko 256). Z tego powodu funkcja potrzebuje "wskazówek", co zrobić ze znakami, których przekonwertować się nie da:
Składnia:
WideCharToMultiByte( UINT CodePage, DWORD dwFlags, LPCWSTR lpWideCharStr, cchWideChar, lpMultiByteStr, int cbMultiByte, LPCSTR lpDefaultChar,
LPBOOL lpUsedDefaultChar )
Pierwszy parametr to strona kodowa, czyli
CP_ACP. Drugi parametr to flagi - nie są one zbyt istotne, więc
0. Potem analogicznie do funkcji
MultiByteToWideChar string Unicode i jego długość, a potem string ANSI i jego rozmiar. Tutaj także występuje haczyk ze znakiem zerowym, tak jak przy
MultiByteToWideChar. Ostatnie dwa parametry są opcjonalne, czyli wielkiej katastrofy nie będzie, jeśli damy tam
NULL.
Dwa ostatnie argumenty funkcji
WideCharToMultiByte pełnią rolę wskazówki, o której przed chwilą wspomnieliśmy. W ciągu znaków Unicode mogą się znajdować znaki nie występujące w danej stronie kodowej ANSI np. jakieś chińskie krzaczki. Ponieważ znaku nie ma w ANSI, musi on zostać zastąpiony przez jakiś inny. Te dwa ostatnie argumenty pozwalają nam właśnie określić jaki znak tam wstawić. Jeśli wystarczy nam standardowy znak systemowy używany w takim wypadku (zwykle jest on wyświetlany jako kwadracik lub kropka), to możemy spokojnie ustawić te parametry na
NULL.
Teraz zobaczmy przykład takiej konwersji:
char MultiByte[ 100 ];
if( !WideCharToMultiByte( CP_ACP, 0, L"Zażółć gęślą jaźń", - 1, MultiByte, 100, NULL, NULL ) )
{
MessageBoxA( hwnd, "Nie można przekonwertować napisu!", "Straszny błąd", 0 );
}
else
{
MessageBoxA( hwnd, MultiByte, "Konwersja OK", 0 );
}