Hinzufügen von Unterstützung für mehrsprachige Benutzeroberflächen zu einer Anwendung

In diesem Lernprogramm wird gezeigt, wie Sie eine monolinguale Anwendung verwenden und sie bereit für die Welt machen. Diese Anwendung hat die Form einer Komplettlösung, die in Microsoft Visual Studio erstellt wird.

Überblick

Ab Windows Vista wurde das Windows-Betriebssystem selbst als mehrsprachige Plattform entwickelt, mit zusätzlicher Unterstützung, mit der Sie mehrsprachige Anwendungen erstellen können, die das Windows MUI-Ressourcenmodell verwenden.

In diesem Lernprogramm wird diese neue Unterstützung für mehrsprachige Anwendungen veranschaulicht, indem die folgenden Aspekte behandelt werden:

  • Verwendet die verbesserte MUI-Plattformunterstützung, um auf einfache Weise mehrsprachige Anwendungen zu ermöglichen.
  • Erweitert mehrsprachige Anwendungen für die Ausführung auf Versionen von Windows vor Windows Vista.
  • Behandelt die zusätzlichen Überlegungen, die bei der Entwicklung spezieller mehrsprachiger Anwendungen wie Konsolenanwendungen angestellt werden.

Diese Links helfen ihnen, die Konzepte zur Internationalisierung und MUI schnell zu aktualisieren:

Die Idee hinter Hello MUI

Wahrscheinlich sind Sie mit der klassischen Hello World- Anwendung vertraut, die grundlegende Programmierkonzepte veranschaulicht. In diesem Lernprogramm wird ein ähnlicher Ansatz verwendet, um zu veranschaulichen, wie Sie mithilfe des MUI-Ressourcenmodells eine monolinguale Anwendung aktualisieren, um eine mehrsprachige Version namens Hello MUI zu erstellen.

Hinweis

Die Aufgaben in diesem Tutorial werden in detaillierten Schritten beschrieben, da diese Aktivitäten präzise ausgeführt werden müssen und die Details auch Entwicklern erklärt werden müssen, die wenig Erfahrung mit diesen Aufgaben haben.

 

Einrichten der Hello MUI-Lösung

In diesen Schritten wird beschrieben, wie Sie die Hello MUI-Lösung erstellen.

Plattformanforderungen

Sie müssen die Codebeispiele in diesem Lernprogramm mit dem Windows Software Development Kit (SDK) für Windows 7 und Visual Studio 2008 kompilieren. Das Windows 7 SDK wird unter Windows XP, Windows Vista und Windows 7 installiert, und die Beispiellösung kann auf einer dieser Betriebssystemversionen erstellt werden.

Alle Codebeispiele in diesem Lernprogramm sind für die Ausführung auf x86- und x64-Versionen von Windows XP, Windows Vista und Windows 7 konzipiert. Bestimmte Teile, die unter Windows XP nicht funktionieren, werden gegebenenfalls aufgerufen.

Voraussetzungen

  1. Installieren Sie Visual Studio 2008.

    Weitere Informationen finden Sie im Visual Studio Developer Center.

  2. Installieren Sie das Windows SDK für Windows 7.

    Sie können es über die Windows SDK-Seite des Windows Developer Center installieren. Das SDK enthält Dienstprogramme zum Entwickeln von Anwendungen für Betriebssystemversionen ab Windows XP bis zur neuesten Version.

    Hinweis

    Wenn Sie das Paket nicht am Standardspeicherort installieren oder nicht auf dem Systemlaufwerk, das normalerweise das C-Laufwerk ist, installieren, notieren Sie sich den Installationspfad.

     

  3. Konfigurieren Sie die Befehlszeilenparameter von Visual Studio.

    1. Öffnen Sie das Eingabefenster von Visual Studio.
    2. Geben Sie Pfad festlegen ein.
    3. Vergewissern Sie sich, dass die Pfadvariable den Pfad des Bin-Ordners des Windows 7 SDK enthält: ... Microsoft SDKs\Windows\v7.0\bin
  4. Installieren Sie das Add-On-Paket der Microsoft NLS-Downlevel-APIs.

    Hinweis

    Im Kontext dieses Lernprogramms ist dieses Paket nur erforderlich, wenn Sie die Anwendung so anpassen, dass sie unter Windows-Versionen vor Windows Vista ausgeführt wird. Siehe Schritt 5: Anpassen von Hello MUI.

    1. Laden Sie das Paket herunter und installieren Sie es. Es ist nicht mehr im Microsoft Download Centerverfügbar. Verwenden Sie ICU-Globalisierungs-APIs unter Update Mai 2019 von Windows 10 und höheren Versionen.

    2. Wie bei Windows SDK, wenn Sie das Paket nicht am Standardspeicherort installieren oder nicht auf dem Systemlaufwerk, das normalerweise das C-Laufwerk ist, installieren, notieren Sie sich den Installationspfad.

    3. Wenn Ihre Entwicklungsplattform Windows XP oder Windows Server 2003 ist, vergewissern Sie sich, dass Nlsdl.dll installiert und ordnungsgemäß registriert ist.

      1. Navigieren Sie unter dem Installationspfad zum Ordner „redist“.
      2. Führen Sie die entsprechende redistributable Nlsdl.*.exe aus, z. B. nlsdl.x86.exe. In diesem Schritt werden Nlsdl.dll installiert und registriert.

    Hinweis

    Wenn Sie eine Anwendung entwickeln, die MUI verwendet und auf Windows-Versionen vor Windows Vista ausgeführt werden muss, muss Nlsdl.dll auf der Ziel-Windows-Plattform vorhanden sein. In den meisten Fällen bedeutet dies, dass die Anwendung das weiterverteilbare Nlsdl-Installationsprogramm tragen und installieren muss (und nicht nur Nlsdl.dll selbst kopieren).

     

Schritt 0: Erstellen der fest codierten Hello MUI

Dieses Tutorial beginnt mit der monolingualen Version der Hello MUI-Anwendung. Die Anwendung setzt voraus, dass die C++-Programmiersprache, breite Zeichenfolgen und die MessageBoxW-Funktion für die Ausgabe verwendet werden.

Erstellen Sie zunächst die ursprüngliche GuiStep_0-Anwendung sowie die HelloMUI-Lösung, die alle Anwendungen in diesem Tutorial enthält.

  1. Erstellen Sie in Visual Studio 2008 ein neues Projekt. Verwenden Sie die folgenden Einstellungen und Werte:

    1. Projekttyp: Wählen Sie unter Visual C++ Win32 und dann unter Visual Studio installierte Vorlagen Win32 Project aus.
    2. Name: GuiStep_0.
    3. Speicherort: ProjectRootDirectory (Spätere Schritte verweisen auf dieses Verzeichnis).
    4. Lösungsname: HelloMUI.
    5. Aktivieren Sie Projektmappenverzeichnis erstellen.
    6. Wählen Sie im Win32-Anwendungs-Assistenten den Standardanwendungstyp aus: Windows-Anwendung.
  2. Konfigurieren des Projektthreadingmodells:

    1. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das Projekt GuiStep_0, und wählen Sie dann Eigenschaften aus.

    2. Im Dialogfeld Eigenschaftenseiten des Projekts:

      1. Legen Sie in der Dropdownliste oben links Konfiguration auf Alle Konfigurationen fest.
      2. Erweitern Sie unter Konfigurationseigenschaften C/C++, wählen Sie Codegenerierung aus, und legen Sie die Laufzeitbibliothek fest: Multithread-Debug (/MTd).
  3. Ersetzen Sie den Inhalt von GuiStep_0.cpp durch den folgenden Code:

    // 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;
    }
    
  4. Erstellen Sie die Anwendung und führen Sie sie aus.

Der obige unkomplizierte Quellcode trifft die einfache Designentscheidung, alle Ausgaben, die der Benutzer sehen wird - in diesem Fall den Text „Hello MUI“ - fest zu kodieren bzw. einzubetten. Diese Auswahl beschränkt das Dienstprogramm der Anwendung für Benutzer, die das englische Wort „Hello“ nicht erkennen. Da MUI ein technologiebasiertes englisches Akronym ist, wird für dieses Lernprogramm davon ausgegangen, dass die Zeichenfolge für alle Sprachen „MUI“ bleibt.

Schritt 1: Implementieren des Basisressourcenmoduls

Microsoft Win32 bietet Anwendungsentwicklern seit langem die Möglichkeit, ihre UI-Ressourcendaten vom Quellcode der Anwendung zu trennen. Diese Trennung erfolgt in Form des Win32-Ressourcenmodells, in dem Zeichenfolgen, Bitmaps, Symbole, Nachrichten und andere Elemente, die normalerweise für einen Benutzer angezeigt werden, in einen separaten Abschnitt der ausführbaren Datei gepackt werden, getrennt von ausführbarem Code.

Zur Veranschaulichung dieser Trennung zwischen ausführbarem Code und Ressourcendatenpaketierung platziert das Lernprogramm die zuvor fest codierte „Hello“-Zeichenfolge (die Ressource „en-US“) in den Ressourcenabschnitt eines DLL-Moduls im HelloModule_en_us Projekt.

Diese Win32-DLL könnte auch ausführbare Funktionen vom Bibliothekstyp enthalten (wie jede andere DLL möglicherweise). Um sich jedoch auf die mit den Win32-Ressourcen verbundenen Aspekte zu konzentrieren, lassen wir den Laufzeit-DLL-Code in dllmain.cpp weg. In den nachfolgenden Abschnitten dieses Tutorials werden die hier erstellten HelloModule-Ressourcendaten verwendet und außerdem entsprechender Laufzeitcode dargestellt.

Um ein Win32-Ressourcenmodul zu erstellen, beginnen Sie mit der Erstellung einer DLL mit einer ausgelagerten dllmain:

  1. Fügen Sie der HelloMUI-Lösung ein neues Projekt hinzu:

    1. Wählen Sie im Menü Datei die Option Hinzufügen und dann Neues Projekt aus.
    2. Projekttyp: Win32-Projekt.
    3. Name: HelloModule_en_us.
    4. Speicherort: ProjectRootDirectory\HelloMUI.
    5. Wählen Sie im Win32-Anwendungs-Assistenten Anwendungstyp: DLL aus.
  2. Konfigurieren Sie das Threadingmodell dieses Projekts:

    1. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das Projekt HelloModule_en_us, und wählen Sie Eigenschaften aus.

    2. Im Dialogfeld Eigenschaftenseiten des Projekts:

      1. Legen Sie in der Dropdownliste oben links Konfiguration auf Alle Konfigurationen fest.
      2. Erweitern Sie unter Konfigurationseigenschaften C/C++, wählen Sie Codegenerierung aus, und legen Sie die Laufzeitbibliothek fest: Multithread-Debug (/MTd).
  3. Überprüfen Sie dllmain.cpp. (Möglicherweise möchten Sie die UNREFERENCED_PARAMETER-Makros dem generierten Code hinzufügen, wie wir es hier haben, damit sie auf Warnungsebene 4 kompiliert werden kann.)

    // 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;
    }
    
  4. Fügen Sie dem Projekt eine Ressourcendefinitionsdatei HelloModule.rc hinzu:

    1. Klicken Sie unter dem Projekt HelloModule_en_us mit der rechten Maustaste auf den Ordner Ressourcendateien, und wählen Sie Hinzufügen aus, und wählen Sie dann Neues Element aus.

    2. Wählen Sie im Dialogfeld Neues Element hinzufügen die folgenden Schritte aus:

      1. Kategorien: Wählen Sie unter Visual C++ Ressource aus, und wählen Sie dann unter „Installierte Visual Studio-Vorlagen“ die Option Ressourcendatei (RC) aus.
      2. Name: HelloModule.
      3. Speicherort: Übernehmen Sie die Standardeinstellung.
      4. Klicken Sie auf Hinzufügen.
    3. Geben Sie an, dass die neue Datei HelloModule.rc als Unicode gespeichert werden soll:

      1. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf HelloModule.rc, und wählen Sie Code anzeigen aus.
      2. Wenn eine Meldung angezeigt wird, die Ihnen sagt, dass die Datei bereits geöffnet ist, und Sie fragt, ob Sie sie schließen möchten, klicken Sie auf Ja.
      3. Wenn die Datei als Text angezeigt wird, treffen Sie die Menüauswahl Datei und dann Erweiterte Speicheroptionen.
      4. Geben Sie unter Codierung Unicode - Codepage 1200 an.
      5. Klicken Sie auf OK.
      6. Speichern und schließen Sie HelloModule.rc.
    4. Fügen Sie eine Zeichenfolgentabelle hinzu, die die Zeichenfolge „Hello“ enthält:

      1. Klicken Sie in der Ressourcenansicht mit der rechten Maustaste auf HelloModule.rc, und wählen Sie Ressource hinzufügen aus.

      2. Wählen Sie Zeichenfolgentabelle aus.

      3. Klicken Sie auf Neu.

      4. Fügen Sie der Zeichenfolgentabelle eine Zeichenfolge hinzu:

        1. ID: HELLO_MUI_STR_0.
        2. Wert: 0.
        3. Beschriftung: Hello.

      Wenn Sie HelloModule.rc jetzt als Text anzeigen, werden verschiedene Teile des ressourcenspezifischen Quellcodes angezeigt. Am interessantesten ist der Abschnitt, der die Zeichenfolge „Hello“ beschreibt:

      /////////////////////////////////////////////////////////////////////////////
      //
      // String Table
      //
      
      STRINGTABLE 
      BEGIN
          HELLO_MUI_STR_0         "Hello"
      END
      

      Diese „Hello“-Zeichenfolge ist die Ressource, die lokalisiert werden muss (d. h. übersetzt) in jede Sprache, die von der Anwendung unterstützt werden soll. Beispielsweise enthält das HelloModule_ta_in-Projekt (das Sie im nächsten Schritt erstellen) eine eigene lokalisierte Version von HelloModule.rc für „ta-IN“:

      /////////////////////////////////////////////////////////////////////////////
      //
      // String Table
      //
      
      STRINGTABLE 
      BEGIN
          HELLO_MUI_STR_0         "வணக்கம்"
      END
      
    5. Erstellen Sie das HelloModule_en_us Projekt, um sicherzustellen, dass keine Fehler vorhanden sind.

  5. Erstellen Sie sechs weitere Projekte in der HelloMUI-Lösung (oder so viele wie gewünscht), um sechs weitere Ressourcen-DLLs für zusätzliche Sprachen zu erstellen. Verwenden Sie die Werte in dieser Tabelle:

    Projektname Name der .rc-Datei Zeichenfolgen-ID Zeichenfolgenwert Zeichenfolgenbeschriftung
    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 வணக்கம்

     

Weitere Informationen zur RC-Dateistruktur und -Syntax finden Sie unter Informationen zu Ressourcendateien.

Schritt 2: Erstellen des Basisressourcenmoduls

Bei Verwendung früherer Ressourcenmodelle würde das Erstellen eines der sieben HelloModule-Projekte zu sieben separaten DLLs führen. Jede DLL würde einen Ressourcenabschnitt mit einer einzelnen Zeichenfolge enthalten, die in die entsprechende Sprache lokalisiert ist. Obwohl es für das historische Win32-Ressourcenmodell geeignet ist, nutzt dieses Design nicht die Vorteile von MUI.

Im Windows Vista SDK und höher bietet MUI die Möglichkeit, ausführbare Dateien in Quellcode und lokalisierbare Inhaltsmodule aufzuteilen. Mit den zusätzlichen Anpassungen, die später in Schritt 5 behandelt werden, können Anwendungen für die mehrsprachige Unterstützung aktiviert werden, um auf Versionen vor Windows Vista zu laufen.

Die primären Mechanismen, die zum Aufteilen von Ressourcen aus ausführbarem Code verfügbar sind, beginnend mit Windows Vista, sind:

  • Verwendung von rc.exe (rc Compiler) mit bestimmten Schaltern oder
  • Verwendung eines Post-Build-Splitting-Tools namens muirct.exe.

Weitere Informationen finden Sie unter Ressourcendienstprogramme.

Aus Gründen der Einfachheit verwendet dieses Tutorial muirct.exe, um die ausführbare Datei „Hello MUI“ aufzuteilen.

Aufteilen der verschiedenen Sprachressourcenmodule: Eine Übersicht

Ein mehrteiliger Prozess ist daran beteiligt, die DLLs in eine ausführbare HelloModule.dll aufzuteilen, sowie eine HelloModule.dll.mui für jede der sieben unterstützten Sprachen in diesem Tutorial. In dieser Übersicht werden die erforderlichen Schritte beschrieben; der nächste Abschnitt enthält eine Befehlsdatei, die diese Schritte ausführt.

Teilen Sie zunächst das englischsprachige HelloModule.dll-Modul mithilfe des Befehls auf:

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

In der obigen Befehlszeile wird die Konfigurationsdatei DoReverseMuiLoc.rcconfig verwendet. Diese Art von Konfigurationsdatei wird in der Regel von muirct.exe verwendet, um Ressourcen zwischen der sprachneutralen DLL (LN) und den sprachabhängigen .mui-Dateien aufzuteilen. In diesem Fall gibt die XML-Datei DoReverseMuiLoc.rcconfig (im nächsten Abschnitt aufgeführt) viele Ressourcentypen an, aber alle fallen in die Dateikategorie „localizedResources“ oder .mui-Datei, und es gibt keine Ressourcen in der sprachneutralen Kategorie. Weitere Informationen zum Vorbereiten einer Ressourcenkonfigurationsdatei finden Sie unter Vorbereiten einer Ressourcenkonfigurationsdatei.

Neben der Erstellung einer HelloModule.dll.mui-Datei, die die englische Zeichenfolge „Hello“ enthält, bettet muirct.exe auch eine MUI-Ressource während der Aufteilung in das HelloModule.dll-Modul ein. Damit die entsprechenden Ressourcen zur Laufzeit ordnungsgemäß aus den sprachspezifischen HelloModule.dll.mui-Modulen geladen werden können, müssen für jede .mui-Datei die Prüfsummen festgelegt sein, damit sie den Prüfsummen im LN-Modul der Basissprache entsprechen. Dies erfolgt durch einen Befehl, z. B.:

muirct.exe -c HelloModule.dll -e en-US\HelloModule.dll.mui

Ebenso wird muirct.exe aufgerufen, um eine HelloModule.dll.mui-Datei für jede der anderen Sprachen zu erstellen. In diesen Fällen wird jedoch die sprachneutrale DLL verworfen, da nur die erste erstellte DLL benötigt wird. Die Befehle, die die spanischen und französischen Ressourcen verarbeiten, sehen wie folgt aus:

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

Eines der wichtigsten Dinge, die in den obigen Befehlszeilen von muirct.exe zu beachten sind, ist, dass das Flag -x zur Angabe einer Zielsprachen-ID verwendet wird. Der für muirct.exe bereitgestellte Wert unterscheidet sich für jedes sprachspezifische HelloModule.dll-Modul. Dieser Sprachwert ist zentral und wird von muirct.exe verwendet, um die .mui-Datei während der Trennung entsprechend zu markieren. Ein falscher Wert erzeugt Fehler beim Laden von Ressourcen für diese bestimmte .mui-Datei zur Laufzeit. Weitere Informationen zum Zuordnen des Sprachnamens zu LCID finden Sie unter Sprachbezeichnerkonstanten und Zeichenfolgen.

Jede aufgeteilte .mui-Datei wird in einem Verzeichnis abgelegt, das dem Namen der Sprache entspricht, und zwar direkt unter dem Verzeichnis, in dem sich die sprachneutrale HelloModule.dll befinden wird. Weitere Informationen zur .mui-Dateiplatzierung finden Sie unter Anwendungsbereitstellung.

Aufteilen der verschiedenen Sprachressourcenmodule: Erstellen der Dateien

In diesem Tutorial erstellen Sie eine Befehlsdatei, die die Befehle enthält, um die verschiedenen DLLs aufzuteilen, und Sie rufen sie manuell auf. Beachten Sie, dass Sie bei der eigentlichen Entwicklungsarbeit das Potenzial für Build-Fehler verringern könnten, indem Sie diese Befehle als Pre-Build- oder Post-Build-Ereignisse in die HelloMUI-Lösung einbinden, aber das würde den Rahmen dieses Tutorials sprengen.

Erstellen Sie die Dateien für den Debugbuild:

  1. Erstellen Sie eine Befehlsdatei DoReverseMuiLoc.cmd, die die folgenden Befehle enthält:

    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
    
  2. Erstellen Sie eine XML-Datei DoReverseMuiLoc.rcconfig, die die folgenden Zeilen enthält:

    <?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>
    
  3. Kopieren Sie DoReverseMuiLoc.cmd und DoReverseMuiLoc.rcconfig in ProjectRootDirectory\HelloMUI\Debug.

  4. Öffnen Sie die Visual Studio 2008-Eingabeaufforderung, und navigieren Sie zum Debugverzeichnis.

  5. Führen Sie DoReverseMuiLoc.cmd aus.

Wenn Sie einen Releasebuild erstellen, kopieren Sie dieselben DoReverseMuiLoc.cmd- und DoReverseMuiLoc.rcconfig-Dateien in das Releaseverzeichnis, und führen Sie dort die Befehlsdatei aus.

Schritt 3: Erstellen einer Ressource-Savvy „Hello MUI“

Aufbauend auf dem ursprünglichen, fest kodierten GuiStep_0.exe-Beispiel von oben könnten Sie die Reichweite der Anwendung auf mehrsprachige Benutzer erweitern, indem Sie das Win32-Ressourcenmodell einbeziehen. Der neue Laufzeitcode, der in diesem Schritt vorgestellt wird, umfasst die Logik zum Laden von Modulen ( LoadLibraryEx) und zum Abrufen von Strings ( LoadString).

  1. Fügen Sie der HelloMUI-Projektmappe (unter Verwendung der Menüauswahl Datei, Hinzufügen und Neues Projekt) ein neues Projekt mit den folgenden Einstellungen und Werten hinzu:

    1. Projekttyp: Win32-Projekt.
    2. Name: GuiStep_1.
    3. Speicherort: Übernehmen Sie die Standardeinstellung.
    4. Wählen Sie im Win32-Anwendungs-Assistenten den Standardanwendungstyp aus: Windows-Anwendung.
  2. Legen Sie dieses Projekt so fest, dass es in Visual Studio ausgeführt wird, und konfigurieren Sie sein Threadingmodell:

    1. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das Projekt GuiStep_1, und wählen Sie Als Startprojekt festlegen aus.

    2. Klicken Sie erneut mit der rechten Maustaste, und wählen Sie Eigenschaften aus.

    3. Im Dialogfeld Eigenschaftenseiten des Projekts:

      1. Legen Sie in der Dropdownliste oben links Konfiguration auf Alle Konfigurationen fest.
      2. Erweitern Sie unter Konfigurationseigenschaften C/C++, wählen Sie Codegenerierung aus, und legen Sie die Laufzeitbibliothek fest: Multithread-Debug (/MTd).
  3. Ersetzen Sie den Inhalt von GuiStep_1.cpp durch den folgenden Code:

    // 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;
    }
    
  4. Erstellen Sie die Anwendung und führen Sie sie aus. Die Ausgabe wird in der aktuell festgelegten Sprache als Anzeigesprache des Computers angezeigt (vorausgesetzt, sie ist eine der sieben Sprachen, die wir erstellt haben).

Schritt 4: Globalisieren von „Hello MUI“

Obwohl das vorherige Beispiel in der Lage ist, seine Ausgabe in verschiedenen Sprachen anzuzeigen, fällt es in einer Reihe von Bereichen kurz. Vielleicht ist am auffälligsten, dass die Anwendung nur in einer kleinen Teilmenge von Sprachen im Vergleich zum Windows-Betriebssystem selbst verfügbar ist. Wenn z. B. die GuiStep_1 Anwendung aus dem vorherigen Schritt auf einem japanischen Build von Windows installiert wurde, sind Ressourcenspeicherortfehler wahrscheinlich.

Um diese Situation zu beheben, haben Sie zwei primäre Optionen:

  • Stellen Sie sicher, dass Ressourcen in einer vorher festgelegten endgültigen Fallbacksprache enthalten sind.
  • Bieten Sie dem Endbenutzer eine Möglichkeit, seine Spracheinstellungen aus der Teilmenge der Sprachen zu konfigurieren, die speziell von der Anwendung unterstützt werden.

Von diesen Optionen ist die eine endgültige Fallbacksprache dringend ratsam und wird in diesem Tutorial implementiert, indem sie die Flag -g an muirct.exe übergeben, wie oben zu sehen. Diese Kennzeichnung teilt muirct.exe mit, eine bestimmte Sprache (de-DE / 0x0407) zur endgültigen Fallbacksprache zu machen, die dem sprachneutralen DLL-Modul (HelloModule.dll) zugeordnet ist. Zur Laufzeit wird dieser Parameter als letztes Mittel verwendet, um einen Text zu erzeugen, der dem Benutzer angezeigt wird. Falls keine endgültige Fallbacksprache gefunden wird und keine entsprechende Ressource in der sprachneutralen Binärdatei verfügbar ist, schlägt das Laden der Ressource fehl. Daher sollten Sie sorgfältig die Szenarien ermitteln, auf die Ihre Anwendung stoßen kann, und die endgültige Fallbacksprache entsprechend planen.

Die andere Möglichkeit, konfigurierbare Spracheinstellungen zuzulassen und Ressourcen basierend auf dieser benutzerdefinierten Hierarchie zu laden, kann die Kundenzufriedenheit erheblich steigern. Leider wird dadurch auch die in der Anwendung benötigte Funktionalität erschwert.

In diesem Schritt des Tutorials wird ein vereinfachter Textdateimechanismus verwendet, um eine benutzerdefinierte Benutzersprachenkonfiguration zu ermöglichen. Die Textdatei wird zur Laufzeit von der Anwendung analysiert, und die analysierte und überprüfte Sprachliste wird zum Einrichten einer benutzerdefinierten Fallbackliste verwendet. Nachdem die benutzerdefinierte Fallbackliste eingerichtet wurde, laden die Windows-APIs Ressourcen gemäß der in dieser Liste festgelegten Sprachrangfolge. Der Rest des Codes ähnelt dem des vorangegangenen Schritts.

  1. Fügen Sie der HelloMUI-Projektmappe (unter Verwendung der Menüauswahl Datei, Hinzufügen und Neues Projekt) ein neues Projekt mit den folgenden Einstellungen und Werten hinzu:

    1. Projekttyp: Win32-Projekt.
    2. Name: GuiStep_2.
    3. Speicherort: Übernehmen Sie die Standardeinstellung.
    4. Wählen Sie im Win32-Anwendungs-Assistenten den Standardanwendungstyp aus: Windows-Anwendung.
  2. Legen Sie dieses Projekt so fest, dass es in Visual Studio ausgeführt wird, und konfigurieren Sie sein Threadingmodell:

    1. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das Projekt GuiStep_2, und wählen Sie Als Startprojekt festlegen aus.

    2. Klicken Sie erneut mit der rechten Maustaste, und wählen Sie Eigenschaften aus.

    3. Im Dialogfeld Eigenschaftenseiten des Projekts:

      1. Legen Sie in der Dropdownliste oben links Konfiguration auf Alle Konfigurationen fest.
      2. Erweitern Sie unter Konfigurationseigenschaften C/C++, wählen Sie Codegenerierung aus, und legen Sie die Laufzeitbibliothek fest: Multithread-Debug (/MTd).
  3. Ersetzen Sie den Inhalt von GuiStep_2.cpp durch den folgenden Code:

    // 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;
    }
    
  4. Erstellen Sie eine Unicode-Textdatei langs.txt mit der folgenden Zeile:

    hi-IN ta-IN ru-RU fr-FR es-ES en-US
    

    Hinweis

    Achten Sie darauf, die Datei als Unicode zu speichern.

     

    Kopieren Sie die Datei langs.txt in das Verzeichnis, in dem das Programm ausgeführt werden soll:

    • Wenn sie in Visual Studio ausgeführt wird, kopieren Sie sie in ProjectRootDirectory\HelloMUI\GuiStep_2.
    • Wenn Sie von Windows Explorer ausgeführt wird, kopieren Sie sie in dasselbe Verzeichnis wie GuiStep_2.exe.
  5. Erstellen Sie das Projekt, und führen Sie es aus. Versuchen Sie, langs.txt zu bearbeiten, damit verschiedene Sprachen am Anfang der Liste angezeigt werden.

Schritt 5: Anpassen von „Hello MUI“

Eine Reihe der bisher in diesem Tutorial erwähnten Laufzeitfunktionen sind nur unter Windows Vista und höher verfügbar. Sie können den Aufwand, der in das Lokalisieren und Aufteilen von Ressourcen investiert wurde, wiederverwenden, indem Sie die Anwendung für Windows-Betriebssystemversionen, z. B. Windows XP, bearbeiten. Dieser Vorgang umfasst das Anpassen des vorherigen Beispiels in zwei Schlüsselbereichen:

  • Die Pre-Windows Vista-Ressourcenladefunktionen (z. B. LoadString, LoadIcon, LoadBitmap, FormatMessage und andere) sind nicht MUI-fähig. Anwendungen, die mit geteilten Ressourcen (LN- und .mui-Dateien) ausgeliefert werden, müssen Ressourcenmodule mit einer dieser beiden Funktionen laden:

    • Wenn die Anwendung nur unter Windows Vista und höher ausgeführt werden soll, sollte sie Ressourcenmodule mit LoadLibraryEx laden.
    • Wenn die Anwendung auf Versionen vor Windows Vista und Windows Vista oder höher ausgeführt werden soll, muss sie LoadMUILibrary verwenden, bei der es sich um eine bestimmte Downlevel-Funktion handelt, die im Windows 7 SDK bereitgestellt wird.
  • Die Sprachverwaltung und die Unterstützung von Fallbackreihenfolgen, die in den Windows-Betriebssystemversionen vor Windows Vista angeboten wird, unterscheidet sich erheblich von der in Windows Vista und späteren Versionen. Aus diesem Grund müssen Anwendungen, die das Fallback von Benutzer konfigurierten Sprachen zulassen, ihre Methoden für die Sprachverwaltung anpassen:

    • Wenn die Anwendung nur unter Windows Vista und höher ausgeführt werden soll, reicht das Festlegen der Sprachliste mithilfe von SetThreadPreferredUILanguages aus.
    • Wenn die Anwendung auf allen Windows-Versionen ausgeführt werden soll, muss Code erstellt werden, der auf Abwärtsplattformen ausgeführt wird, um die vom Benutzer konfigurierte Sprachliste zu durchlaufen und den Prüfpunkt für das gewünschte Ressourcenmodul zu durchlaufen. Dies ist in den Abschnitten 1c und 2 des Codes zu sehen, der weiter unten in diesem Schritt bereitgestellt wird.

Erstellen Sie ein Projekt, das die lokalisierten Ressourcenmodule in einer beliebigen Windows-Version verwenden kann:

  1. Fügen Sie der HelloMUI-Projektmappe (unter Verwendung der Menüauswahl Datei, Hinzufügen und Neues Projekt) ein neues Projekt mit den folgenden Einstellungen und Werten hinzu:

    1. Projekttyp: Win32-Projekt.
    2. Name: GuiStep_3.
    3. Speicherort: Übernehmen Sie die Standardeinstellung.
    4. Wählen Sie im Win32-Anwendungs-Assistenten den Standardanwendungstyp aus: Windows-Anwendung.
  2. Legen Sie dieses Projekt so fest, dass es in Visual Studio ausgeführt wird, und konfigurieren Sie sein Threadingmodell. Konfigurieren Sie sie außerdem, um die erforderlichen Header und Bibliotheken hinzuzufügen.

    Hinweis

    Die in diesem Tutorial verwendeten Pfade gehen davon aus, dass das Windows 7 SDK und das Microsoft NLS Downlevel APIs-Paket in ihren Standardverzeichnissen installiert wurden. Wenn dies nicht der Fall ist, ändern Sie die Pfade entsprechend.

     

    1. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das Projekt GuiStep_3, und wählen Sie Als Startprojekt festlegen aus.

    2. Klicken Sie erneut mit der rechten Maustaste, und wählen Sie Eigenschaften aus.

    3. Im Dialogfeld Eigenschaftenseiten des Projekts:

      1. Legen Sie in der Dropdownliste oben links Konfiguration auf Alle Konfigurationen fest.

      2. Erweitern Sie unter Konfigurationseigenschaften C/C++, wählen Sie Codegenerierung aus, und legen Sie die Laufzeitbibliothek fest: Multithread-Debug (/MTd).

      3. Wählen Sie Allgemein aus, und fügen Sie sie zu zusätzlichen Includeverzeichnissen hinzu:

        • „C:\Microsoft NLS Downlevel-APIs\Include“.
      4. Wählen Sie Sprache aus, und legen Sie Wchar_t als integrierten Typ behandeln fest: Nein (/Zc:wchar_t-).

      5. Wählen Sie Erweitert aus, und legen Sie die Anrufkonvention fest: _stdcall (/Gz).

      6. Erweitern Sie unter Konfigurationseigenschaften Linker, wählen Sie Eingabe aus, und fügen Sie weitere Abhängigkeiten hinzu:

        • „C:\Program Files\Microsoft SDKs\Windows\v7.0\Lib\MUILoad.lib“.
        • „C:\Microsoft NLS Downlevel APIs\Lib\x86\Nlsdl.lib“.
  3. Ersetzen Sie den Inhalt von GuiStep_3.cpp durch den folgenden Code:

    // 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;
    }
    
  4. Erstellen oder kopieren Sie langs.txt in das entsprechende Verzeichnis, wie zuvor in Schritt 4: Globalisieren von „Hello MUI“ beschrieben.

  5. Erstellen Sie das Projekt, und führen Sie es aus.

Hinweis

Wenn die Anwendung unter Windows-Versionen vor Windows Vista ausgeführt werden soll, lesen Sie unbedingt die Dokumente, die im Lieferumfang des Microsoft NLS-Downlevel-APIs-Pakets enthalten sind, um nlsdl.dll neu zu verteilen. (Dies ist nicht mehr im Microsoft Download Center verfügbar. Verwenden Sie ICU-Globalisierungs-APIs unter Update Mai 2019 von Windows 10 und höheren Versionen. )

 

Weitere Überlegungen zu MUI

Unterstützung für Konsolenanwendungen

Die in diesem Tutorial behandelten Techniken können auch in Konsolenanwendungen verwendet werden. Im Gegensatz zu den meisten Standard-GUI-Steuerelementen kann das Windows-Befehlsfenster jedoch keine Zeichen für alle Sprachen anzeigen. Aus diesem Grund erfordern mehrsprachige Konsolenanwendungen besondere Aufmerksamkeit.

Das Aufrufen der APIs SetThreadUILanguage oder SetThreadPreferredUILanguages mit bestimmten Filterkennzeichnungen bewirkt, dass die Ressourcenladefunktionen Sprachressourcensonden für bestimmte Sprachen entfernen, die normalerweise nicht in einem Befehlsfenster angezeigt werden. Wenn diese Flags festgelegt sind, können die Spracheinstellungsalgorithmen nur die Sprachen zulassen, die im Befehlsfenster ordnungsgemäß in der Fallbackliste angezeigt werden.

Weitere Informationen zur Verwendung dieser APIs zum Erstellen einer mehrsprachigen Konsolenanwendung finden Sie in den Anmerkungen zu SetThreadUILanguage und SetThreadPreferredUILanguages.

Bestimmung der zu unterstützenden Sprachen zur Laufzeit

Sie können einen der folgenden Entwurfsvorschläge einführen, um zu bestimmen, welche Sprachen Ihre Anwendung zur Laufzeit unterstützen soll:

  • Geben Sie dem Endbenutzer während der Installation die Möglichkeit, die bevorzugte Sprache aus einer Liste von unterstützten Sprachen auszuwählen

  • Lesen einer Sprachliste aus einer Konfigurationsdatei

    Einige der Projekte in diesem Tutorial enthalten eine Funktion zum Analysieren einer langs.txt-Konfigurationsdatei, die eine Sprachliste enthält.

    Da diese Funktion externe Eingaben entgegennimmt, sollten Sie die Sprachen, die Sie eingeben, validieren. Weitere Informationen zum Ausführen dieser Überprüfung finden Sie in den Funktionen IsValidLocaleName oder DownLevelLocaleNameToLCID.

  • Abfragen des Betriebssystems, um zu ermitteln, welche Sprachen installiert sind

    Dieser Ansatz hilft der Anwendung, dieselbe Sprache wie das Betriebssystem zu verwenden. Obwohl dies keine Benutzeraufforderung erfordert, beachten Sie bei Auswahl dieser Option, dass Betriebssystemsprachen jederzeit hinzugefügt oder entfernt werden können und sich ändern können, nachdem der Benutzer die Anwendung installiert hat. Beachten Sie außerdem, dass das Betriebssystem in einigen Fällen mit eingeschränkter Sprachunterstützung installiert wird und die Anwendung mehr Wert bietet, wenn sie Sprachen unterstützt, die vom Betriebssystem nicht unterstützt werden.

    Weitere Informationen zum Ermitteln der aktuell installierten Sprachen im Betriebssystem finden Sie in der Funktion EnumUILanguages.

Unterstützung komplexer Skripts in Versionen vor Windows Vista

Wenn eine Anwendung, die bestimmte komplexe Skripts unterstützt, auf einer Windows-Version vor Windows Vista ausgeführt wird, wird Text in diesem Skript möglicherweise nicht ordnungsgemäß in GUI-Komponenten angezeigt. In diesem Tutorial werden beispielsweise hi-IN- und ta-IN-Skripts aufgrund von Problemen mit der Verarbeitung komplexer Skripts und fehlender verwandter Schriftarten möglicherweise nicht im Meldungsfeld angezeigt. Normalerweise stellen sich Probleme dieser Art als quadratische Felder in der GUI-Komponente dar.

Weitere Informationen zum Aktivieren komplexer Skriptverarbeitung finden Sie unter Skript- und Schriftartunterstützung in Windows.

Zusammenfassung

In diesem Tutorial wurde eine monolinguale Anwendung globalisiert und die folgenden bewährten Methoden veranschaulicht.

  • Entwerfen Sie die Anwendung so, dass sie das Win32-Ressourcenmodell nutzt.
  • Nutzen Sie die MUI-Aufteilung von Ressourcen in Satellitenbinärdateien (.mui-Dateien).
  • Stellen Sie sicher, dass der Lokalisierungsprozess die Ressourcen in den .mui-Dateien aktualisiert, damit sie für die Zielsprache geeignet sind.
  • Stellen Sie sicher, dass das Packen und Bereitstellen der Anwendung, der zugehörigen .mui-Dateien und der Konfigurationsinhalte korrekt erfolgt, damit die APIs zum Laden von Ressourcen die lokalisierten Inhalte finden können.
  • Stellen Sie dem Endbenutzer einen Mechanismus zum Anpassen der Sprachkonfiguration der Anwendung bereit.
  • Passen Sie den Laufzeitcode an, um die Vorteile der Sprachkonfiguration zu nutzen, damit die Anwendung besser auf die Bedürfnisse der Endbenutzer reagieren kann.