Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować się zalogować lub zmienić katalog.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
W tym samouczku pokazano, jak zastosować aplikację jednojęzyczną i przygotować ją do użycia na całym świecie. Ta aplikacja jest w formie kompletnego rozwiązania wbudowanego w programie Microsoft Visual Studio.
- — omówienie
-
konfigurowanie rozwiązania Hello MUI
- wymagania dotyczące platformy
- wymagania wstępne
- Krok 0: Tworzenie zakodowanych w kodzie interfejsu MUI hello
- Krok 1. Implementowanie podstawowego modułu zasobów
- Krok 2. Kompilowanie podstawowego modułu zasobów
- Krok 3. Tworzenie Resource-Savvy "Hello MUI"
- Krok 4. Globalizacja "Hello MUI"
- Krok 5: Dostosowywanie "Hello MUI"
- dodatkowe zagadnienia dotyczące interfejsu MUI
- Podsumowanie
Przegląd
Począwszy od systemu Windows Vista, sam system operacyjny Windows został zbudowany od podstaw, aby być wielojęzyczną platformą, z dodatkową obsługą, która umożliwia tworzenie wielojęzycznych aplikacji korzystających z modelu zasobów MUI systemu Windows.
W tym samouczku przedstawiono nową obsługę wielojęzycznych aplikacji, obejmując następujące aspekty:
- Korzysta z ulepszonej obsługi platformy MUI, aby łatwo włączyć wielojęzyczne aplikacje.
- Rozszerza wielojęzyczne aplikacje do uruchamiania w wersjach systemu Windows wcześniejszych niż Windows Vista.
- Dotyczy to dodatkowych zagadnień związanych z opracowywaniem wyspecjalizowanych aplikacji wielojęzycznych, takich jak aplikacje konsolowe.
Te linki ułatwiają szybsze odświeżanie pojęć związanych z internacjonalizacją i interfejsem MUI:
- Krótki przegląd internacjonalizacji.
- pojęcia dotyczące interfejsu MUI.
- używanie interfejsu MUI do tworzenia aplikacji Win32.
Pomysł za Hello MUI
Prawdopodobnie znasz klasyczną aplikację Hello World, która ilustruje podstawowe pojęcia dotyczące programowania. W tym samouczku przedstawiono podobne podejście, aby zilustrować sposób użycia modelu zasobów MUI w celu zaktualizowania aplikacji monolingual w celu utworzenia wielojęzycznej wersji o nazwie Hello MUI.
Notatka
Zadania opisane w tym samouczku zostały opisane w szczegółowych krokach ze względu na precyzję, z jaką należy wykonać te działania, oraz potrzebę wyjaśnienia szczegółów deweloperom, którzy mają niewielkie doświadczenie z tymi zadaniami.
Konfigurowanie rozwiązania Hello MUI
W tych krokach opisano sposób przygotowania do utworzenia rozwiązania Hello MUI.
Wymagania dotyczące platformy
W tym samouczku należy skompilować przykłady kodu przy użyciu zestawu Windows Software Development Kit (SDK) dla systemów Windows 7 i Visual Studio 2008. Zestaw Windows 7 SDK zostanie zainstalowany w systemach Windows XP, Windows Vista i Windows 7, a przykładowe rozwiązanie można utworzyć na dowolnej z tych wersji systemu operacyjnego.
Wszystkie przykłady kodu w tym samouczku są przeznaczone do wykonywania w wersjach x86 i x64 systemów Windows XP, Windows Vista i Windows 7. Określone części, które nie będą działać w systemie Windows XP, są wywoływane tam, gdzie jest to konieczne.
Warunki wstępne
Zainstaluj program Visual Studio 2008.
Aby uzyskać więcej informacji, zobacz Centrum deweloperów programu Visual Studio.
Zainstaluj zestaw Windows SDK dla systemu Windows 7.
Można go zainstalować na stronie Windows SDK w Centrum Deweloperów Windows . Zestaw SDK zawiera narzędzia do tworzenia aplikacji dla wersji systemu operacyjnego, począwszy od systemu Windows XP za pośrednictwem najnowszych.
Notatka
Jeśli pakiet nie jest instalowany w lokalizacji domyślnej lub jeśli nie instalujesz na dysku systemowym, który jest zwykle dyskiem C, zanotuj ścieżkę instalacji.
Skonfiguruj parametry wiersza polecenia programu Visual Studio.
- Otwórz okno polecenia programu Visual Studio.
- Wpisz ustaw ścieżkę.
- Upewnij się, że zmienna path zawiera ścieżkę folderu bin zestawu WINDOWS 7 SDK: ... Microsoft SDKs\Windows\v7.0\bin
Zainstaluj pakiet dodatku Microsoft NLS downlevel APIs.
Notatka
W kontekście tego samouczka ten pakiet jest niezbędny tylko w przypadku dostosowywania aplikacji do uruchamiania w wersjach systemu Windows wcześniejszych niż Windows Vista. Zobacz Krok 5: Dostosowywanie interfejsu MUI hello.
Pobierz i zainstaluj pakiet, który nie jest już dostępny w Centrum pobierania Microsoft . Użyj interfejsów API globalizacji ICU w systemie Windows 10 maj 2019 Update i nowszych wersjach.
Podobnie jak w przypadku zestawu Windows SDK, jeśli nie instalujesz pakietu w lokalizacji domyślnej lub jeśli nie instalujesz na dysku systemowym, który jest zwykle dyskiem C, zanotuj ścieżkę instalacji.
Jeśli platforma programowa to Windows XP lub Windows Server 2003, upewnij się, że Nlsdl.dll jest zainstalowana i zarejestrowana poprawnie.
- Przejdź do folderu "redist" w lokalizacji ścieżki instalacji.
- Uruchom odpowiedni pakiet redystrybucyjny Nlsdl.*.exe, na przykład nlsdl.x86.exe. Ten krok instaluje i rejestruje Nlsdl.dll.
Notatka
Jeśli tworzysz aplikację korzystającą z interfejsu MUI i musi działać w wersjach systemu Windows wcześniejszych niż Windows Vista, Nlsdl.dll musi znajdować się na docelowej platformie systemu Windows. W większości przypadków oznacza to, że aplikacja musi dołączyć i zainstalować redystrybucyjny instalator Nlsdl (a nie tylko skopiować sam Nlsdl.dll).
Krok 0. Tworzenie zakodowanej funkcji Hello MUI
Ten samouczek rozpoczyna się od jednojęzycznej wersji aplikacji Hello MUI. Aplikacja zakłada użycie języka programowania C++, szerokich ciągów znaków i funkcji MessageBoxW dla danych wyjściowych.
Zacznij od utworzenia początkowej aplikacji GuiStep_0, a także rozwiązania HelloMUI, które zawiera wszystkie aplikacje w tym samouczku.
W programie Visual Studio 2008 utwórz nowy projekt. Użyj następujących ustawień i wartości:
- Typ projektu: w obszarze Visual C++ wybierz pozycję Win32, a następnie w obszarze Zainstalowane szablony programu Visual Studio wybierz pozycję Projekt Win32.
- Nazwa: GuiStep_0.
- Lokalizacja: ProjectRootDirectory (późniejsze kroki odwołują się do tego katalogu).
- Nazwa rozwiązania: HelloMUI.
- Wybierz Utwórz katalog dla rozwiązania.
- W Kreatorze aplikacji Win32 wybierz domyślny typ aplikacji: Aplikacja systemu Windows.
Skonfiguruj model wątków projektu:
W Eksploratorze rozwiązań kliknij prawym przyciskiem myszy projekt GuiStep_0, a następnie wybierz polecenie Właściwości.
W oknie dialogowym Strony właściwości projektu:
- Na liście rozwijanej w lewym górnym rogu ustaw pozycję Konfiguracja na Wszystkie konfiguracje.
- W obszarze Właściwości konfiguracji rozwiń węzeł C/C++, wybierz pozycję Generowanie kodu i ustaw pozycję Biblioteka uruchomieniowa: Debugowanie wielowątkowe (/MTd).
Zastąp zawartość GuiStep_0.cpp następującym kodem:
// GuiStep_0.cpp : Defines the entry point for the application. // #include "stdafx.h" #include "GuiStep_0.h" int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { UNREFERENCED_PARAMETER(hInstance); UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); UNREFERENCED_PARAMETER(nCmdShow); MessageBoxW(NULL,L"Hello MUI",L"HelloMUI!",MB_OK | MB_ICONINFORMATION); return 0; }Skompiluj i uruchom aplikację.
Powyższy prosty kod źródłowy został zaprojektowany w sposób umożliwiający łatwe osadzenie, czyli wprowadzenie na stałe, całej wyświetlanej treści—w tym przypadku tekstu "Hello MUI". Ten wybór ogranicza użyteczność aplikacji dla użytkowników, którzy nie rozpoznają angielskiego słowa "Hello." Ponieważ MUI jest angielskim akronimem związanym z technologią, zakłada się, że w tym samouczku ciąg pozostaje "MUI" dla wszystkich języków.
Krok 1. Implementowanie podstawowego modułu zasobów
Platforma Microsoft Win32 od dawna udostępnia możliwości deweloperów aplikacji w celu oddzielenia danych zasobów interfejsu użytkownika od kodu źródłowego aplikacji. Ta separacja jest w postaci modelu zasobów Win32, w którym ciągi, mapy bitowe, ikony, komunikaty i inne elementy zwykle wyświetlane użytkownikowi są pakowane w odrębną sekcję pliku wykonywalnego, oddzieloną od kodu wykonywalnego.
Aby zilustrować tę separację między kodem wykonywalnym a pakowaniem danych zasobów, w tym kroku samouczka wcześniej zakodowany ciąg "Hello" (zasób "en-US") jest umieszczany w sekcji zasobów modułu DLL w projekcie HelloModule_en_us.
Ta biblioteka DLL Win32 może również zawierać funkcje wykonywalne typu biblioteki (tak jak każda inna biblioteka DLL). Jednak aby pomóc skupić się na aspektach związanych z zasobami Win32, pozostawiamy kod DLL w czasie wykonywania pominięty w dllmain.cpp. W kolejnych sekcjach tego samouczka używane są dane zasobów HelloModule tworzone tutaj, a także przedstawiono odpowiedni kod środowiska uruchomieniowego.
Aby skonstruować moduł zasobów Win32, zacznij od utworzenia biblioteki DLL z wyciętą biblioteką dllmain:
Dodaj nowy projekt do rozwiązania HelloMUI:
- W menu Plik wybierz pozycję Dodaj, a następnie pozycję Nowy projekt.
- Typ projektu: Projekt Win32.
- Nazwa: HelloModule_en_us.
- Lokalizacja: ProjectRootDirectory\HelloMUI.
- W Kreatorze aplikacji Win32 wybierz pozycję Typ aplikacji: DLL.
Skonfiguruj model wątkowania tego projektu:
W Eksploratorze rozwiązań kliknij prawym przyciskiem myszy projekt HelloModule_en_us i wybierz polecenie Właściwości.
W oknie dialogowym Strony właściwości projektu:
- Na liście rozwijanej w lewym górnym rogu ustaw pozycję Konfiguracja na Wszystkie konfiguracje.
- W obszarze Właściwości konfiguracji rozwiń węzeł C/C++, wybierz pozycję Generowanie kodu i ustaw Bibliotekę uruchomieniową: Debugowanie wielowątkowe (/MTd).
Sprawdź dllmain.cpp. (Możesz dodać makra UNREFERENCED_PARAMETER do wygenerowanego kodu, jak mamy tutaj, aby umożliwić kompilowanie na poziomie ostrzeżenia 4).
// dllmain.cpp : Defines the entry point for the DLL application. #include "stdafx.h" BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { UNREFERENCED_PARAMETER(hModule); UNREFERENCED_PARAMETER(lpReserved); switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; }Dodaj plik definicji zasobów HelloModule.rc do projektu:
W projekcie HelloModule_en_us kliknij prawym przyciskiem myszy folder Pliki zasobów, a następnie wybierz polecenie Dodaj, a potem wybierz Nowy element.
W oknie dialogowym Dodawanie nowego elementu wybierz następujące opcje:
- Kategorie: w obszarze Visual C++ wybierz pozycję Zasób, a następnie w obszarze "Zainstalowane szablony programu Visual Studio" wybierz pozycję Plik zasobu (.rc).
- Nazwa: HelloModule.
- Lokalizacja: zaakceptuj wartość domyślną.
- Kliknij przycisk Dodaj.
Określ, że nowy plik HelloModule.rc ma zostać zapisany jako Unicode:
- W Eksploratorze rozwiązań kliknij prawym przyciskiem myszy pozycję HelloModule.rc, a następnie wybierz pozycję Wyświetl kod.
- Jeśli zostanie wyświetlony komunikat informujący o tym, że plik jest już otwarty i, czy chcesz go zamknąć, kliknij przycisk Tak.
- Po wyświetleniu pliku jako tekstu wybierz menu Plik, a następnie wybierz pozycję Zaawansowane opcje zapisywania.
- W obszarze Kodowanie określ wartość Unicode — kodowa strona 1200.
- Kliknij przycisk OK.
- Zapisz i zamknij plik HelloModule.rc.
Dodaj tabelę ciągów zawierającą ciąg "Hello":
W widoku zasobów kliknij prawym przyciskiem myszy pozycję HelloModule.rc i wybierz polecenie Dodaj zasób.
Wybierz Tabelę ciągów.
Kliknij pozycję Nowy.
Dodaj ciąg do tabeli ciągów:
- Identyfikator: HELLO_MUI_STR_0.
- Wartość: 0.
- Podpis: Witaj.
Jeśli wyświetlisz teraz tekst HelloModule.rc, zobaczysz różne elementy kodu źródłowego specyficznego dla zasobu. Największe zainteresowanie budzi sekcja opisująca ciąg "Hello".
///////////////////////////////////////////////////////////////////////////// // // String Table // STRINGTABLE BEGIN HELLO_MUI_STR_0 "Hello" ENDTen ciąg "Hello" to zasób, który musi zostać zlokalizowany (czyli przetłumaczony) na każdy język, który aplikacja ma nadzieję obsługiwać. Na przykład projekt HelloModule_ta_in (który zostanie utworzony w następnym kroku) będzie zawierać własną zlokalizowaną wersję helloModule.rc dla elementu "ta-IN":
///////////////////////////////////////////////////////////////////////////// // // String Table // STRINGTABLE BEGIN HELLO_MUI_STR_0 "வணக்கம்" ENDSkompiluj projekt HelloModule_en_us, aby upewnić się, że nie ma żadnych błędów.
Utwórz sześć dodatkowych projektów w rozwiązaniu HelloMUI (lub tyle, ile chcesz), aby stworzyć sześć kolejnych bibliotek DLL zasobów dla dodatkowych języków. Użyj wartości w tej tabeli:
Nazwa projektu Nazwa pliku .rc Identyfikator ciągu Wartość ciągu Etykieta tekstu HelloModule_de_de HelloModule HELLO_MUI_STR_0 0 Hallo HelloModule_es_es HelloModule HELLO_MUI_STR_0 0 Hola WitajModule_fr_fr HelloModule HELLO_MUI_STR_0 0 Bonjour HelloModule_hi_in HelloModule HELLO_MUI_STR_0 0 Namaste HelloModule_ru_ru HelloModule HELLO_MUI_STR_0 0 Здравствуйте HelloModule_ta_in HelloModule HELLO_MUI_STR_0 0 வணக்கம்
Aby uzyskać więcej informacji na temat struktury i składni plików .rc, zobacz About Resource Files.
Krok 2. Kompilowanie podstawowego modułu zasobów
Korzystając z poprzednich modeli zasobów, utworzenie dowolnego z siedmiu projektów HelloModule spowodowałoby powstanie siedmiu oddzielnych bibliotek DLL. Każda biblioteka DLL zawiera sekcję zasobu z pojedynczym ciągiem zlokalizowanym w odpowiednim języku. Mimo że jest odpowiedni dla historycznego modelu zasobów Win32, ten projekt nie korzysta z interfejsu MUI.
W zestawie SDK systemu Windows Vista i nowszych interfejs MUI umożliwia podział plików wykonywalnych na kod źródłowy i lokalizowalne moduły. Dzięki dodatkowemu dostosowaniu omówionemu w dalszej części kroku 5 można włączyć obsługę wielojęzyczną do uruchamiania w wersjach wcześniejszych niż Windows Vista.
Podstawowe mechanizmy dostępne do dzielenia zasobów z kodu wykonywalnego, począwszy od systemu Windows Vista, to:
- Używanie rc.exe (kompilator RC) z określonymi przełącznikami lub
- Używając narzędzia do podziału stylów po kompilacji o nazwie muirct.exe.
Aby uzyskać więcej informacji, zobacz Resource Utilities.
Dla uproszczenia w tym samouczku użyto muirct.exe do podzielenia pliku wykonywalnego "Hello MUI".
Dzielenie różnych modułów zasobów językowych: omówienie
Proces wieloczęściowy polega na podzieleniu bibliotek DLL na jeden plik wykonywalny HelloModule.dlloraz plik HelloModule.dll.mui dla każdego z siedmiu języków obsługiwanych w tym samouczku. W tym omówieniu opisano wymagane kroki; W następnej sekcji przedstawiono plik polecenia, który wykonuje te kroki.
Najpierw podziel moduł HelloModule.dll, który jest tylko w języku angielskim, używając polecenia:
mkdir .\en-US
muirct.exe -q DoReverseMuiLoc.rcconfig -v 2 -x 0x0409 -g 0x0407 .\HelloModule_en_us.dll .\HelloModule.dll .\en-US\HelloModule.dll.mui
Powyższy wiersz polecenia używa pliku konfiguracji DoReverseMuiLoc.rcconfig. Tego rodzaju plik konfiguracyjny jest zwykle używany przez muirct.exe do dzielenia zasobów między bibliotekę DLL neutralną językowo (LN) i zależne językowo pliki MUI. W tym przypadku plik XML DoReverseMuiLoc.rcconfig (wymieniony w następnej sekcji) wskazuje na wiele typów zasobów, ale wszystkie należą do kategorii "zlokalizowane zasoby" lub plików .mui, i nie ma żadnych zasobów w kategorii neutralnej językowo. Aby uzyskać więcej informacji na temat przygotowywania pliku konfiguracji zasobu, zobacz Przygotowywanie pliku konfiguracji zasobów.
Oprócz utworzenia pliku .mui HelloModule.dllzawierającego angielski ciąg "Hello", muirct.exe osadza również zasób MUI w module HelloModule.dll podczas podziału. Aby prawidłowo załadować w czasie wykonywania odpowiednie zasoby z modułów .mui specyficznych dla języka HelloModule.dll, każdy plik .mui musi mieć dostosowane sumy kontrolne, aby odpowiadały sumom kontrolnym modułu LN neutralnego dla języka bazowego. Odbywa się to za pomocą polecenia takiego jak:
muirct.exe -c HelloModule.dll -e en-US\HelloModule.dll.mui
Podobnie muirct.exe jest wywoływana w celu utworzenia pliku .mui HelloModule.dlldla każdego z pozostałych języków. Jednak w takich przypadkach biblioteka DLL neutralna dla języka jest odrzucana, ponieważ będzie potrzebna tylko pierwsza utworzona biblioteka DLL. Polecenia, które przetwarzają zasoby hiszpańskie i francuskie, wyglądają następująco:
mkdir .\es-ES
muirct.exe -q DoReverseMuiLoc.rcconfig -v 2 -x 0x0C0A -g 0x0407 .\HelloModule_es_es.dll .\HelloModule_discard.dll .\es-ES\HelloModule.dll.mui
muirct.exe -c HelloModule.dll -e es-ES\HelloModule.dll.mui
mkdir .\fr-FR
muirct.exe -q DoReverseMuiLoc.rcconfig -v 2 -x 0x040C -g 0x0407 .\HelloModule_fr_fr.dll .\HelloModule_discard.dll .\fr-FR\HelloModule.dll.mui
muirct.exe -c HelloModule.dll -e fr-FR\HelloModule.dll.mui
Jedną z najważniejszych kwestii, które należy zauważyć w powyższych wierszach polecenia muirct.exe jest to, że flaga -x służy do określania identyfikatora języka docelowego. Wartość podana muirct.exe jest inna dla każdego modułu HelloModule.dll specyficznego dla języka. Ta wartość języka jest kluczowa i jest używana przez muirct.exe do odpowiedniego oznaczania pliku .mui podczas podziału. Nieprawidłowa wartość generuje błędy ładowania zasobów dla tego konkretnego pliku mui w czasie wykonywania. Zobacz Stałe i ciągi identyfikatorów języka, aby uzyskać więcej informacji na temat mapowania nazw języka na identyfikator LCID.
Każdy podzielony plik .mui jest umieszczany w katalogu odpowiadającym jego nazwie języka, bezpośrednio poniżej katalogu, w którym będzie znajdować się katalog neutralne dla języka HelloModule.dll. Aby uzyskać więcej informacji na temat umieszczania plików .MUI, odwiedź Application Deployment.
Dzielenie różnych modułów zasobów językowych: tworzenie plików
W tym samouczku utworzysz plik polecenia zawierający polecenia do podziału różnych plików DLL i wywołasz go ręcznie. Należy pamiętać, że w rzeczywistych pracach programistycznych można zmniejszyć potencjał błędów kompilacji, uwzględniając te polecenia jako zdarzenia przed kompilacją lub po kompilacji w rozwiązaniu HelloMUI, ale wykracza to poza zakres tego samouczka.
Utwórz pliki dla kompilacji debugowania:
Utwórz plik polecenia DoReverseMuiLoc.cmd zawierający następujące polecenia:
mkdir .\en-US muirct.exe -q DoReverseMuiLoc.rcconfig -v 2 -x 0x0409 -g 0x0407 .\HelloModule_en_us.dll .\HelloModule.dll .\en-US\HelloModule.dll.mui muirct.exe -c HelloModule.dll -e en-US\HelloModule.dll.mui mkdir .\de-DE muirct.exe -q DoReverseMuiLoc.rcconfig -v 2 -x 0x0407 -g 0x0407 .\HelloModule_de_de.dll .\HelloModule_discard.dll .\de-DE\HelloModule.dll.mui muirct.exe -c HelloModule.dll -e de-DE\HelloModule.dll.mui mkdir .\es-ES muirct.exe -q DoReverseMuiLoc.rcconfig -v 2 -x 0x0C0A -g 0x0407 .\HelloModule_es_es.dll .\HelloModule_discard.dll .\es-ES\HelloModule.dll.mui muirct.exe -c HelloModule.dll -e es-ES\HelloModule.dll.mui mkdir .\fr-FR muirct.exe -q DoReverseMuiLoc.rcconfig -v 2 -x 0x040C -g 0x0407 .\HelloModule_fr_fr.dll .\HelloModule_discard.dll .\fr-FR\HelloModule.dll.mui muirct.exe -c HelloModule.dll -e fr-FR\HelloModule.dll.mui mkdir .\hi-IN muirct.exe -q DoReverseMuiLoc.rcconfig -v 2 -x 0x0439 -g 0x0407 .\HelloModule_hi_in.dll .\HelloModule_discard.dll .\hi-IN\HelloModule.dll.mui muirct.exe -c HelloModule.dll -e hi-IN\HelloModule.dll.mui mkdir .\ru-RU muirct.exe -q DoReverseMuiLoc.rcconfig -v 2 -x 0x0419 -g 0x0407 .\HelloModule_ru_ru.dll .\HelloModule_discard.dll .\ru-RU\HelloModule.dll.mui muirct.exe -c HelloModule.dll -e ru-RU\HelloModule.dll.mui mkdir .\ta-IN muirct.exe -q DoReverseMuiLoc.rcconfig -v 2 -x 0x0449 -g 0x0407 .\HelloModule_ta_in.dll .\HelloModule_discard.dll .\ta-IN\HelloModule.dll.mui muirct.exe -c HelloModule.dll -e ta-IN\HelloModule.dll.mui pauseUtwórz plik XML DoReverseMuiLoc.rcconfig zawierający następujące wiersze:
<?xml version="1.0" encoding="utf-8"?> <localization> <resources> <win32Resources fileType="Application"> <neutralResources> </neutralResources> <localizedResources> <resourceType typeNameId="#1"/> <resourceType typeNameId="#10"/> <resourceType typeNameId="#1024"/> <resourceType typeNameId="#11"/> <resourceType typeNameId="#12"/> <resourceType typeNameId="#13"/> <resourceType typeNameId="#14"/> <resourceType typeNameId="#15"/> <resourceType typeNameId="#16"/> <resourceType typeNameId="#17"/> <resourceType typeNameId="#18"/> <resourceType typeNameId="#19"/> <resourceType typeNameId="#2"/> <resourceType typeNameId="#20"/> <resourceType typeNameId="#2110"/> <resourceType typeNameId="#23"/> <resourceType typeNameId="#240"/> <resourceType typeNameId="#3"/> <resourceType typeNameId="#4"/> <resourceType typeNameId="#5"/> <resourceType typeNameId="#6"/> <resourceType typeNameId="#7"/> <resourceType typeNameId="#8"/> <resourceType typeNameId="#9"/> <resourceType typeNameId="HTML"/> <resourceType typeNameId="MOFDATA"/> </localizedResources> </win32Resources> </resources> </localization>Skopiuj DoReverseMuiLoc.cmd i DoReverseMuiLoc.rcconfig do ProjectRootDirectory\HelloMUI\Debug.
Otwórz wiersz polecenia programu Visual Studio 2008 i przejdź do katalogu Debug.
Uruchom DoReverseMuiLoc.cmd.
Podczas tworzenia kompilacji wydania skopiujesz te same pliki DoReverseMuiLoc.cmd i DoReverseMuiLoc.rcconfig do katalogu Release i uruchomisz tam plik polecenia.
Krok 3. Tworzenie Resource-Savvy "Hello MUI"
Opierając się na początkowym, zakodowanym przykładzie GuiStep_0.exe powyżej, można rozszerzyć zasięg aplikacji na użytkowników różnych języków, uwzględniając model zasobów Win32. Nowy kod czasu wykonywania zaprezentowany w tym kroku obejmuje logikę ładowania modułu (LoadLibraryEx) oraz pobierania ciągu (LoadString).
Dodaj nowy projekt do rozwiązania HelloMUI (przy użyciu menu wyboru Plik, Dodaj i Nowy projekt) z następującymi ustawieniami i wartościami:
- Typ projektu: Projekt Win32.
- Nazwa: GuiStep_1.
- Lokalizacja: zaakceptuj wartość domyślną.
- W Kreatorze aplikacji Win32 wybierz domyślny typ aplikacji: Aplikacja systemu Windows.
Ustaw ten projekt do uruchomienia z poziomu programu Visual Studio i skonfiguruj model wątków:
W Eksploratorze rozwiązań kliknij prawym przyciskiem myszy projekt GuiStep_1 i wybierz polecenie Ustaw jako projekt startowy.
Kliknij go ponownie prawym przyciskiem myszy i wybierz polecenie Właściwości.
W oknie dialogowym Strony właściwości projektu:
- Na liście rozwijanej w lewym górnym rogu ustaw pozycję Konfiguracja na Wszystkie konfiguracje.
- W obszarze Właściwości konfiguracji rozwiń węzeł C/C++, wybierz pozycję Generowanie kodu i ustaw pozycję Biblioteka środowiska uruchomieniowego: Debugowanie wielowątkowe (/MTd).
Zastąp zawartość GuiStep_1.cpp następującym kodem:
// GuiStep_1.cpp : Defines the entry point for the application. // #include "stdafx.h" #include "GuiStep_1.h" #include "..\HelloModule_en_us\resource.h" #define SUFFICIENTLY_LARGE_STRING_BUFFER (MAX_PATH*2) #define SUFFICIENTLY_LARGE_ERROR_BUFFER (1024*2) #define HELLO_MODULE_CONTRIVED_FILE_PATH (L"HelloModule.dll") int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { UNREFERENCED_PARAMETER(hInstance); UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); UNREFERENCED_PARAMETER(nCmdShow); // The following code presents a hypothetical, yet common use pattern of MUI technology WCHAR displayBuffer[SUFFICIENTLY_LARGE_ERROR_BUFFER]; // 1. Basic application obtains access to the proper resource container // for standard Win32 resource loading this is normally a PE module - use LoadLibraryEx // LoadLibraryEx is the preferred alternative for resource modules as used below because it // provides increased security and performance over that of LoadLibrary HMODULE resContainer = LoadLibraryExW(HELLO_MODULE_CONTRIVED_FILE_PATH,NULL,LOAD_LIBRARY_AS_IMAGE_RESOURCE | LOAD_LIBRARY_AS_DATAFILE); if(!resContainer) { swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"FAILURE: Unable to load the resource container module, last error = %d.",GetLastError()); MessageBoxW(NULL,displayBuffer,L"HelloMUI ERROR!",MB_OK | MB_ICONERROR); return 1; // exit } // 2. Application parses the resource container to find the appropriate item WCHAR szHello[SUFFICIENTLY_LARGE_STRING_BUFFER]; if(LoadStringW(resContainer,HELLO_MUI_STR_0,szHello,SUFFICIENTLY_LARGE_STRING_BUFFER) == 0) { swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"FAILURE: Unable to load the resource string, last error = %d.",GetLastError()); MessageBoxW(NULL,displayBuffer,L"HelloMUI ERROR!",MB_OK | MB_ICONERROR); FreeLibrary(resContainer); return 1; // exit } // 3. Application presents the discovered resource to the user via UI swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"%s MUI",szHello); MessageBoxW(NULL,displayBuffer,L"HelloMUI",MB_OK | MB_ICONINFORMATION); // 4. Application cleans up memory associated with the resource container after this item is no longer needed. if(!FreeLibrary(resContainer)) { swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"FAILURE: Unable to unload the resource container, last error = %d.",GetLastError()); MessageBoxW(NULL,displayBuffer,L"HelloMUI ERROR!",MB_OK | MB_ICONERROR); return 1; // exit } return 0; }Skompiluj i uruchom aplikację. Dane wyjściowe będą wyświetlane w języku ustawionym obecnie jako język wyświetlania komputera (pod warunkiem, że jest to jeden z siedmiu języków, które utworzyliśmy).
Krok 4. Globalizacja "Hello MUI"
Mimo że w poprzednim przykładzie można wyświetlić dane wyjściowe w różnych językach, brakuje mu tego w kilku obszarach. Być może najbardziej zauważalne jest to, że aplikacja jest dostępna tylko w małym podzestawie języków w porównaniu z samym systemem operacyjnym Windows. Jeśli na przykład aplikacja GuiStep_1 z poprzedniego kroku została zainstalowana w japońskiej kompilacji systemu Windows, prawdopodobnie wystąpią błędy lokalizacji zasobów.
Aby rozwiązać tę sytuację, masz dwie podstawowe opcje:
- Upewnij się, że uwzględniono zasoby w wstępnie określonym języku rezerwowym.
- Umożliwia użytkownikowi końcowemu skonfigurowanie preferencji językowych z podzbioru języków obsługiwanych przez aplikację.
Spośród tych opcji, ta w pełni zapewniająca język rezerwowy jest wysoce zalecana i jest implementowana w tym samouczku poprzez przekazanie flagi -g do muirct.exe, jak pokazano powyżej. Ta flaga informuje muirct.exe o utworzeniu określonego języka (de-DE / 0x0407) ostatecznego języka rezerwowego skojarzonego z modułem dll neutralnym dla języka (HelloModule.dll). W czasie wykonywania ten parametr jest używany jako ostatnia metoda generowania tekstu do wyświetlania użytkownikowi. W przypadku, gdy ostateczny język rezerwowy nie zostanie znaleziony i w pliku binarnym neutralnym dla języka nie będzie dostępny odpowiedni zasób, ładowanie zasobu kończy się niepowodzeniem. W związku z tym należy dokładnie określić scenariusze, które aplikacja może napotkać i odpowiednio zaplanować ostateczny język rezerwowy.
Druga opcja umożliwiająca konfigurowanie preferencji językowych i ładowania zasobów na podstawie tej hierarchii zdefiniowanej przez użytkownika może znacznie zwiększyć zadowolenie klientów. Niestety, komplikuje również funkcjonalność wymaganą w aplikacji.
W tym kroku samouczka jest używany uproszczony mechanizm plików tekstowych umożliwiający niestandardową konfigurację języka użytkownika. Plik tekstowy jest analizowany w czasie wykonywania przez aplikację, a przeanalizowana i zweryfikowana lista języków jest używana podczas ustanawiania niestandardowej listy rezerwowej. Po ustanowieniu niestandardowej listy rezerwowej interfejsy API systemu Windows będą ładować zasoby zgodnie z pierwszeństwem języka określonym na tej liście. Pozostała część kodu jest podobna do tej, która znajduje się w poprzednim kroku.
Dodaj nowy projekt do rozwiązania HelloMUI (przy użyciu menu wyboru Plik, Dodaj i Nowy projekt) z następującymi ustawieniami i wartościami:
- Typ projektu: Projekt Win32.
- Nazwa: GuiStep_2.
- Lokalizacja: zaakceptuj wartość domyślną.
- W Kreatorze aplikacji Win32 wybierz domyślny typ aplikacji: Aplikacja systemu Windows.
Ustaw ten projekt do uruchomienia z poziomu programu Visual Studio i skonfiguruj model wątków:
W Eksploratorze rozwiązań kliknij prawym przyciskiem myszy projekt GuiStep_2 i wybierz polecenie Ustaw jako projekt startowy.
Kliknij go ponownie prawym przyciskiem myszy i wybierz polecenie Właściwości.
W oknie dialogowym Strony właściwości projektu:
- Na liście rozwijanej w lewym górnym rogu ustaw pozycję Konfiguracja na Wszystkie konfiguracje.
- W obszarze Właściwości konfiguracji rozwiń C/C++, wybierz Generowanie kodu i ustaw Bibliotekę środowiska uruchomieniowego na Debugowanie wielowątkowe (/MTd).
Zastąp zawartość GuiStep_2.cpp następującym kodem:
// GuiStep_2.cpp : Defines the entry point for the application. // #include "stdafx.h" #include "GuiStep_2.h" #include <strsafe.h> #include "..\HelloModule_en_us\resource.h" #define SUFFICIENTLY_LARGE_STRING_BUFFER (MAX_PATH*2) #define USER_CONFIGURATION_STRING_BUFFER (((LOCALE_NAME_MAX_LENGTH+1)*5)+1) #define SUFFICIENTLY_LARGE_ERROR_BUFFER (1024*2) #define HELLO_MODULE_CONTRIVED_FILE_PATH (L"HelloModule.dll") BOOL GetMyUserDefinedLanguages(WCHAR * langStr, DWORD langStrSize); BOOL ConvertMyLangStrToMultiLangStr(WCHAR * langStr, WCHAR * langMultiStr, DWORD langMultiStrSize); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { UNREFERENCED_PARAMETER(hInstance); UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); UNREFERENCED_PARAMETER(nCmdShow); // The following code presents a hypothetical, yet common use pattern of MUI technology WCHAR displayBuffer[SUFFICIENTLY_LARGE_ERROR_BUFFER]; // 1. Application starts by applying any user defined language preferences // (language setting is potentially optional for an application that wishes to strictly use OS system language fallback) // 1a. Application looks in pre-defined location for user preferences (registry, file, web, etc.) WCHAR userLanguagesString[USER_CONFIGURATION_STRING_BUFFER*2]; if(!GetMyUserDefinedLanguages(userLanguagesString,USER_CONFIGURATION_STRING_BUFFER*2)) { swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"FAILURE: Unable to find the user defined language configuration, last error = %d.",GetLastError()); MessageBoxW(NULL,displayBuffer,L"HelloMUI ERROR!",MB_OK | MB_ICONERROR); return 1; // exit } // 1b. Application converts the user defined 'readable' languages to the proper multi-string 'less readable' language name format WCHAR userLanguagesMultiString[USER_CONFIGURATION_STRING_BUFFER]; if(!ConvertMyLangStrToMultiLangStr(userLanguagesString,userLanguagesMultiString,USER_CONFIGURATION_STRING_BUFFER)) { swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"FAILURE: Unable to convert the user defined language configuration to multi-string, last error = %d.",GetLastError()); MessageBoxW(NULL,displayBuffer,L"HelloMUI ERROR!",MB_OK | MB_ICONERROR); return 1; // exit } // 1c. Application now sets the appropriate fallback list DWORD langCount = 0; // next commented out line of code could be used on Windows 7 and later // using SetProcessPreferredUILanguages is recomended for new applications (esp. multi-threaded applications) // if(!SetProcessPreferredUILanguages(MUI_LANGUAGE_NAME,userLanguagesMultiString,&langCount) || langCount == 0) // the following line of code is supported on Windows Vista and later if(!SetThreadPreferredUILanguages(MUI_LANGUAGE_NAME,userLanguagesMultiString,&langCount) || langCount == 0) { swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"FAILURE: Unable to set the user defined languages, last error = %d.",GetLastError()); MessageBoxW(NULL,displayBuffer,L"HelloMUI ERROR!",MB_OK | MB_ICONERROR); return 1; // exit } // NOTES on step #1: // an application developer that makes the assumption the fallback list provided by the // system / OS is entirely sufficient may or may not be making a good assumption based // mostly on: // A. your choice of languages installed with your application // B. the languages on the OS at application install time // C. the OS users propensity to install/uninstall language packs // D. the OS users propensity to change laguage settings // 2. Application obtains access to the proper resource container // for standard Win32 resource loading this is normally a PE module - use LoadLibraryEx // LoadLibraryEx is the preferred alternative for resource modules as used below because it // provides increased security and performance over that of LoadLibrary HMODULE resContainer = LoadLibraryExW(HELLO_MODULE_CONTRIVED_FILE_PATH,NULL,LOAD_LIBRARY_AS_IMAGE_RESOURCE | LOAD_LIBRARY_AS_DATAFILE); if(!resContainer) { swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"FAILURE: Unable to load the resource container module, last error = %d.",GetLastError()); MessageBoxW(NULL,displayBuffer,L"HelloMUI ERROR!",MB_OK | MB_ICONERROR); return 1; // exit } // 3. Application parses the resource container to find the appropriate item WCHAR szHello[SUFFICIENTLY_LARGE_STRING_BUFFER]; if(LoadStringW(resContainer,HELLO_MUI_STR_0,szHello,SUFFICIENTLY_LARGE_STRING_BUFFER) == 0) { swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"FAILURE: Unable to load the resource string, last error = %d.",GetLastError()); MessageBoxW(NULL,displayBuffer,L"HelloMUI ERROR!",MB_OK | MB_ICONERROR); FreeLibrary(resContainer); return 1; // exit } // 4. Application presents the discovered resource to the user via UI swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"%s MUI",szHello); MessageBoxW(NULL,displayBuffer,L"HelloMUI",MB_OK | MB_ICONINFORMATION); // 5. Application cleans up memory associated with the resource container after this item is no longer needed. if(!FreeLibrary(resContainer)) { swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"FAILURE: Unable to unload the resource container, last error = %d.",GetLastError()); MessageBoxW(NULL,displayBuffer,L"HelloMUI ERROR!",MB_OK | MB_ICONERROR); return 1; // exit } return 0; } BOOL GetMyUserDefinedLanguages(WCHAR * langStr, DWORD langStrSize) { BOOL rtnVal = FALSE; // very simple implementation - assumes that first 'langStrSize' characters of the // L".\\langs.txt" file comprises a string of one or more languages HANDLE langConfigFileHandle = CreateFileW(L".\\langs.txt", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(langConfigFileHandle != INVALID_HANDLE_VALUE) { // clear out the input variables DWORD bytesActuallyRead = 0; if(ReadFile(langConfigFileHandle,langStr,langStrSize*sizeof(WCHAR),&bytesActuallyRead,NULL) && bytesActuallyRead > 0) { rtnVal = TRUE; DWORD nullIndex = (bytesActuallyRead/sizeof(WCHAR) < langStrSize) ? bytesActuallyRead/sizeof(WCHAR) : langStrSize; langStr[nullIndex] = L'\0'; } CloseHandle(langConfigFileHandle); } return rtnVal; } BOOL ConvertMyLangStrToMultiLangStr(WCHAR * langStr, WCHAR * langMultiStr, DWORD langMultiStrSize) { BOOL rtnVal = FALSE; size_t strLen = 0; rtnVal = SUCCEEDED(StringCchLengthW(langStr,USER_CONFIGURATION_STRING_BUFFER*2,&strLen)); if(rtnVal && strLen > 0 && langMultiStr && langMultiStrSize > 0) { WCHAR * langMultiStrPtr = langMultiStr; WCHAR * last = langStr + (langStr[0] == 0xFEFF ? 1 : 0); WCHAR * context = last; WCHAR * next = wcstok_s(last,L",; :",&context); while(next && rtnVal) { // make sure you validate the user input if(SUCCEEDED(StringCchLengthW(last,LOCALE_NAME_MAX_LENGTH,&strLen)) && IsValidLocaleName(next)) { langMultiStrPtr[0] = L'\0'; rtnVal &= SUCCEEDED(StringCchCatW(langMultiStrPtr,(langMultiStrSize - (langMultiStrPtr - langMultiStr)),next)); langMultiStrPtr += strLen + 1; } next = wcstok_s(NULL,L",; :",&context); if(next) last = next; } if(rtnVal && (langMultiStrSize - (langMultiStrPtr - langMultiStr))) // make sure there is a double null term for the multi-string { langMultiStrPtr[0] = L'\0'; } else // fail and guard anyone whom might use the multi-string { langMultiStr[0] = L'\0'; langMultiStr[1] = L'\0'; } } return rtnVal; }Utwórz plik tekstowy Unicode langs.txt zawierający następujący wiersz:
hi-IN ta-IN ru-RU fr-FR es-ES en-USNotatka
Pamiętaj, aby zapisać plik jako Unicode.
Skopiuj langs.txt do katalogu, z którego zostanie uruchomiony program:
- Jeśli działa z poziomu programu Visual Studio, skopiuj go do ProjectRootDirectory\HelloMUI\GuiStep_2.
- Jeśli korzystasz z Eksploratora Windows, skopiuj go do tego samego katalogu co GuiStep_2.exe.
Skompiluj i uruchom projekt. Spróbuj edytować langs.txt, aby różne języki pojawiały się na początku listy.
Krok 5. Dostosowywanie interfejsu "Hello MUI"
Niektóre funkcje czasu wykonywania wymienione do tej pory w tym samouczku są dostępne tylko w Windows Vista i późniejszych wersjach. Możesz ponownie użyć nakładu pracy zainwestowanego w lokalizowanie i dzielenie zasobów, dzięki czemu aplikacja działa na niższych poziomach wersji systemu operacyjnego Windows, takich jak Windows XP. Ten proces obejmuje dostosowanie poprzedniego przykładu w dwóch kluczowych obszarach:
Funkcje ładowania zasobów przed systemem Windows Vista (takie jak LoadString, LoadIcon, LoadBitmap, FormatMessagei inne) nie są obsługujące interfejsu MUI. Aplikacje dostarczane z podzielonymi zasobami (pliki LN i mui) muszą ładować moduły zasobów przy użyciu jednej z następujących dwóch funkcji:
- Jeśli aplikacja ma być uruchamiana tylko w systemie Windows Vista i nowszych wersjach, należy załadować moduły zasobów za pomocą LoadLibraryEx.
- Jeśli aplikacja ma być uruchamiana w wersjach wcześniejszych niż Windows Vista, a także Windows Vista lub nowszych, musi używać LoadMUILibrary, która jest określoną funkcją downlevel udostępnioną w zestawie SDK systemu Windows 7.
Obsługa zarządzania językami i kolejności zastępowania języka oferowana w wersjach systemu operacyjnego przed Windows Vista znacznie różni się od tej w systemie Windows Vista i nowszych wersjach. Z tego powodu aplikacje, które zezwalają na powrót języka skonfigurowanego przez użytkownika, muszą dostosować swoje praktyki zarządzania językami:
- Jeśli aplikacja ma być uruchamiana tylko w systemie Windows Vista i nowszych, ustawienie listy języków przy użyciu SetThreadPreferredUILanguages jest wystarczające.
- Jeśli aplikacja ma być uruchamiana we wszystkich wersjach systemu Windows, należy skonstruować kod, który będzie uruchamiany na platformach obniżających poziom, aby iterować za pośrednictwem listy języków skonfigurowanych przez użytkownika i sondować żądany moduł zasobów. Można to zobaczyć w sekcjach 1c i 2 kodu podanego w dalszej części tego kroku.
Utwórz projekt, który może używać zlokalizowanych modułów zasobów w dowolnej wersji systemu Windows:
Dodaj nowy projekt do rozwiązania HelloMUI (przy użyciu menu wyboru Plik, Dodaj i Nowy projekt) z następującymi ustawieniami i wartościami:
- Typ projektu: Projekt Win32.
- Nazwa: GuiStep_3.
- Lokalizacja: zaakceptuj wartość domyślną.
- W Kreatorze aplikacji Win32 wybierz domyślny typ aplikacji: Aplikacja systemu Windows.
Ustaw ten projekt do uruchomienia z poziomu programu Visual Studio i skonfiguruj model wątków. Ponadto skonfiguruj go, aby dodać niezbędne nagłówki i biblioteki.
Notatka
Ścieżki używane w tym samouczku zakładają, że zestaw SDK systemu Windows 7 i pakiet interfejsów API platformy Microsoft NLS w dół zostały zainstalowane w katalogach domyślnych. Jeśli tak nie jest, zmodyfikuj odpowiednio ścieżki.
W Eksploratorze rozwiązań kliknij prawym przyciskiem myszy projekt GuiStep_3 i wybierz polecenie Ustaw jako projekt startowy.
Kliknij go ponownie prawym przyciskiem myszy i wybierz polecenie Właściwości.
W oknie dialogowym stron właściwości projektu:
Na liście rozwijanej w lewym górnym rogu ustaw pozycję Konfiguracja na Wszystkie konfiguracje.
W obszarze Właściwości konfiguracji rozwiń węzeł C/C++, wybierz pozycję Generowanie kodu i ustaw pozycję Biblioteka środowiska uruchomieniowego: Debugowanie wielowątkowe (/MTd).
Wybierz opcję Ogólne i dodaj do Dodatkowe katalogi dołączania:
- C:\Microsoft NLS Downlevel APIs\Include.
Wybierz pozycję Język i ustaw opcję Traktuj wchar_t jako typ wbudowany: nie (/Zc:wchar_t-).
Wybierz pozycję Zaawansowane i ustaw pozycję Konwencja wywoływania: _stdcall (/Gz).
W obszarze Właściwości konfiguracji rozwiń węzeł Linker, wybierz pozycję Dane wejściowe i dodaj do sekcji Dodatkowe zależności:
- "C:\Program Files\Microsoft SDKs\Windows\v7.0\Lib\MUILoad.lib".
- "C:\Microsoft NLS Downlevel APIs\Lib\x86\Nlsdl.lib".
Zastąp zawartość GuiStep_3.cpp następującym kodem:
// GuiStep_3.cpp : Defines the entry point for the application. // #include "stdafx.h" #include "GuiStep_3.h" #include <strsafe.h> #include <Nlsdl.h> #include <MUILoad.h> #include "..\HelloModule_en_us\resource.h" #define SUFFICIENTLY_LARGE_STRING_BUFFER (MAX_PATH*2) #define USER_CONFIGURATION_STRING_BUFFER (((LOCALE_NAME_MAX_LENGTH+1)*5)+1) #define SUFFICIENTLY_LARGE_ERROR_BUFFER (1024*2) #define HELLO_MODULE_CONTRIVED_FILE_PATH (L"HelloModule.dll") BOOL GetMyUserDefinedLanguages(WCHAR * langStr, DWORD langStrSize); BOOL ConvertMyLangStrToMultiLangStr(WCHAR * langStr, WCHAR * langMultiStr, DWORD langMultiStrSize); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { UNREFERENCED_PARAMETER(hInstance); UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); UNREFERENCED_PARAMETER(nCmdShow); // The following code presents a hypothetical, yet common use pattern of MUI technology WCHAR displayBuffer[SUFFICIENTLY_LARGE_ERROR_BUFFER]; // 1. Application starts by applying any user defined language preferences // (language setting is potentially optional for an application that wishes to strictly use OS system language fallback) // 1a. Application looks in pre-defined location for user preferences (registry, file, web, etc.) WCHAR userLanguagesString[USER_CONFIGURATION_STRING_BUFFER*2]; if(!GetMyUserDefinedLanguages(userLanguagesString,USER_CONFIGURATION_STRING_BUFFER*2)) { swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"FAILURE: Unable to find the user defined language configuration, last error = %d.",GetLastError()); MessageBoxW(NULL,displayBuffer,L"HelloMUI ERROR!",MB_OK | MB_ICONERROR); return 1; // exit } // 1b. Application converts the user defined 'readable' languages to the proper multi-string 'less readable' language name format WCHAR userLanguagesMultiString[USER_CONFIGURATION_STRING_BUFFER]; if(!ConvertMyLangStrToMultiLangStr(userLanguagesString,userLanguagesMultiString,USER_CONFIGURATION_STRING_BUFFER)) { swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"FAILURE: Unable to convert the user defined language configuration to multi-string, last error = %d.",GetLastError()); MessageBoxW(NULL,displayBuffer,L"HelloMUI ERROR!",MB_OK | MB_ICONERROR); return 1; // exit } // 1c. Application now attempts to set the fallback list - this is much different for a down-level // shipping application when compared to a Windows Vista or Windows 7 only shipping application BOOL setSuccess = FALSE; DWORD setLangCount = 0; HMODULE hDLL = GetModuleHandleW(L"kernel32.dll"); if( hDLL ) { typedef BOOL (* SET_PREFERRED_UI_LANGUAGES_PROTOTYPE ) ( DWORD, PCWSTR, PULONG ); SET_PREFERRED_UI_LANGUAGES_PROTOTYPE fp_SetPreferredUILanguages = (SET_PREFERRED_UI_LANGUAGES_PROTOTYPE)NULL; fp_SetPreferredUILanguages = (SET_PREFERRED_UI_LANGUAGES_PROTOTYPE) GetProcAddress(hDLL,"SetProcessPreferredUILanguages"); if( fp_SetPreferredUILanguages ) { // call SetProcessPreferredUILanguages if it is available in Kernel32.dll's export table - Windows 7 and later setSuccess = fp_SetPreferredUILanguages(MUI_LANGUAGE_NAME,userLanguagesMultiString,&setLangCount); } else { fp_SetPreferredUILanguages = (SET_PREFERRED_UI_LANGUAGES_PROTOTYPE) GetProcAddress(hDLL,"SetThreadPreferredUILanguages"); // call SetThreadPreferredUILanguages if it is available in Kernel32.dll's export table - Windows Vista and later if(fp_SetPreferredUILanguages) setSuccess = fp_SetPreferredUILanguages(MUI_LANGUAGE_NAME,userLanguagesMultiString,&setLangCount); } } // 2. Application obtains access to the proper resource container // for standard Win32 resource loading this is normally a PE module // LoadMUILibrary is the preferred alternative for loading of resource modules // when the application is potentially run on OS versions prior to Windows Vista // LoadMUILibrary is available via Windows SDK releases in Windows Vista and later // When available, it is advised to get the most up-to-date Windows SDK (e.g., Windows 7) HMODULE resContainer = NULL; if(setSuccess) // Windows Vista and later OS scenario { resContainer = LoadMUILibraryW(HELLO_MODULE_CONTRIVED_FILE_PATH,MUI_LANGUAGE_NAME,0); } else // this block should only be hit on Windows XP and earlier OS platforms as setSuccess will be TRUE on Windows Vista and later { // need to provide your own fallback mechanism such as the implementation below // in essence the application will iterate through the user configured language list WCHAR * next = userLanguagesMultiString; while(!resContainer && *next != L'\0') { // convert the language name to an appropriate LCID // DownlevelLocaleNameToLCID is available via standalone download package // and is contained in Nlsdl.h / Nlsdl.lib LCID nextLcid = DownlevelLocaleNameToLCID(next,DOWNLEVEL_LOCALE_NAME); // then have LoadMUILibrary attempt to probe for the right .mui module resContainer = LoadMUILibraryW(HELLO_MODULE_CONTRIVED_FILE_PATH,MUI_LANGUAGE_NAME,(LANGID)nextLcid); // increment to the next language name in our list size_t nextStrLen = 0; if(SUCCEEDED(StringCchLengthW(next,LOCALE_NAME_MAX_LENGTH,&nextStrLen))) next += (nextStrLen + 1); else break; // string is invalid - need to exit } // if the user configured list did not locate a module then try the languages associated with the system if(!resContainer) resContainer = LoadMUILibraryW(HELLO_MODULE_CONTRIVED_FILE_PATH,MUI_LANGUAGE_NAME,0); } if(!resContainer) { swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"FAILURE: Unable to load the resource container module, last error = %d.",GetLastError()); MessageBoxW(NULL,displayBuffer,L"HelloMUI ERROR!",MB_OK | MB_ICONERROR); return 1; // exit } // 3. Application parses the resource container to find the appropriate item WCHAR szHello[SUFFICIENTLY_LARGE_STRING_BUFFER]; if(LoadStringW(resContainer,HELLO_MUI_STR_0,szHello,SUFFICIENTLY_LARGE_STRING_BUFFER) == 0) { swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"FAILURE: Unable to load the resource string, last error = %d.",GetLastError()); MessageBoxW(NULL,displayBuffer,L"HelloMUI ERROR!",MB_OK | MB_ICONERROR); FreeLibrary(resContainer); return 1; // exit } // 4. Application presents the discovered resource to the user via UI swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"%s MUI",szHello); MessageBoxW(NULL,displayBuffer,L"HelloMUI",MB_OK | MB_ICONINFORMATION); // 5. Application cleans up memory associated with the resource container after this item is no longer needed. if(!FreeMUILibrary(resContainer)) { swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"FAILURE: Unable to unload the resource container, last error = %d.",GetLastError()); MessageBoxW(NULL,displayBuffer,L"HelloMUI ERROR!",MB_OK | MB_ICONERROR); return 1; // exit } return 0; } BOOL GetMyUserDefinedLanguages(WCHAR * langStr, DWORD langStrSize) { BOOL rtnVal = FALSE; // very simple implementation - assumes that first 'langStrSize' characters of the // L".\\langs.txt" file comprises a string of one or more languages HANDLE langConfigFileHandle = CreateFileW(L".\\langs.txt", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(langConfigFileHandle != INVALID_HANDLE_VALUE) { // clear out the input variables DWORD bytesActuallyRead = 0; if(ReadFile(langConfigFileHandle,langStr,langStrSize*sizeof(WCHAR),&bytesActuallyRead,NULL) && bytesActuallyRead > 0) { rtnVal = TRUE; DWORD nullIndex = (bytesActuallyRead/sizeof(WCHAR) < langStrSize) ? bytesActuallyRead/sizeof(WCHAR) : langStrSize; langStr[nullIndex] = L'\0'; } CloseHandle(langConfigFileHandle); } return rtnVal; } BOOL ConvertMyLangStrToMultiLangStr(WCHAR * langStr, WCHAR * langMultiStr, DWORD langMultiStrSize) { BOOL rtnVal = FALSE; size_t strLen = 0; rtnVal = SUCCEEDED(StringCchLengthW(langStr,USER_CONFIGURATION_STRING_BUFFER*2,&strLen)); if(rtnVal && strLen > 0 && langMultiStr && langMultiStrSize > 0) { WCHAR * langMultiStrPtr = langMultiStr; WCHAR * last = langStr + (langStr[0] == 0xFEFF ? 1 : 0); WCHAR * context = last; WCHAR * next = wcstok_s(last,L",; :",&context); while(next && rtnVal) { // make sure you validate the user input if(SUCCEEDED(StringCchLengthW(last,LOCALE_NAME_MAX_LENGTH,&strLen)) && DownlevelLocaleNameToLCID(next,0) != 0) { langMultiStrPtr[0] = L'\0'; rtnVal &= SUCCEEDED(StringCchCatW(langMultiStrPtr,(langMultiStrSize - (langMultiStrPtr - langMultiStr)),next)); langMultiStrPtr += strLen + 1; } next = wcstok_s(NULL,L",; :",&context); if(next) last = next; } if(rtnVal && (langMultiStrSize - (langMultiStrPtr - langMultiStr))) // make sure there is a double null term for the multi-string { langMultiStrPtr[0] = L'\0'; } else // fail and guard anyone whom might use the multi-string { langMultiStr[0] = L'\0'; langMultiStr[1] = L'\0'; } } return rtnVal; }Utwórz lub skopiuj langs.txt do odpowiedniego katalogu, zgodnie z opisem w Krok 4: Globalizacja "Hello MUI".
Skompiluj i uruchom projekt.
Notatka
Jeśli aplikacja powinna działać w wersjach systemu Windows wcześniejszych niż Windows Vista, zapoznaj się z dokumentami dołączonymi do pakietu microsoft NLS downlevel APIs, aby dowiedzieć się, jak ponownie dystrybuować Nlsdl.dll. (To nie jest już dostępne w Centrum pobierania Microsoft. Użyj interfejsów API globalizacji ICU w systemie Windows 10 maj 2019 Update i nowszych wersjach.)
Dodatkowe kwestie dotyczące interfejsu MUI
Obsługa aplikacji konsolowych
Techniki omówione w tym samouczku mogą być również używane w aplikacjach konsolowych. Jednak w przeciwieństwie do większości standardowych kontrolek graficznego interfejsu użytkownika okno poleceń systemu Windows nie może wyświetlać znaków dla wszystkich języków. Z tego powodu wielojęzyczne aplikacje konsolowe wymagają szczególnej uwagi.
Wywoływanie interfejsów API SetThreadUILanguage lub SetThreadPreferredUILanguages z określonymi flagami filtrowania powoduje, że funkcje ładowania zasobów usuwają sondy zasobów językowych dla określonych języków, które nie są zwykle wyświetlane w oknie polecenia. Po ustawieniu tych flag algorytmy ustawień języka zezwalają tylko na te języki, które będą prawidłowo wyświetlane w oknie poleceń, aby znajdować się na liście rezerwowej.
Aby uzyskać więcej informacji na temat tworzenia wielojęzycznej aplikacji konsolowej przy użyciu tych interfejsów API, zobacz sekcje SetThreadUILanguage i SetThreadPreferredUILanguages.
Określanie języków do obsługi w Run-Time
Możesz przyjąć jedną z następujących sugestii projektowych, aby określić języki, które aplikacja powinna obsługiwać w czasie wykonywania:
Podczas instalacji włącz użytkownikowi końcowemu wybór preferowanego języka z listy obsługiwanych języków
Odczytywanie listy języków z pliku konfiguracji
Niektóre projekty w tym samouczku zawierają funkcję służącą do analizowania pliku konfiguracji langs.txt zawierającego listę języków.
Ponieważ ta funkcja przyjmuje dane wejściowe zewnętrzne, zweryfikuj języki, które są dostarczane jako dane wejściowe. Aby uzyskać więcej informacji na temat przeprowadzania tej weryfikacji, zobacz funkcje IsValidLocaleName lub DownLevelLocaleNameToLCID.
Wykonaj zapytanie dotyczące systemu operacyjnego, aby określić, które języki są zainstalowane
Takie podejście pomaga aplikacji używać tego samego języka co system operacyjny. Mimo że nie wymaga to monitowania użytkownika, jeśli wybierzesz tę opcję, należy pamiętać, że języki systemu operacyjnego można dodawać lub usuwać w dowolnym momencie i mogą ulec zmianie po zainstalowaniu aplikacji przez użytkownika. Należy również pamiętać, że w niektórych przypadkach system operacyjny jest instalowany z ograniczoną obsługą języka, a aplikacja oferuje większą wartość, jeśli obsługuje języki, których system operacyjny nie obsługuje.
Aby uzyskać więcej informacji na temat określania aktualnie zainstalowanych języków w systemie operacyjnym, zobacz funkcję EnumUILanguages.
Obsługa złożonych skryptów w wersjach wcześniejszych niż Windows Vista
Jeśli aplikacja obsługując niektóre złożone skrypty działa w wersji systemu Windows wcześniejszej niż Windows Vista, tekst w tym skrypie może nie być poprawnie wyświetlany w składnikach graficznego interfejsu użytkownika. Na przykład w projekcie downlevel w tym samouczku hi-IN i skrypty ta-IN mogą nie być wyświetlane w polu komunikatu z powodu problemów z przetwarzaniem złożonych skryptów i brakiem powiązanych czcionek. Zwykle problemy z tym charakterem przedstawiają się jako pola kwadratowe w składniku graficznego interfejsu użytkownika.
Więcej informacji na temat włączania złożonego przetwarzania skryptów można znaleźć na stronie Obsługa skryptów i czcionek w systemie Windows.
Streszczenie
Ten poradnik zglobalizował aplikację jednojęzyczną i zademonstrował następujące najlepsze praktyki.
- Zaprojektuj aplikację, aby korzystać z modelu zasobów Win32.
- Zastosuj podział zasobów na pliki satelitarne (.mui).
- Upewnij się, że proces lokalizacji aktualizuje zasoby w plikach mui, aby były odpowiednie dla języka docelowego.
- Upewnij się, że pakowanie i wdrażanie aplikacji, skojarzonych plików mui i zawartości konfiguracji jest wykonywane poprawnie, aby umożliwić interfejsom API ładowania zasobów znajdowanie zlokalizowanej zawartości.
- Podaj użytkownikowi końcowemu mechanizm dostosowywania konfiguracji języka aplikacji.
- Dostosuj kod czasu wykonywania, aby korzystać z konfiguracji języka, aby aplikacja odpowiadała potrzebom użytkowników końcowych.