Пример многоязыкового интерфейса пользователя: Application-Specific параметров (Windows Vista)
Пример приложения, описанный в этом разделе, — это еще одно приложение Hello MUI, которое поддерживает параметры конкретных приложений для языков пользовательского интерфейса и работает в Windows Vista и более поздних версий.
Это приложение сначала анализирует список языков с разделителями в текстовом файле и преобразует его в список языков с несколькими строками, чтобы определить языковые настройки для конкретного приложения. В примере поддерживаются следующие разделители: ",", ";", ":" и " ". После анализа списка код находит и загружает ресурсы на указанном языке, как и в примере параметров системы. Этот код загружает и освобождает файлы ресурсов с помощью вызовов функций загрузчика ресурсов LoadLibraryEx и FreeLibrary.
// Vista and later enabled application, this application will not work on OS versions prior to Vista
#include <windows.h>
#include <wchar.h>
#include <strsafe.h>
#include "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)
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 forward
// using SetProcessPreferredUILanguages is recommended 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 forward
if(!SetThreadPreferredUILanguages(MUI_LANGUAGE_NAME,userLanguagesMultiString,&langCount) || langCount == 0)
{
swprintf_s(displayBuffer,SUFFICIENTLY_LARGE_ERROR_BUFFER,L"FAILURE: Unable to set the user defined, 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;
}