Aggiunta del supporto interfaccia utente multilingue a un'applicazione
Questa esercitazione illustra come usare un'applicazione monolinguale e renderla pronta al mondo. Questa applicazione è sotto forma di una soluzione completa creata in Microsoft Visual Studio.
- Sintesi
- Configurazione della soluzione Hello MUI
- Passaggio 0: Creazione di Hello MUI hardcoded
- Passaggio 1: Implementazione del modulo di risorse di base
- Passaggio 2: Compilazione del modulo di risorse di base
- Passaggio 3: Creazione di un'istanza di Resource-Savvy "Hello MUI"
- Passaggio 4: Globalizzazione di "Hello MUI"
- Passaggio 5: Personalizzazione di "Hello MUI"
- Considerazioni aggiuntive per MUI
- Riepilogo
Panoramica
A partire da Windows Vista, il sistema operativo Windows stesso è stato creato da zero per essere una piattaforma multilingue, con supporto aggiuntivo che consente di creare applicazioni multilingue che usano il modello di risorse MUI di Windows.
Questa esercitazione illustra questo nuovo supporto per le applicazioni multilingue illustrando gli aspetti seguenti:
- Usa il supporto migliorato della piattaforma MUI per abilitare facilmente le applicazioni multilingue.
- Estende le applicazioni multilingue da eseguire nelle versioni di Windows precedenti a Windows Vista.
- Vengono illustrate le considerazioni aggiuntive relative allo sviluppo di applicazioni multilingue specializzate, ad esempio applicazioni console.
Questi collegamenti consentono di fornire un aggiornamento rapido sui concetti relativi all'internazionalizzazione e all'muI:
- Panoramica rapida dell'internazionalizzazione.
- Concetti relativi a MUI.
- Uso di MUI per sviluppare applicazioni Win32.
L'idea dietro Hello MUI
Probabilmente si ha familiarità con l'applicazione Hello World classica, che illustra i concetti di programmazione di base. Questa esercitazione usa un approccio simile per illustrare come usare il modello di risorse MUI per aggiornare un'applicazione monolinguale per creare una versione multilingue denominata Hello MUI.
Nota
Le attività in questa esercitazione sono descritte in passaggi dettagliati a causa della precisione con cui queste attività devono essere eseguite e la necessità di spiegare i dettagli agli sviluppatori che hanno poca esperienza con queste attività.
Configurazione della soluzione Hello MUI
Questi passaggi illustrano come preparare la soluzione Hello MUI.
Requisiti della piattaforma
È necessario compilare gli esempi di codice in questa esercitazione usando Windows Software Development Kit (SDK) per Windows 7 e Visual Studio 2008. Windows 7 SDK verrà installato in Windows XP, Windows Vista e Windows 7 e la soluzione di esempio può essere basata su una di queste versioni del sistema operativo.
Tutti gli esempi di codice di questa esercitazione sono progettati per essere eseguiti in versioni x86 e x64 di Windows XP, Windows Vista e Windows 7. Le parti specifiche che non funzioneranno in Windows XP vengono evidenziate dove appropriato.
Prerequisiti
Installare Visual Studio 2008.
Per altre informazioni, vedere Visual Studio Developer Center.
Installare Windows SDK per Windows 7.
È possibile installarlo dalla pagina di Windows SDK del Centro per sviluppatori Windows. L'SDK include utilità per lo sviluppo di applicazioni per le versioni del sistema operativo a partire da Windows XP tramite le versioni più recenti.
Nota
Se non si installa il pacchetto nel percorso predefinito o se non si esegue l'installazione nell'unità di sistema, che in genere è l'unità C, prendere nota del percorso di installazione.
Configurare i parametri della riga di comando di Visual Studio.
- Aprire una finestra di comando di Visual Studio.
- Digitare il percorso del set.
- Verificare che la variabile path includa il percorso della cartella bin di Windows 7 SDK: ... Microsoft SDK\Windows\v7.0\bin
Installare il pacchetto aggiuntivo microsoft NLS per le API di livello inferiore.
Nota
Nel contesto di questa esercitazione, questo pacchetto è necessario solo se si personalizza l'applicazione per l'esecuzione nelle versioni di Windows precedenti a Windows Vista. Vedere Passaggio 5: Personalizzazione di Hello MUI.
Scaricare e installare il pacchetto, che non è più disponibile dall'Area download Microsoft. Usare le API di globalizzazione dell'ICU in Aggiornamento di Windows 10 (maggio 2019) e versioni successive.
Come per Windows SDK, se non si installa il pacchetto nel percorso predefinito o se non si esegue l'installazione nell'unità di sistema, che in genere è l'unità C, prendere nota del percorso di installazione.
Se la piattaforma di sviluppo è Windows XP o Windows Server 2003, verificare che Nlsdl.dll sia installato e registrato correttamente.
- Passare alla cartella "redist" nel percorso di installazione.
- Eseguire il file Nlsdl.*.exe ridistribuibile appropriato, ad esempio nlsdl.x86.exe. Questo passaggio installa e registra Nlsdl.dll.
Nota
Se si sviluppa un'applicazione che usa MUI e che deve essere eseguita nelle versioni di Windows precedenti a Windows Vista, Nlsdl.dll deve essere presente nella piattaforma Windows di destinazione. Nella maggior parte dei casi, ciò significa che l'applicazione deve portare e installare il programma di installazione Nlsdl ridistribuibile (e non solo copiare Nlsdl.dll stesso).
Passaggio 0: Creazione di Hello MUI hardcoded
Questa esercitazione inizia con la versione monolinguale dell'applicazione Hello MUI. L'applicazione presuppone l'uso del linguaggio di programmazione C++, delle stringhe di caratteri wide e della funzione MessageBoxW per l'output.
Iniziare creando l'applicazione GuiStep_0 iniziale, nonché la soluzione HelloMUI, che contiene tutte le applicazioni in questa esercitazione.
In Visual Studio 2008 creare un nuovo progetto. Usare le impostazioni e i valori seguenti:
- Tipo di progetto: in Visual C++ selezionare Win32 e quindi in Modelli installati in Visual Studio selezionare Progetto Win32.
- Nome: GuiStep_0.
- Percorso: ProjectRootDirectory (passaggi successivi fanno riferimento a questa directory).
- Nome soluzione: HelloMUI.
- Selezionare Crea directory per la soluzione.
- Nella Creazione guidata applicazione Win32 selezionare il tipo di applicazione predefinito: Applicazione Windows.
Configurare il modello di threading del progetto:
Nella Esplora soluzioni fare clic con il pulsante destro del mouse sul GuiStep_0 del progetto e quindi scegliere Proprietà.
Nella finestra di dialogo Pagine delle proprietà del progetto:
- Nell'elenco a discesa in alto a sinistra impostare Configurazione su Tutte le configurazioni.
- In Proprietà di configurazione espandere C/C++, selezionare Generazione codice e impostare Libreria di runtime: Debug multithread (/MTd).
Sostituire il contenuto di GuiStep_0.cpp con il codice seguente:
// 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; }
Compilare ed eseguire l'applicazione.
Il codice sorgente semplice riportato sopra rende semplice la scelta progettuale di hardcoded o incorporamento, tutto l'output che l'utente vedrà, in questo caso il testo "Hello MUI". Questa scelta limita l'utilità dell'applicazione per gli utenti che non riconoscono la parola inglese "Hello". Poiché MUI è un acronimo inglese basato sulla tecnologia, si presuppone che la stringa rimanga "MUI" per tutte le lingue.
Passaggio 1: Implementazione del modulo di risorse di base
Microsoft Win32 ha esposto a lungo la funzionalità per gli sviluppatori di applicazioni per separare i dati delle risorse dell'interfaccia utente dal codice sorgente dell'applicazione. Questa separazione è costituita dal modello di risorse Win32, in cui stringhe, bitmap, icone, messaggi e altri elementi normalmente visualizzati a un utente vengono inseriti in un pacchetto in una sezione distinta dell'eseguibile, separata dal codice eseguibile.
Per illustrare questa separazione tra codice eseguibile e creazione di pacchetti di dati delle risorse, in questo passaggio l'esercitazione inserisce la stringa "Hello" hardcoded precedentemente (la risorsa "en-US") nella sezione della risorsa di un modulo DLL nel progetto HelloModule_en_us.
Questa DLL Win32 può contenere anche funzionalità eseguibili del tipo di libreria (come qualsiasi altra DLL). Ma per concentrarsi sugli aspetti correlati alle risorse Win32, lasciare il codice DLL in fase di esecuzione stub in dllmain.cpp. Le sezioni successive di questa esercitazione usano i dati delle risorse HelloModule compilati qui e presentano anche il codice di runtime appropriato.
Per costruire un modulo di risorse Win32, iniziare creando una DLL con dllmain stub:
Aggiungere un nuovo progetto alla soluzione HelloMUI:
- Scegliere Aggiungi dal menu File e quindi Nuovo progetto.
- Tipo di progetto: Progetto Win32.
- Nome: HelloModule_en_us.
- Percorso: ProjectRootDirectory\HelloMUI.
- Nella Creazione guidata applicazione Win32 selezionare Tipo di applicazione: DLL.
Configurare il modello di threading di questo progetto:
Nella Esplora soluzioni fare clic con il pulsante destro del mouse sul HelloModule_en_us del progetto e scegliere Proprietà.
Nella finestra di dialogo Pagine delle proprietà del progetto:
- Nell'elenco a discesa in alto a sinistra impostare Configurazione su Tutte le configurazioni.
- In Proprietà di configurazione espandere C/C++, selezionare Generazione codice e impostare Libreria di runtime: Debug multithread (/MTd).
Esaminare dllmain.cpp. È possibile aggiungere le macro UNREFERENCED_PARAMETER al codice generato, come illustrato qui, per abilitare la compilazione al livello di avviso 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; }
Aggiungere un file di definizione della risorsa HelloModule.rc al progetto:
Nel HelloModule_en_us del progetto fare clic con il pulsante destro del mouse sulla cartella File di risorse e scegliere Aggiungi e quindi nuovo elemento.
Nella finestra di dialogo Aggiungi nuovo elemento scegliere quanto segue:
- Categorie: in Visual C++ selezionare Risorsa e quindi in "Modelli installati in Visual Studio" selezionare File di risorse (rc).
- Nome: HelloModule.
- Percorso: accettare l'impostazione predefinita.
- Scegliere Aggiungi.
Specificare che il nuovo file HelloModule.rc deve essere salvato come Unicode:
- Nel Esplora soluzioni fare clic con il pulsante destro del mouse su HelloModule.rc e scegliere Visualizza codice.
- Se viene visualizzato un messaggio che indica che il file è già aperto e chiede se si desidera chiuderlo, fare clic su Sì.
- Con il file visualizzato come testo, selezionare File di menu e quindi Opzioni di salvataggio avanzate.
- In Codifica specificare Unicode - Codepage 1200.
- Fare clic su OK.
- Salvare e chiudere HelloModule.rc.
Aggiungere una tabella di stringhe contenente la stringa "Hello":
Nella visualizzazione risorse fare clic con il pulsante destro del mouse su HelloModule.rc e scegliere Aggiungi risorsa.
Selezionare Tabella stringhe.
Fare clic su Nuovo.
Aggiungere una stringa alla tabella stringhe:
- ID: HELLO_MUI_STR_0.
- Valore: 0.
- Didascalia: Hello.
Se si visualizza HelloModule.rc come testo ora, verranno visualizzati vari pezzi di codice sorgente specifico della risorsa. Uno dei più interessanti è la sezione che descrive la stringa "Hello":
///////////////////////////////////////////////////////////////////////////// // // String Table // STRINGTABLE BEGIN HELLO_MUI_STR_0 "Hello" END
Questa stringa "Hello" è la risorsa che deve essere localizzata (ovvero tradotta) in ogni lingua che l'applicazione spera di supportare. Ad esempio, il progetto di HelloModule_ta_in (che verrà creato nel passaggio successivo) conterrà la propria versione localizzata di HelloModule.rc per "ta-IN":
///////////////////////////////////////////////////////////////////////////// // // String Table // STRINGTABLE BEGIN HELLO_MUI_STR_0 "வணக்கம்" END
Compilare il progetto HelloModule_en_us per assicurarsi che non siano presenti errori.
Creare sei altri progetti nella soluzione HelloMUI (o molti di essi desiderati) per creare altre sei DLL di risorse per altre lingue. Usare i valori in questa tabella:
Nome progetto Nome del file RC ID stringa Valore stringa String didascalia HelloModule_de_de HelloModule HELLO_MUI_STR_0 0 Hallo HelloModule_es_es HelloModule HELLO_MUI_STR_0 0 Hola HelloModule_fr_fr HelloModule HELLO_MUI_STR_0 0 Bonjour HelloModule_hi_in HelloModule HELLO_MUI_STR_0 0 नमस् HelloModule_ru_ru HelloModule HELLO_MUI_STR_0 0 Здравствуйте HelloModule_ta_in HelloModule HELLO_MUI_STR_0 0 வணக்கம்
Per altre informazioni sulla struttura e la sintassi dei file RC, vedere Informazioni sui file di risorse.
Passaggio 2: Compilazione del modulo di risorse di base
Usando i modelli di risorse precedenti, la compilazione di uno dei sette progetti HelloModule genera sette DLL separate. Ogni DLL conterrà una sezione della risorsa con una singola stringa localizzata nel linguaggio appropriato. Anche se appropriato per il modello di risorse Win32 storico, questa progettazione non sfrutta muI.
In Windows Vista SDK e versioni successive, MUI offre la possibilità di suddividere i file eseguibili in codice sorgente e moduli di contenuto localizzabili. Con la personalizzazione aggiuntiva illustrata più avanti nel passaggio 5, le applicazioni possono essere abilitate per il supporto multilingue da eseguire nelle versioni precedenti a Windows Vista.
I meccanismi principali disponibili per suddividere le risorse dal codice eseguibile, a partire da Windows Vista, sono:
- Uso di rc.exe (compilatore RC) con opzioni specifiche o
- Uso di uno strumento di divisione dello stile post-compilazione denominato muirct.exe.
Per altre informazioni, vedere Utilità risorse.
Per semplicità, questa esercitazione usa muirct.exe per suddividere l'eseguibile "Hello MUI".
Suddivisione dei vari moduli di risorse del linguaggio: panoramica
Un processo in più parti è coinvolto nella suddivisione delle DLL in un file eseguibile HelloModule.dll, oltre a HelloModule.dll.mui per ognuna delle sette lingue supportate all'interno di questa esercitazione. Questa panoramica descrive i passaggi necessari; La sezione successiva presenta un file di comando che esegue questi passaggi.
Prima di tutto, dividere il modulo HelloModule.dll solo inglese usando il comando :
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
La riga di comando precedente usa il file di configurazione DoReverseMuiLoc.rcconfig. Questo tipo di file di configurazione viene in genere usato da muirct.exe per suddividere le risorse tra la DLL (LN) indipendente dal linguaggio e i file mui dipendenti dalla lingua. In questo caso, il file xml DoReverseMuiLoc.rcconfig (elencato nella sezione successiva) indica molti tipi di risorse, ma tutti rientrano nella categoria di file "localizedResources" o mui e non sono presenti risorse nella categoria indipendente dal linguaggio. Per altre informazioni su come preparare un file di configurazione delle risorse, vedere Preparazione di un file di configurazione delle risorse.
Oltre a creare un file HelloModule.dll.mui che contiene la stringa inglese "Hello", muirct.exe incorpora anche una risorsa MUI nel modulo HelloModule.dll durante la suddivisione. Per caricare correttamente in fase di esecuzione le risorse appropriate dai moduli HelloModule.dll.mui specifici del linguaggio, ogni file mui deve avere i checksum fissi in modo che corrispondano ai checksum nel modulo LN indipendente dal linguaggio di base. Questa operazione viene eseguita da un comando, ad esempio:
muirct.exe -c HelloModule.dll -e en-US\HelloModule.dll.mui
Analogamente, muirct.exe viene richiamato per creare un file HelloModule.dll.mui per ognuna delle altre lingue. Tuttavia, in questi casi la DLL indipendente dal linguaggio viene rimossa, perché sarà necessario solo il primo creato. I comandi che elaborano le risorse spagnolo e francese hanno un aspetto simile al seguente:
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
Una delle cose più importanti da notare nelle righe di comando muirct.exe sopra è che il flag -x viene usato per specificare un ID lingua di destinazione. Il valore fornito a muirct.exe è diverso per ogni modulo HelloModule.dll specifico della lingua. Questo valore della lingua è centrale e viene usato da muirct.exe per contrassegnare in modo appropriato il file mui durante la suddivisione. Un valore errato genera errori di caricamento delle risorse per quel particolare file con estensione mui in fase di esecuzione. Per altre informazioni sul mapping del nome della lingua a LCID, vedere Costanti e stringhe dell'identificatore di lingua.
Ogni file con estensione mui diviso finisce per essere inserito in una directory corrispondente al nome della lingua, immediatamente sotto la directory in cui risiederà il file HelloModule.dll indipendente dalla lingua. Per altre informazioni sul posizionamento dei file mui, vedere Distribuzione di applicazioni.
Suddivisione dei vari moduli di risorse del linguaggio: creazione dei file
Per questa esercitazione si crea un file di comando contenente i comandi per suddividere le varie DLL e richiamarlo manualmente. Si noti che nel lavoro di sviluppo effettivo è possibile ridurre il potenziale di errori di compilazione includendo questi comandi come eventi di pre-compilazione o post-compilazione nella soluzione HelloMUI, ma che esula dall'ambito di questa esercitazione.
Creare i file per la compilazione di debug:
Creare un file di comando DoReverseMuiLoc.cmd contenente i comandi seguenti:
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 pause
Creare un file XML DoReverseMuiLoc.rcconfig contenente le righe seguenti:
<?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>
Copiare DoReverseMuiLoc.cmd e DoReverseMuiLoc.rcconfig in ProjectRootDirectory\HelloMUI\Debug.
Aprire il prompt dei comandi di Visual Studio 2008 e passare alla directory Debug.
Eseguire DoReverseMuiLoc.cmd.
Quando si crea una build di versione, copiare gli stessi file DoReverseMuiLoc.cmd e DoReverseMuiLoc.rcconfig nella directory Release ed eseguire il file di comando.
Passaggio 3: Creazione di un'istanza di Resource-Savvy "Hello MUI"
Basandosi sull'esempio iniziale hardcoded GuiStep_0.exe precedente, è possibile estendere la portata dell'applicazione a più utenti linguistici scegliendo di incorporare il modello di risorse Win32. Il nuovo codice di runtime presentato in questo passaggio include il caricamento del modulo (LoadLibraryEx) e la logica di recupero di stringhe (LoadString).
Aggiungere un nuovo progetto alla soluzione HelloMUI (usando la selezione di menu File, Aggiungi e Nuovo progetto) con le impostazioni e i valori seguenti:
- Tipo di progetto: Progetto Win32.
- Nome: GuiStep_1.
- Percorso: accettare l'impostazione predefinita.
- Nella Creazione guidata applicazione Win32 selezionare il tipo di applicazione predefinito: Applicazione Windows.
Impostare questo progetto da eseguire da Visual Studio e configurare il modello di threading:
Nella Esplora soluzioni fare clic con il pulsante destro del mouse sul GuiStep_1 del progetto e scegliere Imposta come progetto di avvio.
Fare di nuovo clic con il pulsante destro del mouse e selezionare Proprietà.
Nella finestra di dialogo Pagine delle proprietà del progetto:
- Nell'elenco a discesa in alto a sinistra impostare Configurazione su Tutte le configurazioni.
- In Proprietà di configurazione espandere C/C++, selezionare Generazione codice e impostare Libreria di runtime: Debug multithread (/MTd).
Sostituire il contenuto di GuiStep_1.cpp con il codice seguente:
// 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; }
Compilare ed eseguire l'applicazione. L'output verrà visualizzato nella lingua attualmente impostata come lingua di visualizzazione del computer (purché sia una delle sette lingue compilate).
Passaggio 4: Globalizzazione di "Hello MUI"
Anche se l'esempio precedente è in grado di visualizzare l'output in lingue diverse, non rientra in diverse aree. Forse il più evidente è che l'applicazione è disponibile solo in un piccolo subset di lingue rispetto al sistema operativo Windows stesso. Se, ad esempio, l'applicazione GuiStep_1 del passaggio precedente è stata installata in una build giapponese di Windows, è probabile che si verifichino errori di posizione delle risorse.
Per risolvere questa situazione, sono disponibili due opzioni principali:
- Assicurarsi che le risorse in un linguaggio di fallback finale predeterminato siano incluse.
- Fornire un modo per consentire all'utente finale di configurare le proprie preferenze di lingua tra il subset di lingue supportate in modo specifico dall'applicazione.
Di queste opzioni, quella che fornisce un linguaggio di fallback finale è altamente consigliabile ed è implementata in questa esercitazione passando il flag -g a muirct.exe, come illustrato in precedenza. Questo flag indica a muirct.exe di creare una lingua specifica (de-DE / 0x0407) il linguaggio di fallback finale associato al modulo dll indipendente dal linguaggio (HelloModule.dll). In fase di esecuzione questo parametro viene usato come ultima risorsa per generare testo da visualizzare all'utente. Nel caso in cui non venga trovato un linguaggio di fallback finale e che non sia disponibile alcuna risorsa appropriata nel file binario indipendente dalla lingua, il caricamento della risorsa ha esito negativo. È pertanto consigliabile determinare attentamente gli scenari che l'applicazione potrebbe incontrare e pianificare il linguaggio di fallback finale di conseguenza.
L'altra opzione di consentire le preferenze linguistiche configurabili e il caricamento delle risorse in base a questa gerarchia definita dall'utente può aumentare notevolmente la soddisfazione dei clienti. Sfortunatamente, complica anche le funzionalità necessarie all'interno dell'applicazione.
Questo passaggio dell'esercitazione usa un meccanismo di file di testo semplificato per abilitare la configurazione personalizzata della lingua utente. Il file di testo viene analizzato in fase di esecuzione dall'applicazione e l'elenco di lingue analizzate e convalidate viene usato per stabilire un elenco di fallback personalizzato. Dopo aver stabilito l'elenco di fallback personalizzato, le API di Windows caricheranno le risorse in base alla precedenza della lingua impostata in questo elenco. Il resto del codice è simile a quello trovato nel passaggio precedente.
Aggiungere un nuovo progetto alla soluzione HelloMUI (usando la selezione di menu File, Aggiungi e Nuovo progetto) con le impostazioni e i valori seguenti:
- Tipo di progetto: Progetto Win32.
- Nome: GuiStep_2.
- Percorso: accettare l'impostazione predefinita.
- Nella Creazione guidata applicazione Win32 selezionare il tipo di applicazione predefinito: Applicazione Windows.
Impostare questo progetto da eseguire da Visual Studio e configurare il modello di threading:
Nella Esplora soluzioni fare clic con il pulsante destro del mouse sul GuiStep_2 del progetto e scegliere Imposta come progetto di avvio.
Fare di nuovo clic con il pulsante destro del mouse e selezionare Proprietà.
Nella finestra di dialogo Pagine delle proprietà del progetto:
- Nell'elenco a discesa in alto a sinistra impostare Configurazione su Tutte le configurazioni.
- In Proprietà di configurazione espandere C/C++, selezionare Generazione codice e impostare Libreria di runtime: Debug multithread (/MTd).
Sostituire il contenuto di GuiStep_2.cpp con il codice seguente:
// 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; }
Creare un file di testo Unicode langs.txt contenente la riga seguente:
hi-IN ta-IN ru-RU fr-FR es-ES en-US
Nota
Assicurarsi di salvare il file come Unicode.
Copiare langs.txt nella directory da cui verrà eseguito il programma:
- Se in esecuzione da Visual Studio, copiarlo in ProjectRootDirectory\HelloMUI\GuiStep_2.
- Se viene eseguito da Esplora risorse, copiarlo nella stessa directory di GuiStep_2.exe.
Compilare ed eseguire il progetto. Provare a modificare langs.txt in modo che diverse lingue vengano visualizzate all'inizio dell'elenco.
Passaggio 5: Personalizzazione di "Hello MUI"
Alcune delle funzionalità di runtime indicate finora in questa esercitazione sono disponibili solo in Windows Vista e versioni successive. Potresti voler riutilizzare lo sforzo investito nella localizzazione e nella suddivisione delle risorse facendo in modo che l'applicazione funzioni sulle versioni del sistema operativo Windows di livello inferiore, ad esempio Windows XP. Questo processo comporta la regolazione dell'esempio precedente in due aree chiave:
Le funzioni di caricamento delle risorse pre-Windows Vista(ad esempio LoadString, LoadIcon, LoadBitmap, FormatMessage e altri) non sono in grado di essere consapevoli. Le applicazioni fornite con risorse suddivise (file LN e mui) devono caricare i moduli di risorse usando una di queste due funzioni:
- Se l'applicazione deve essere eseguita solo in Windows Vista e versioni successive, deve caricare moduli di risorse con LoadLibraryEx.
- Se l'applicazione deve essere eseguita nelle versioni precedenti a Windows Vista, oltre a Windows Vista o versioni successive, deve usare LoadMUILibrary, che è una funzione di livello inferiore specifica fornita in Windows 7 SDK.
Il supporto dell'ordine di fallback del linguaggio e della gestione del linguaggio offerto nelle versioni precedenti a Windows Vista del sistema operativo Windows differisce in modo significativo rispetto a quello di Windows Vista e versioni successive. Per questo motivo, le applicazioni che consentono il fallback del linguaggio configurato dall'utente devono modificare le procedure di gestione del linguaggio:
- Se l'applicazione deve essere eseguita solo in Windows Vista e versioni successive, l'impostazione dell'elenco di lingue tramite SetThreadPreferredUILanguages è sufficiente.
- Se l'applicazione deve essere eseguita in tutte le versioni di Windows, è necessario costruire il codice che verrà eseguito su piattaforme di livello inferiore per scorrere l'elenco di lingue configurato dall'utente e eseguire il probe per il modulo di risorsa desiderato. Questa operazione può essere visualizzata nelle sezioni 1c e 2 del codice fornito più avanti in questo passaggio.
Creare un progetto in grado di usare i moduli di risorse localizzati in qualsiasi versione di Windows:
Aggiungere un nuovo progetto alla soluzione HelloMUI (usando la selezione di menu File, Aggiungi e Nuovo progetto) con le impostazioni e i valori seguenti:
- Tipo di progetto: Progetto Win32.
- Nome: GuiStep_3.
- Percorso: accettare l'impostazione predefinita.
- Nella Creazione guidata applicazione Win32 selezionare il tipo di applicazione predefinito: Applicazione Windows.
Impostare questo progetto da eseguire da Visual Studio e configurare il modello di threading. Configurarlo anche per aggiungere le intestazioni e le librerie necessarie.
Nota
I percorsi usati in questa esercitazione presuppongono che il pacchetto delle API di livello inferiore di Windows 7 e NLS di Microsoft siano state installate nelle directory predefinite. In caso contrario, modificare i percorsi in modo appropriato.
Nella Esplora soluzioni fare clic con il pulsante destro del mouse sul GuiStep_3 del progetto e scegliere Imposta come progetto di avvio.
Fare di nuovo clic con il pulsante destro del mouse e selezionare Proprietà.
Nella finestra di dialogo Pagine delle proprietà del progetto:
Nell'elenco a discesa in alto a sinistra impostare Configurazione su Tutte le configurazioni.
In Proprietà di configurazione espandere C/C++, selezionare Generazione codice e impostare Libreria di runtime: Debug multithread (/MTd).
Selezionare Generale e aggiungere a Altre directory di inclusione:
- "C:\Microsoft NLS Downlevel APIs\Include".
Selezionare Lingua e impostare Considera wchar_t come tipo predefinito: No (/Zc:wchar_t-).
Selezionare Avanzate e impostare Convenzione di chiamata: _stdcall (/Gz).
In Proprietà di configurazione espandere Linker selezionare Input e aggiungere a Dipendenze aggiuntive:
- "C:\Programmi\Microsoft SDKs\Windows\v7.0\Lib\MUILoad.lib".
- "C:\Microsoft NLS Downlevel APIs\Lib\x86\Nlsdl.lib".
Sostituire il contenuto di GuiStep_3.cpp con il codice seguente:
// 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; }
Creare o copiare langs.txt nella directory appropriata, come descritto in precedenza nel passaggio 4: Globalizzazione di "Hello MUI".
Compilare ed eseguire il progetto.
Nota
Se l'applicazione deve essere eseguita nelle versioni di Windows precedenti a Windows Vista, assicurarsi di leggere i documenti disponibili con il pacchetto delle API di livello inferiore Microsoft NLS su come ridistribuire Nlsdl.dll. Non è più disponibile nell'Area download Microsoft. Usare le API di globalizzazione dell'ICU in Aggiornamento di Windows 10 (maggio 2019) e versioni successive.
Considerazioni aggiuntive per MUI
Supporto per le applicazioni console
Le tecniche descritte in questa esercitazione possono essere usate anche nelle applicazioni console. Tuttavia, a differenza della maggior parte dei controlli GUI standard, la finestra di comando di Windows non può visualizzare caratteri per tutte le lingue. Per questo motivo, le applicazioni console multilingue richiedono particolare attenzione.
Se si chiamano le API SetThreadUILanguage o SetThreadPreferredUILanguages con flag di filtro specifici, le funzioni di caricamento delle risorse vengono rimosse per lingue specifiche che normalmente non vengono visualizzate all'interno di una finestra di comando. Quando questi flag sono impostati, gli algoritmi di impostazione della lingua consentono solo alle lingue che verranno visualizzate correttamente nella finestra di comando di trovarsi nell'elenco di fallback.
Per altre informazioni sull'uso di queste API per creare un'applicazione console multilingue, vedere le sezioni osservazioni di SetThreadUILanguage e SetThreadPreferredUILanguages.
Determinazione dei linguaggi da supportare in fase di esecuzione
È possibile adottare uno dei suggerimenti di progettazione seguenti per determinare quali lingue l'applicazione deve supportare in fase di esecuzione:
Durante l'installazione, consentire all'utente finale di selezionare la lingua preferita da un elenco di lingue supportate
Leggere un elenco di lingue da un file di configurazione
Alcuni dei progetti di questa esercitazione contengono una funzione usata per analizzare un file di configurazione langs.txt che contiene un elenco di lingue.
Poiché questa funzione accetta input esterno, convalidare le lingue fornite come input. Per altri dettagli sull'esecuzione della convalida, vedere le funzioni IsValidLocaleName o DownLevelLocaleNameToLCID.
Eseguire una query sul sistema operativo per determinare quali lingue sono installate
Questo approccio consente all'applicazione di usare la stessa lingua del sistema operativo. Anche se ciò non richiede la richiesta dell'utente, se si sceglie questa opzione, tenere presente che le lingue del sistema operativo possono essere aggiunte o rimosse in qualsiasi momento e possono cambiare dopo l'installazione dell'applicazione. Tenere inoltre presente che in alcuni casi, il sistema operativo viene installato con supporto linguistico limitato e l'applicazione offre più valore se supporta le lingue non supportate dal sistema operativo.
Per altre informazioni su come determinare le lingue attualmente installate nel sistema operativo, vedere la funzione EnumUILanguages.
Supporto di script complessi nelle versioni precedenti a Windows Vista
Quando un'applicazione che supporta determinati script complessi viene eseguita in una versione di Windows precedente a Windows Vista, il testo in tale script potrebbe non essere visualizzato correttamente nei componenti GUI. Ad esempio, nel progetto di livello inferiore all'interno di questa esercitazione, gli script hi-IN e ta-IN potrebbero non essere visualizzati nella finestra di messaggio a causa di problemi con l'elaborazione di script complessi e la mancanza di tipi di carattere correlati. Normalmente, i problemi di questa natura si presentano come caselle quadrate nel componente GUI.
Altre informazioni su come abilitare l'elaborazione di script complessi sono disponibili in Supporto di script e tipi di carattere in Windows.
Riepilogo
Questa esercitazione ha globalizzato un'applicazione monolinguale e ha illustrato le procedure consigliate seguenti.
- Progettare l'applicazione per sfruttare il modello di risorse Win32.
- Usare la suddivisione MUI delle risorse in file binari satellite (file con estensione mui).
- Assicurarsi che il processo di localizzazione aggiorni le risorse nei file mui in modo che siano appropriate per la lingua di destinazione.
- Assicurarsi che la creazione di pacchetti e la distribuzione dell'applicazione, i file mui associati e il contenuto di configurazione vengano eseguiti correttamente per consentire alle API di caricamento delle risorse di trovare il contenuto localizzato.
- Fornire all'utente finale un meccanismo per modificare la configurazione della lingua dell'applicazione.
- Modificare il codice di runtime per sfruttare la configurazione del linguaggio per rendere l'applicazione più reattiva alle esigenze dell'utente finale.