アプリケーションへの多言語ユーザー インターフェイスのサポートの追加
このチュートリアルでは、単一言語のアプリケーションを世界対応にする方法について説明します。 このアプリケーションは、Microsoft Visual Studio 内でビルドされる完全なソリューションの形式になっています。
- 概要
- Hello MUI ソリューションのセットアップ
- ステップ 0: ハードコーディングされた Hello MUI の作成
- ステップ 1: 基本的なリソース モジュールの実装
- ステップ 2: 基本的なリソース モジュールのビルド
- ステップ 3: リソース対応の "Hello MUI" の作成
- ステップ 4: "Hello MUI" のグローバル化
- ステップ 5: "Hello MUI" のカスタマイズ
- MUI に関するその他の考慮事項
- まとめ
概要
Windows Vista 以降の Windows オペレーティング システムは、それ自体が、Windows MUI リソース モデルを使用する多言語アプリケーションを作成できる追加のサポートを使って、多言語プラットフォームとして最初から構築されました。
このチュートリアルでは、多言語アプリケーションに対するこの新しいサポートの次の側面について説明します。
- 改善された MUI プラットフォームのサポートを使って、多言語アプリケーションを簡単に有効にします。
- Windows Vista より前のバージョンの Windows で実行するように、多言語アプリケーションを拡張します。
- コンソール アプリケーションなどの特殊な多言語アプリケーションの開発に関連するその他の考慮事項について説明します。
以下のリンクは、国際化と MUI に関する概念を簡単に再確認するのに役立ちます。
Hello MUI の背後にあるアイデア
基本的なプログラミングの概念を示す従来の Hello World アプリケーションには慣れているでしょう。 このチュートリアルでは、同様の方法により、MUI リソース モデルを使って単一言語アプリケーションを更新し、Hello MUI という名前の多言語バージョンを作成する方法を説明します。
Note
このチュートリアルのタスクについては、アクティビティを正確に行う必要があり、これらのタスクの経験がほとんどない開発者に詳しく説明する必要があるため、詳細な手順を説明します。
Hello MUI ソリューションのセットアップ
以下の手順では、Hello MUI ソリューションの作成の準備方法について説明します。
プラットフォームの要件
このチュートリアルのコード サンプルは、Windows 7 および Visual Studio 2008 用の Windows ソフトウェア開発キット (SDK) を使ってコンパイルする必要があります。 Windows 7 SDK は Windows XP、Windows Vista、Windows 7 にインストールされ、サンプル ソリューションは、これらのオペレーティング システムのバージョンのいずれでもビルドできます。
このチュートリアルのすべてのコード サンプルは、x86 と x64 バージョンの Windows XP、Windows Vista、Windows 7 で実行するように設計されています。 Windows XP で動作しない特定の部分は、必要に応じて明示されています。
前提条件
Visual Studio 2008 をインストールします。
詳しくは、Visual Studio デベロッパー センターをご覧ください。
Windows SDK for Windows 7 をインストールします。
Windows デベロッパー センターの Windows SDK のページからインストールできます。 SDK には、Windows XP から最新バージョンまでの OS バージョン用のアプリケーションを開発するためのユーティリティが含まれています。
Note
パッケージを既定の場所にインストールしない場合、またはシステム ドライブ (通常は C ドライブ) にインストールしない場合は、インストール パスを記録しておいてください。
Visual Studio のコマンド ライン パラメーターを構成します。
- Visual Studio のコマンド ウィンドウを開きます。
- 「set path」と入力します。
- パス変数に Windows 7 SDK の bin フォルダーのパス (...Microsoft SDKs\Windows\v7.0\bin) が含まれていることを確認します
Microsoft NLS ダウンレベル API アドオン パッケージをインストールします。
Note
このチュートリアルのコンテキストでは、このパッケージは、Windows Vista より前のバージョンの Windows で実行するようにアプリケーションをカスタマイズする場合にのみ必要です。 「ステップ 5: Hello MUI のカスタマイズ」をご覧ください。
Microsoft ダウンロード センターから入手できなくなったパッケージをダウンロードしてインストールします。 Windows 10 May 2019 Update 以降のバージョンの ICU グローバリゼーション API を使います。
Windows SDK と同様に、パッケージを既定の場所にインストールしない場合、またはシステム ドライブ (通常は C ドライブ) にインストールしない場合は、インストール パスを記録しておいてください。
開発プラットフォームが Windows XP または Windows Server 2003 の場合は、Nlsdl.dll が正しくインストールされて登録されていることを確認します。
- インストール パスの場所にある "redist" フォルダーを参照します。
- 適切な再頒布可能コンポーネント Nlsdl.*.exe (nlsdl.x86.exe など) を実行します。 このステップでは、Nlsdl.dll をインストールして登録します。
Note
MUI を使用し、Windows Vista より前のバージョンの Windows で実行する必要があるアプリケーションを開発する場合は、対象の Windows プラットフォームに Nlsdl.dll が存在している必要があります。 ほとんどの場合、これは、(Nlsdl.dll 自体を単にコピーするのではなく) アプリケーションで再頒布可能な Nlsdl インストーラーを実行してインストールする必要があることを意味します。
ステップ 0: ハードコーディングされた Hello MUI の作成
このチュートリアルは、単一言語バージョンの Hello MUI アプリケーションから始めます。 このアプリケーションでは、C++ プログラミング言語、ワイド文字列、および出力に MessageBoxW 関数を使うことが想定されています。
最初に、初期の GuiStep_0 アプリケーションと、このチュートリアルのすべてのアプリケーションを含む HelloMUI ソリューションを作成します。
Visual Studio 2008 で、新しいプロジェクトを作成します。 次の設定と値を使います。
- プロジェクトの種類: Visual C++ で Win32 を選んでから、Visual Studio にインストールされているテンプレートで Win32 プロジェクトを選びます。
- 名前: GuiStep_0。
- 場所: ProjectRootDirectory (後の手順でこのディレクトリを参照します)。
- ソリューション名: HelloMUI。
- [ソリューションのディレクトリを作成する] を選びます。
- Win32 アプリケーション ウィザードで、既定のアプリケーションの種類 (Windows アプリケーション) を選びます。
プロジェクト スレッド モデルを構成します。
ソリューション エクスプローラーで、GuiStep_0 プロジェクトを右クリックして、[プロパティ] を選びます。
プロジェクトの [プロパティ ページ] ダイアログ ボックスで次のようにします。
- 左上のドロップダウンで、[構成] を [すべての構成] に設定します。
- [構成プロパティ] で [C/C++] を展開し、[コード生成] を選んで、[ランタイム ライブラリ] を [マルチスレッド デバッグ (/MTd)] に設定します。
GuiStep_0.cpp の内容を次のコードに置き換えます。
// 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; }
アプリケーションをビルドして実行します。
上記の単純なソース コードでは、ユーザーに表示されるすべての出力 (この場合は "Hello MUI" というテキスト) をハード コーディングする (埋め込む) 単純な設計が選択されています。 この選択により、英語の単語 "Hello" の意味がわからないユーザーに対するアプリケーションの有用性が制限されます。MUI はテクノロジ ベースの英語の頭字語であるため、このチュートリアルでは、この文字列はすべての言語で "MUI" のままであると想定されています。
ステップ 1: 基本的なリソース モジュールの実装
Microsoft Win32 には、以前から、アプリケーション開発者が UI リソース データをアプリケーション ソース コードから分離する機能がありました。 この分離を提供する Win32 リソース モデルでは、ユーザーに通常表示される文字列、ビットマップ、アイコン、メッセージ、その他の項目は、実行可能なコードから分離され、実行可能ファイルの個別のセクションにパッケージ化されます。
実行可能なコードとリソース データ パッケージの間の分離を説明するため、このステップでは、以前はハードコーディングされていた "Hello" という文字列 ("en-US" リソース) を、HelloModule_en_us プロジェクトの DLL モジュールのリソース セクションに配置します。
この Win32 DLL には、ライブラリの種類の実行可能機能を含めることもできます (他の DLL の場合と同様)。 ただし、Win32 リソース関連の側面に注目しやすいよう、ランタイム DLL のコードは dllmain.cpp にスタブとして外出しにします。 このチュートリアルの以降のセクションでは、ここでビルドされる HelloModule リソース データを使い、適切なランタイム コードも示します。
Win32 リソース モジュールを構築するには、まず、スタブ化された dllmain を含む DLL を作成します。
HelloMUI ソリューションに新しいプロジェクトを追加します。
- [ファイル] メニューの [追加] を選んでから、[新しいプロジェクト] を選びます。
- プロジェクトの種類: Win32 プロジェクト。
- 名前: HelloModule_en_us。
- 場所: ProjectRootDirectory\HelloMUI.
- Win32 アプリケーション ウィザードで、アプリケーションの種類として DLL を選びます。
このプロジェクトのスレッド モデルを構成します。
ソリューション エクスプローラーで、HelloModule_en_us プロジェクトを右クリックして、[プロパティ] を選びます。
プロジェクトの [プロパティ ページ] ダイアログ ボックスで次のようにします。
- 左上のドロップダウンで、[構成] を [すべての構成] に設定します。
- [構成プロパティ] で [C/C++] を展開し、[コード生成] を選んで、[ランタイム ライブラリ] を [マルチスレッド デバッグ (/MTd)] に設定します。
dllmain.cpp を調べます。 (ここで行っているように、生成されたコードに UNREFERENCED_PARAMETER マクロを追加して、警告レベル 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; }
リソース定義ファイル HelloModule.rc をプロジェクトに追加します。
HelloModule_en_us プロジェクトで、[リソース ファイル] フォルダーを右クリックし、[追加] を選んでから [新しい項目] を選びます。
[新しい項目の追加] ダイアログ ボックスで、次のように選択します。
- カテゴリ: [Visual C++] で [リソース] を選んでから、[Visual Studio にインストールされたテンプレート] で [リソース ファイル (.rc)] を選びます。
- 名前: HelloModule。
- 場所: 既定値をそのまま使います。
- [追加] をクリックします。
新しい HelloModule.rc ファイルを Unicode として保存することを指定します。
- ソリューション エクスプローラーで、[HelloModule.rc] を右クリックして、[コードの表示] を選びます。
- ファイルが既に開かれていることを示すメッセージが表示され、ファイルを閉じるかどうかを確認するメッセージが表示されたら、[はい] をクリックします。
- ファイルがテキストとして表示されたら、[ファイル] メニューを選んで [高度な保存オプション] を選びます。
- [エンコード] で [Unicode - コードページ 1200] を指定します。
- OK をクリックします。
- HelloModule.rc を保存して閉じます。
"Hello" という文字列を含む文字列テーブルを追加します。
リソース ビューで HelloModule.rc を右クリックして、[リソースの追加] を選びます。
[ストリング テーブル] を選びます。
新規 をクリックします。
ストリング テーブルに文字列を追加します。
- ID: HELLO_MUI_STR_0。
- 値: 0。
- キャプション: Hello。
HelloModule.rc をテキストとして表示すると、リソース固有のさまざまなソース コードが表示されます。 最も関心がある部分の 1 つは、"Hello" という文字列が記述されているセクションです。
///////////////////////////////////////////////////////////////////////////// // // String Table // STRINGTABLE BEGIN HELLO_MUI_STR_0 "Hello" END
この "Hello" という文字列は、アプリケーションでサポートするすべての言語にローカライズ (つまり、翻訳) する必要があるリソースです。 たとえば、HelloModule_ta_in プロジェクト (次のステップで作成します) には、"ta-IN" 用にローカライズされた HelloModule.rc の独自のバージョンが含まれます。
///////////////////////////////////////////////////////////////////////////// // // String Table // STRINGTABLE BEGIN HELLO_MUI_STR_0 "வணக்கம்" END
HelloModule_en_us プロジェクトをビルドして、エラーがないことを確認します。
HelloMUI ソリューションにさらに 6 つの (または好きなだけいくつでも) プロジェクトを作成し、追加の言語用に 6 つのリソース DLL を作成します。 次の表の値を使います。
プロジェクト名 .rc ファイルの名前 "文字列 ID" 文字列値 文字列キャプション 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 வணக்கம்
.rc ファイルの構造と構文について詳しくは、「リソース ファイルについて」をご覧ください。
ステップ 2: 基本的なリソース モジュールのビルド
以前のリソース モデルを使い、7 つの HelloModule プロジェクトのいずれかをビルドすると、7 つの個別の DLL が生成されます。 各 DLL には、適切な言語にローカライズされた 1 つの文字列を含むリソース セクションが含まれます。 過去の Win32 リソース モデルには適していますが、この設計では MUI は利用されません。
Windows Vista SDK 以降では、MUI により実行可能ファイルをソース コードとローカライズ可能なコンテンツ モジュールに分割する機能が提供されます。 後のステップ 5 で説明する追加のカスタマイズでは、多言語サポートを有効にしたアプリケーションを Windows Vista より前のバージョンで実行できます。
Windows Vista 以降での、実行可能コードからリソースを分割するために使用できる主なメカニズムは、次のとおりです。
- 特定のスイッチでの rc.exe (RC コンパイラ) の使用、または
- muirct.exe という名前のビルド後スタイル分割ツールの使用。
詳しくは、「リソース ユーティリティ」をご覧ください。
わかりやすくするため、このチュートリアルでは muirct.exe を使って "Hello MUI" の実行可能ファイルを分割します。
さまざまな言語リソース モジュールの分割: 概要
1 つの実行可能な HelloModule.dll と、このチュートリアルでサポートされている 7 つの言語ごとの HelloModule.dll.mui への DLL の分割には、マルチパート プロセスが含まれます。 この概要では必要な手順について説明し、次のセクションではそれらの手順を実行するコマンド ファイルを示します。
最初に、次のコマンドを使って、英語のみの HelloModule.dll モジュールを分割します。
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
上のコマンド ラインでは、構成ファイル DoReverseMuiLoc.rcconfig が使われています。 通常、この種類の構成ファイルは、言語に依存しない (LN) DLL と言語に依存する .mui ファイルの間でリソースを分割するために、muirct.exe によって使われます。 この例の DoReverseMuiLoc.rcconfig xml ファイル (次のセクションに記載) では多くのリソースの種類が示されていますが、それらはすべて "localizedResources" または .mui ファイル カテゴリに分類され、言語に依存しないカテゴリにはリソースがありません。 リソース構成ファイルの準備方法について詳しくは、「リソース構成ファイルの準備」をご覧ください。
英語の文字列 "Hello" を含む HelloModule.dll.mui ファイルを作成するだけでなく、muirct.exe では分割の間に MUI リソースが HelloModule.dll モジュールに埋め込まれます。 実行時に言語固有の HelloModule.dll.mui モジュールから適切なリソースが適切に読み込まれるようにするため、各 .mui ファイルには、基になる言語に依存しない LN モジュールのチェックサムと一致するチェックサムが設定されている必要があります。 これは、次のようなコマンドによって行われます。
muirct.exe -c HelloModule.dll -e en-US\HelloModule.dll.mui
同様に、他の各言語用の HelloModule.dll.mui ファイルを作成するために muirct.exe が呼び出されます。 ただし、言語に依存しない DLL は、最初に作成されるものだけに必要なので、それらの場合には破棄されます。 スペイン語とフランス語のリソースを処理するコマンドは次のようになります。
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
上記の muirct.exe コマンド ラインで注目すべき最も重要なことの 1 つは、ターゲットの言語 ID を指定するために -x フラグが使われていることです。 muirct.exe に提供される値は、言語固有の HelloModule.dll モジュールごとに異なります。 この言語の値は中央にあり、分割中に .mui ファイルを適切にマークするために muirct.exe によって使われます。 値が正しくないと、実行時にその特定の .mui ファイルでリソース読み込みエラーが発生します。 言語名と LCID の対応について詳しくは、「言語識別子の定数と文字列」をご覧ください。
各分割により、.mui ファイルは、言語に依存しない HelloModule.dll が存在するディレクトリのすぐ下にある、その言語名に対応するディレクトリに配置されます。 .mui ファイルの配置について詳しくは、「アプリケーションの展開」をご覧ください。
さまざまな言語リソース モジュールの分割: ファイルの作成
このチュートリアルでは、さまざまな DLL を分割するコマンドを含むコマンド ファイルを作成し、手動でそれを呼び出します。 実際の開発作業では、これらのコマンドをビルド前またはビルド後のイベントとして HelloMUI ソリューションに含めることで、ビルド エラーの可能性を減らせることに注意してください。ただし、それはこのチュートリアルの範囲外です。
デバッグ ビルド用のファイルを作成します。
次のコマンドを含むコマンド ファイル DoReverseMuiLoc.cmd を作成します。
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
次の行を含む xml ファイル DoReverseMuiLoc.rcconfig を作成します。
<?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>
DoReverseMuiLoc.cmd と DoReverseMuiLoc.rcconfig を <プロジェクト ルート ディレクトリ>\HelloMUI\Debug にコピーします。
Visual Studio 2008 のコマンド プロンプトを開き、Debug ディレクトリに移動します。
DoReverseMuiLoc.cmd を実行します。
リリース ビルドを作成するときは、同じ DoReverseMuiLoc.cmd ファイルと DoReverseMuiLoc.rcconfig ファイルを Release ディレクトリにコピーし、そこでコマンド ファイルを実行します。
ステップ 3: リソース対応の "Hello MUI" の作成
上記の最初のハードコーディングされた GuiStep_0.exe の例に基づき、Win32 リソース モデルを組み込むことで、アプリケーションの範囲を複数の言語のユーザーに拡張できます。 このステップで示す新しい実行時コードには、モジュールの読み込み (LoadLibraryEx) と文字列の取得 (LoadString) のロジックが含まれます。
次の設定と値を使って、HelloMUI ソリューションに新しいプロジェクトを追加します (メニューの [ファイル] > [追加] > [新しいプロジェクト] を使用)。
- プロジェクトの種類: Win32 プロジェクト。
- 名前: GuiStep_1。
- 場所: 既定値をそのまま使います。
- Win32 アプリケーション ウィザードで、既定のアプリケーションの種類 (Windows アプリケーション) を選びます。
このプロジェクトを Visual Studio 内から実行するように設定し、スレッド モデルを構成します。
ソリューション エクスプローラーで、GuiStep_1 プロジェクトを右クリックして、[スタートアップ プロジェクトとして設定] を選びます。
それをもう一度右クリックして、[プロパティ] を選びます。
プロジェクトの [プロパティ ページ] ダイアログ ボックスで次のようにします。
- 左上のドロップダウンで、[構成] を [すべての構成] に設定します。
- [構成プロパティ] で [C/C++] を展開し、[コード生成] を選んで、[ランタイム ライブラリ] を [マルチスレッド デバッグ (/MTd)] に設定します。
GuiStep_1.cpp の内容を次のコードに置き換えます。
// 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; }
アプリケーションをビルドして実行します。 出力は、コンピューターの表示言語として現在設定されている言語で表示されます (ビルドした 7 つの言語のいずれかである場合)。
ステップ 4: "Hello MUI" のグローバル化
前の例では、さまざまな言語で出力を表示できますが、足りない部分がいくつかあります。 おそらく最も気になるのは、Windows オペレーティング システム自体と比べて、アプリケーションを使用できるのが言語の小さなサブセットのみであるということです。 たとえば、前のステップの GuiStep_1 アプリケーションを Windows の日本語ビルドにインストールした場合、リソースの場所でエラーが発生する可能性があります。
この状況に対処するには、主に次の 2 つのオプションがあります。
- あらかじめ決定されている最終フォールバック言語のリソースが含まれるようにします。
- アプリケーションで明示的にサポートされている言語のサブセットからエンド ユーザーが言語の優先順位を構成する手段を提供します。
これらのオプションのうち、強くお勧めするのは最終フォールバック言語を提供する方法であり、このチュートリアルでは、上で示したように、-g フラグを muirct.exe に渡すことによってそれを実装しています。 このフラグは、特定の言語 (de-DE/0x0407) を、言語に依存しない dll モジュール (HelloModule.dll) に関連付けられた最終フォールバック言語にするよう、muirct.exe に指示します。 実行時には、ユーザーに表示するテキストを生成するための最後の手段としてこのパラメーターが使われます。 最終フォールバック言語が見つからず、言語に依存しないバイナリで適切なリソースを使用できない場合、リソースの読み込みは失敗します。 そのため、アプリケーションで発生する可能性があるシナリオを慎重に判断し、それに応じて最終フォールバック言語を計画する必要があります。
言語の優先順位を構成できるようにし、このユーザー定義の階層に基づいてリソースを読み込むというもう 1 つのオプションを使うと、顧客満足度が大幅に向上する可能性があります。 残念ながら、アプリケーション内で必要な機能も複雑になります。
チュートリアルのこのステップでは、簡略化されたテキスト ファイルのメカニズムを使って、カスタム ユーザー言語構成を有効にします。 実行時にアプリケーションによってテキスト ファイルが解析され、解析および検証された言語リストが、カスタム フォールバック リストの設定に使われます。 カスタム フォールバック リストが設定されると、Windows API は、このリストで設定されている言語の優先順位に従ってリソースを読み込みます。 コードの残りの部分は、前のステップで見たものと似ています。
次の設定と値を使って、HelloMUI ソリューションに新しいプロジェクトを追加します (メニューの [ファイル] > [追加] > [新しいプロジェクト] を使用)。
- プロジェクトの種類: Win32 プロジェクト。
- 名前: GuiStep_2。
- 場所: 既定値をそのまま使います。
- Win32 アプリケーション ウィザードで、既定のアプリケーションの種類 (Windows アプリケーション) を選びます。
このプロジェクトを Visual Studio 内から実行するように設定し、スレッド モデルを構成します。
ソリューション エクスプローラーで、GuiStep_2 プロジェクトを右クリックして、[スタートアップ プロジェクトとして設定] を選びます。
それをもう一度右クリックして、[プロパティ] を選びます。
プロジェクトの [プロパティ ページ] ダイアログ ボックスで次のようにします。
- 左上のドロップダウンで、[構成] を [すべての構成] に設定します。
- [構成プロパティ] で [C/C++] を展開し、[コード生成] を選んで、[ランタイム ライブラリ] を [マルチスレッド デバッグ (/MTd)] に設定します。
GuiStep_2.cpp の内容を次のコードに置き換えます。
// 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; }
次の行を含む Unicode テキスト ファイル langs.txt を作成します。
hi-IN ta-IN ru-RU fr-FR es-ES en-US
Note
必ずファイルを Unicode として保存してください。
プログラムを実行するディレクトリに langs.txt をコピーします。
- Visual Studio 内から実行する場合は、それを <プロジェクト ルート ディレクトリ>\HelloMUI\GuiStep_2 にコピーします。
- エクスプローラーから実行する場合は、GuiStep_2.exe と同じディレクトリにコピーします。
プロジェクトをビルドして実行します。 リストの先頭が異なる言語になるように、langs.txt を編集してみてください。
ステップ 5: "Hello MUI" のカスタマイズ
このチュートリアルでこれまでに説明した実行時の機能の多くは、Windows Vista 以降でのみ使用できます。 Windows XP などのダウンレベルの Windows オペレーティング システム バージョンでアプリケーションを動くようにして、リソースのローカライズと分割に費やした作業を再利用したいと思うかもしれません。 このプロセスには、2 つの重要な部分に関する、前の例の調整が含まれます。
Windows Vista より前のリソース読み込み関数 (LoadString、LoadIcon、LoadBitmap、FormatMessage など) は、MUI に対応していません。 分割リソース (LN ファイルと .mui ファイル) に付属するアプリケーションでは、次の 2 つの関数のいずれかを使ってリソース モジュールを読み込む必要があります。
- アプリケーションを Windows Vista 以降でのみ実行する場合は、LoadLibraryEx を使ってリソース モジュールを読み込む必要があります。
- Windows Vista より前のバージョンと Windows Vista 以降の両方でアプリケーションを実行する場合は、Windows 7 SDK で提供されている特定のダウンレベル関数である LoadMUILibrary を使う必要があります。
Windows Vista より前のバージョンの Windows オペレーティング システムで提供されている言語管理と言語フォールバック順序のサポートは、Windows Vista 以降のバージョンと大きく異なります。 このため、ユーザーが言語フォールバックを構成できるアプリケーションでは、言語管理の方法を調整する必要があります。
- Windows Vista 以降だけでアプリケーションを実行する場合は、SetThreadPreferredUILanguages を使って言語リストを設定するだけで十分です。
- アプリケーションをすべての Windows バージョンで実行する場合は、ダウンレベル プラットフォームで実行し、ユーザーが構成した言語リストを反復処理して、目的のリソース モジュールをプローブするように、コードを構築する必要があります。 これは、このステップで後ほど示すコードのセクション 1c と 2 で確認できます。
任意のバージョンの Windows でローカライズされたリソース モジュールを使用できるプロジェクトを作成します。
次の設定と値を使って、HelloMUI ソリューションに新しいプロジェクトを追加します (メニューの [ファイル] > [追加] > [新しいプロジェクト] を使用)。
- プロジェクトの種類: Win32 プロジェクト。
- 名前: GuiStep_3。
- 場所: 既定値をそのまま使います。
- Win32 アプリケーション ウィザードで、既定のアプリケーションの種類 (Windows アプリケーション) を選びます。
このプロジェクトを Visual Studio 内から実行するように設定し、スレッド モデルを構成します。 また、必要なヘッダーとライブラリを追加するように構成します。
Note
このチュートリアルで使われるパスでは、Windows 7 SDK と Microsoft NLS ダウンレベル API パッケージが、既定のディレクトリにインストールされているものと想定しています。 そうでない場合は、パスを適切に変更してください。
ソリューション エクスプローラーで、GuiStep_3 プロジェクトを右クリックして、[スタートアップ プロジェクトとして設定] を選びます。
それをもう一度右クリックして、[プロパティ] を選びます。
プロジェクトの [プロパティ ページ] ダイアログ ボックスで次のようにします。
左上のドロップダウンで、[構成] を [すべての構成] に設定します。
[構成プロパティ] で [C/C++] を展開し、[コード生成] を選んで、[ランタイム ライブラリ] を [マルチスレッド デバッグ (/MTd)] に設定します。
[全般] を選び、[追加のインクルード ディレクトリ] に以下を追加します。
- "C:\Microsoft NLS Downlevel APIs\Include"
[言語] を選んで、[wchar_t をビルトイン型として扱う] を [いいえ (/Zc:wchar_t-)] に設定します。
[詳細設定] を選んで、[呼び出し規約] を [_stdcall (/Gz)] に設定します。
[構成プロパティ] で [リンカー] を展開し、[入力] を選んで、[追加の依存ファイル] に以下を追加します。
- "C:\Program Files\Microsoft SDKs\Windows\v7.0\Lib\MUILoad.lib"
- "C:\Microsoft NLS Downlevel APIs\Lib\x86\Nlsdl.lib"
GuiStep_3.cpp の内容を次のコードに置き換えます。
// 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: "Hello MUI" のグローバル化」で説明したように、適切なディレクトリに langs.txt を作成またはコピーします。
プロジェクトをビルドして実行します。
Note
Windows Vista より前の Windows バージョンでアプリケーションを実行する必要がある場合は、Microsoft NLS ダウンレベル API パッケージに付属するドキュメントで、Nlsdl.dll を再配布する方法を必ず読んでください。 (これは、Microsoft ダウンロード センターからは入手できなくなりました。Windows 10 May 2019 Update 以降のバージョンでは、ICU グローバリゼーション API を使用してください。)
MUI に関するその他の考慮事項
コンソール アプリケーションのサポート
このチュートリアルで説明した手法は、コンソール アプリケーションでも使用できます。 ただし、ほとんどの標準 GUI コントロールとは異なり、Windows コマンド ウィンドウではすべての言語の文字を表示することはできません。 このため、多言語コンソール アプリケーションでは特別な注意が必要です。
特定のフィルター フラグを指定して API SetThreadUILanguage または SetThreadPreferredUILanguages を呼び出すと、リソース読み込み関数は、通常コマンド ウィンドウ内に表示されない特定の言語の言語リソース プローブを削除します。 これらのフラグが設定されていると、言語設定アルゴリズムは、コマンド ウィンドウに正しく表示される言語のみを、フォールバック リストに設定します。
これらの API を使って多言語コンソール アプリケーションを構築する方法について詳しくは、SetThreadUILanguage と SetThreadPreferredUILanguages の解説セクションをご覧ください。
実行時にサポートする言語の決定
実行時にアプリケーションでサポートする必要がある言語の決定については、次のいずれかの設計提案を採用できます。
インストール時に、サポートされている言語の一覧からエンド ユーザーが優先言語を選択できるようにする
構成ファイルから言語リストを読み取る
このチュートリアルの一部のプロジェクトには、言語リストを含む langs.txt 構成ファイルの解析に使われる関数が含まれています。
この関数は外部入力を受け取るため、入力として指定された言語を検証します。 その検証の実行について詳しくは、IsValidLocaleName または DownLevelLocaleNameToLCID 関数をご覧ください。
オペレーティング システムに照会して、インストールされている言語を特定する
この方法は、アプリケーションでオペレーティング システムと同じ言語を使う場合に役立ちます。 このためにユーザー プロンプトを表示する必要はありませんが、このオプションを選ぶ場合は、オペレーティング システムの言語はいつでも追加または削除でき、ユーザーがアプリケーションをインストールした後で変更される可能性があることに注意してください。 また、場合によっては、オペレーティング システムのインストールで言語のサポートが制限されていて、オペレーティング システムがサポートしていない言語をアプリケーションがサポートしている場合、アプリケーションの価値が高まる可能性があることに注意してください。
オペレーティング システムに現在インストールされている言語を確認する方法について詳しくは、EnumUILanguages 関数をご覧ください。
Windows Vista より前のバージョンでの複雑なスクリプトのサポート
特定の複雑なスクリプトをサポートするアプリケーションを Windows Vista より前のバージョンの Windows で実行すると、そのスクリプト内のテキストが GUI コンポーネントに正しく表示されないことがあります。 たとえば、このチュートリアルのダウンレベル プロジェクトでは、複雑なスクリプトの処理に関する問題と、関連するフォントがないことのため、hi-IN スクリプトと ta-IN スクリプトがメッセージ ボックスに表示されない場合があります。 通常、この種の問題は、GUI コンポーネントに正方形のボックスとして表示されます。
複雑なスクリプトの処理を有効にする方法について詳しくは、「Windows でのスクリプトとフォントのサポート」をご覧ください。
まとめ
このチュートリアルでは、単一言語アプリケーションをグローバル化し、次のベスト プラクティスを示しました。
- Win32 リソース モデルを利用するようにアプリケーションを設計します。
- MUI を利用してリソースをサテライト バイナリ (.mui ファイル) に分割します。
- ローカライズ プロセスにより、.mui ファイル内のリソースがターゲット言語に適するものに更新されることを確認します。
- リソース読み込み API がローカライズされたコンテンツを見つけられるように、アプリケーション、関連する .mui ファイル、構成コンテンツのパッケージ化と展開が正しく行われることを確認します。
- アプリケーションの言語構成を調整するメカニズムをエンド ユーザーに提供します。
- 実行時コードを調整して言語構成を活用し、エンド ユーザーのニーズにアプリケーションがいっそう応えられるようにします。