413 lines
12 KiB
C++
413 lines
12 KiB
C++
/******************************************************************************
|
|
|
|
Copyright (c) 2002 Microsoft Corporation
|
|
|
|
Module Name:
|
|
muiutil.cpp
|
|
|
|
Abstract:
|
|
Implements helper functions for self-updating MUI stuff
|
|
|
|
******************************************************************************/
|
|
|
|
#include "pch.h"
|
|
#include "muiutil.h"
|
|
#include "osdet.h"
|
|
|
|
typedef struct tagSLangIDStringMap
|
|
{
|
|
LANGID langid;
|
|
LPTSTR szISOName;
|
|
} SLangIDStringMap;
|
|
|
|
/*
|
|
|
|
// this is a combination of the languages used by WU and the languages used
|
|
// by MUI
|
|
// Mappings determined from the following sources
|
|
// For langid to full language name : MSDN
|
|
// full language name to 2 char name: http://www.oasis-open.org/cover/iso639a.html
|
|
// country name to 2 character name : http://www.din.de/gremien/nas/nabd/iso3166ma
|
|
|
|
This table is no longer used, but is kept as a reference for langid -> string
|
|
mappings
|
|
|
|
const SLangIDStringMap g_rgLangMap[] =
|
|
{
|
|
{ 0x0401, _T("ar") },
|
|
{ 0x0402, _T("bg") },
|
|
{ 0x0403, _T("ca") },
|
|
{ 0x0404, _T("zhTW") },
|
|
{ 0x0405, _T("cs") },
|
|
{ 0x0406, _T("da") },
|
|
{ 0x0407, _T("de") },
|
|
{ 0x0408, _T("el") },
|
|
{ 0x0409, _T("en") },
|
|
{ 0x040b, _T("fi") },
|
|
{ 0x040c, _T("fr") },
|
|
{ 0x040d, _T("he") },
|
|
{ 0x040e, _T("hu") },
|
|
{ 0x0410, _T("it") },
|
|
{ 0x0411, _T("ja") },
|
|
{ 0x0412, _T("ko") },
|
|
{ 0x0413, _T("nl") },
|
|
{ 0x0414, _T("no") },
|
|
{ 0x0415, _T("pl") },
|
|
{ 0x0416, _T("ptBR") },
|
|
{ 0x0418, _T("ro") },
|
|
{ 0x0419, _T("ru") },
|
|
{ 0x041a, _T("hr") },
|
|
{ 0x041b, _T("sk") },
|
|
{ 0x041d, _T("sv") },
|
|
{ 0x041e, _T("en") },
|
|
{ 0x041f, _T("tr") },
|
|
{ 0x0424, _T("sl") },
|
|
{ 0x0425, _T("et") },
|
|
{ 0x0426, _T("lv") },
|
|
{ 0x0427, _T("lt") },
|
|
{ 0x042d, _T("eu") },
|
|
{ 0x0804, _T("zhCN") },
|
|
{ 0x080a, _T("es") },
|
|
{ 0x0816, _T("pt") },
|
|
{ 0x0c0a, _T("es") }
|
|
};
|
|
*/
|
|
|
|
|
|
// ******************************************************************************
|
|
BOOL MapLangIdToStringName(LANGID langid, LPCTSTR pszIdentFile,
|
|
LPTSTR pszLangString, DWORD cchLangString)
|
|
{
|
|
LOG_Block("MapLangIdToStringName");
|
|
|
|
TCHAR szLang[32];
|
|
DWORD cch;
|
|
LCID lcid;
|
|
BOOL fRet = FALSE;
|
|
|
|
lcid = MAKELCID(langid, SORT_DEFAULT);
|
|
|
|
fRet = LookupLocaleStringFromLCID(lcid, szLang, ARRAYSIZE(szLang));
|
|
if (fRet == FALSE)
|
|
{
|
|
LOG_ErrorMsg(GetLastError());
|
|
goto done;
|
|
}
|
|
|
|
// first try the whole string ("<lang>-<country>")
|
|
cch = GetPrivateProfileString(IDENT_LANG, szLang,
|
|
_T(""),
|
|
pszLangString, cchLangString,
|
|
pszIdentFile);
|
|
if (cch == cchLangString - 1)
|
|
{
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
LOG_ErrorMsg(ERROR_INSUFFICIENT_BUFFER);
|
|
goto done;
|
|
}
|
|
|
|
// if that fails, strip off the country code & just try the language
|
|
else if (cch == 0)
|
|
{
|
|
LPTSTR pszDash;
|
|
|
|
pszDash = StrChr(szLang, _T('-'));
|
|
if (pszDash != NULL)
|
|
{
|
|
*pszDash = _T('\0');
|
|
cch = GetPrivateProfileString(IDENT_LANG, szLang,
|
|
_T(""),
|
|
pszLangString, cchLangString,
|
|
pszIdentFile);
|
|
if (cch == cchLangString - 1)
|
|
{
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
LOG_ErrorMsg(ERROR_INSUFFICIENT_BUFFER);
|
|
goto done;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (cch > 0 && pszLangString[0] == _T('/'))
|
|
{
|
|
// want to use the full cch (& not cch - 1) because we want to copy the
|
|
// NULL terminator also...
|
|
MoveMemory(&pszLangString[0], &pszLangString[1], cch * sizeof(TCHAR));
|
|
}
|
|
|
|
fRet = TRUE;
|
|
|
|
done:
|
|
return fRet;
|
|
}
|
|
|
|
// ******************************************************************************
|
|
BOOL CALLBACK EnumUILangProc(LPTSTR szUILang, LONG_PTR lParam)
|
|
{
|
|
LOG_Block("EnumUILangProc");
|
|
|
|
AU_LANGLIST *paull = (AU_LANGLIST *)lParam;
|
|
AU_LANG *paulNew = NULL;
|
|
HRESULT hr;
|
|
LANGID langid;
|
|
LPTSTR pszStop;
|
|
TCHAR szAUName[32];
|
|
DWORD cchMuiName, cchAUName, cbNeed, cchAvail, dwLangID;
|
|
BOOL fRet = FALSE, fMap;
|
|
|
|
if (szUILang == NULL || lParam == NULL)
|
|
goto done;
|
|
|
|
langid = (LANGID)_tcstoul(szUILang, &pszStop, 16);
|
|
|
|
// if we don't have a mapping for a langid, then just skip the language
|
|
// and return success
|
|
szAUName[0] = _T('\0');
|
|
fMap = MapLangIdToStringName(langid, paull->pszIdentFile,
|
|
szAUName, ARRAYSIZE(szAUName));
|
|
if (fMap == FALSE || szAUName[0] == _T('\0'))
|
|
{
|
|
fRet = TRUE;
|
|
goto done;
|
|
}
|
|
|
|
if (paull->cLangs >= paull->cSlots)
|
|
{
|
|
AU_LANG **rgpaulNew = NULL;
|
|
DWORD cNewSlots = paull->cSlots * 2;
|
|
|
|
if (cNewSlots == 0)
|
|
cNewSlots = 32;
|
|
|
|
if (paull->rgpaulLangs != NULL)
|
|
{
|
|
rgpaulNew = (AU_LANG **)HeapReAlloc(GetProcessHeap(),
|
|
HEAP_ZERO_MEMORY,
|
|
paull->rgpaulLangs,
|
|
cNewSlots * sizeof(AU_LANG *));
|
|
}
|
|
else
|
|
{
|
|
rgpaulNew = (AU_LANG **)HeapAlloc(GetProcessHeap(),
|
|
HEAP_ZERO_MEMORY,
|
|
cNewSlots * sizeof(AU_LANG *));
|
|
}
|
|
if (rgpaulNew == NULL)
|
|
goto done;
|
|
|
|
paull->rgpaulLangs = rgpaulNew;
|
|
paull->cSlots = cNewSlots;
|
|
}
|
|
|
|
// we will be adding an '_' to the beginning of the AUName, so make sure
|
|
// the size we compute here reflects that.
|
|
cchAUName = lstrlen(szAUName) + 1;
|
|
cchMuiName = lstrlen(szUILang);
|
|
|
|
// alloc a buffer to hold the AU_LANG struct plus the two strings (and
|
|
// don't forget the NULL terminators!).
|
|
// The layout of the buffer is as follows:
|
|
// <AU_LANG>
|
|
// <szMuiName>
|
|
// _<szAUName>
|
|
// NOTE: if this buffer format ever change, gotta make sure that the
|
|
// contents are aligned properly (otherwise, we'll fault on ia64)
|
|
cbNeed = sizeof(AU_LANG);
|
|
cbNeed += ((cchMuiName + cchAUName + 2) * sizeof(TCHAR));
|
|
paulNew = (AU_LANG *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbNeed);
|
|
if (paulNew == NULL)
|
|
goto done;
|
|
|
|
paulNew->szMuiName = (LPTSTR)((PBYTE)paulNew + sizeof(AU_LANG));
|
|
paulNew->szAUName = paulNew->szMuiName + cchMuiName + 1;
|
|
|
|
// this should never truncate the buffers cuz we calc'ed the size above and
|
|
// allocated a buffer exactly long enuf to hold all of this
|
|
cchAvail = (cbNeed - sizeof(AU_LANG)) / sizeof(TCHAR);
|
|
hr = StringCchCopyEx(paulNew->szMuiName, cchAvail, szUILang,
|
|
NULL, NULL, MISTSAFE_STRING_FLAGS);
|
|
if (FAILED(hr))
|
|
goto done;
|
|
|
|
cchAvail -= (cchMuiName + 1);
|
|
|
|
// need to put an '_' in front of the AU name, so add it to the buffer and
|
|
// reduce the available size by one. Also make sure to start copying the
|
|
// AUName *after* the '_' character.
|
|
paulNew->szAUName[0] = _T('_');
|
|
cchAvail--;
|
|
|
|
hr = StringCchCopyEx(&paulNew->szAUName[1], cchAvail, szAUName,
|
|
NULL, NULL, MISTSAFE_STRING_FLAGS);
|
|
if (FAILED(hr))
|
|
goto done;
|
|
|
|
paull->rgpaulLangs[paull->cLangs++] = paulNew;
|
|
paulNew = NULL;
|
|
|
|
fRet = TRUE;
|
|
|
|
done:
|
|
if (paulNew != NULL)
|
|
HeapFree(GetProcessHeap(), 0, paulNew);
|
|
|
|
return fRet;
|
|
}
|
|
|
|
// ******************************************************************************
|
|
HRESULT GetMuiLangList(AU_LANGLIST *paull,
|
|
LPTSTR pszMuiDir, DWORD *pcchMuiDir,
|
|
LPTSTR pszHelpMuiDir, DWORD *pcchHelpMuiDir)
|
|
{
|
|
LOG_Block("GetMuiLangList");
|
|
|
|
HRESULT hr = NOERROR;
|
|
DWORD cMuiLangs;
|
|
int iLang;
|
|
|
|
paull->cLangs = 0;
|
|
paull->cSlots = 0;
|
|
paull->rgpaulLangs = NULL;
|
|
|
|
if (EnumUILanguages(EnumUILangProc, 0, (LONG_PTR)paull) == FALSE)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
goto done;
|
|
}
|
|
|
|
// We need to deal with MUI stuff only if we've have more than 0 languages
|
|
// to worry about
|
|
if (paull->cLangs > 0)
|
|
{
|
|
LPTSTR pszHelp = NULL, pszMui = NULL;
|
|
size_t cchAvail, cchAvailHelp;
|
|
TCHAR szPath[MAX_PATH + 1];
|
|
DWORD dwAttrib;
|
|
DWORD cch, cLangs = (int)paull->cLangs;
|
|
BOOL fDeleteLang;
|
|
|
|
// need to get the directory we'll stuff MUI updates into
|
|
cch = GetSystemWindowsDirectory(pszMuiDir, *pcchMuiDir);
|
|
|
|
// note 2nd compare takes into account the addition of an extra '\\' after
|
|
// the system windows dir
|
|
if (cch == 0 || cch >= *pcchMuiDir)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
goto done;
|
|
}
|
|
|
|
// tack on an extra '\\' if necessary
|
|
if (pszMuiDir[cch - 1] != _T('\\'))
|
|
{
|
|
pszMuiDir[cch++] = _T('\\');
|
|
pszMuiDir[cch] = _T('\0');
|
|
}
|
|
|
|
hr = StringCchCopyEx(pszHelpMuiDir, *pcchHelpMuiDir, pszMuiDir,
|
|
NULL, NULL, MISTSAFE_STRING_FLAGS);
|
|
if (FAILED(hr))
|
|
goto done;
|
|
|
|
hr = StringCchCatEx(pszHelpMuiDir, *pcchHelpMuiDir, MUI_HELPSUBDIR,
|
|
&pszHelp, &cchAvailHelp, MISTSAFE_STRING_FLAGS);
|
|
if (FAILED(hr))
|
|
goto done;
|
|
|
|
hr = StringCchCatEx(pszMuiDir, *pcchMuiDir, MUI_SUBDIR,
|
|
&pszMui, &cchAvail, MISTSAFE_STRING_FLAGS);
|
|
if (FAILED(hr))
|
|
goto done;
|
|
|
|
*pcchMuiDir -= cchAvail;
|
|
*pcchHelpMuiDir -= cchAvailHelp;
|
|
|
|
// check and make sure that the MUI directories exist- remove all those that
|
|
// do not. This section also checks to make sure that the buffer passed in
|
|
// is large enuf to hold the language
|
|
for(iLang = (int)(cLangs - 1); iLang >= 0; iLang--)
|
|
{
|
|
fDeleteLang = FALSE;
|
|
|
|
hr = StringCchCopyEx(pszMui, cchAvail,
|
|
paull->rgpaulLangs[iLang]->szMuiName,
|
|
NULL, NULL, MISTSAFE_STRING_FLAGS);
|
|
if (FAILED(hr))
|
|
goto done;
|
|
|
|
dwAttrib = GetFileAttributes(pszMuiDir);
|
|
if (dwAttrib == INVALID_FILE_ATTRIBUTES ||
|
|
(dwAttrib & FILE_ATTRIBUTE_DIRECTORY) == 0)
|
|
{
|
|
fDeleteLang = TRUE;
|
|
}
|
|
else
|
|
{
|
|
hr = StringCchCopyEx(pszHelp, cchAvailHelp,
|
|
paull->rgpaulLangs[iLang]->szMuiName,
|
|
NULL, NULL, MISTSAFE_STRING_FLAGS);
|
|
if (FAILED(hr))
|
|
goto done;
|
|
|
|
dwAttrib = GetFileAttributes(pszHelpMuiDir);
|
|
if (dwAttrib == INVALID_FILE_ATTRIBUTES ||
|
|
(dwAttrib & FILE_ATTRIBUTE_DIRECTORY) == 0)
|
|
{
|
|
fDeleteLang = TRUE;
|
|
}
|
|
}
|
|
|
|
if (fDeleteLang)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, paull->rgpaulLangs[iLang]);
|
|
if (iLang != paull->cLangs - 1)
|
|
{
|
|
MoveMemory(&paull->rgpaulLangs[iLang],
|
|
&paull->rgpaulLangs[iLang + 1],
|
|
(paull->cLangs - iLang - 1) * sizeof(AU_LANG *));
|
|
}
|
|
paull->rgpaulLangs[--paull->cLangs] = NULL;
|
|
}
|
|
}
|
|
|
|
pszMuiDir[*pcchMuiDir] = _T('\0');
|
|
pszHelpMuiDir[*pcchHelpMuiDir] = _T('\0');
|
|
|
|
}
|
|
|
|
done:
|
|
if (FAILED(hr))
|
|
CleanupMuiLangList(paull);
|
|
|
|
return hr;
|
|
}
|
|
|
|
// ******************************************************************************
|
|
HRESULT CleanupMuiLangList(AU_LANGLIST *paull)
|
|
{
|
|
LOG_Block("CleanupMuiLangList");
|
|
|
|
HRESULT hr = S_OK;
|
|
DWORD i;
|
|
|
|
// if it's NULL, just return success
|
|
if (paull == NULL)
|
|
return hr;
|
|
|
|
if (paull->rgpaulLangs == NULL)
|
|
goto done;
|
|
|
|
for (i = 0; i < paull->cLangs; i++)
|
|
{
|
|
if (paull->rgpaulLangs[i] != NULL)
|
|
HeapFree(GetProcessHeap(), 0, paull->rgpaulLangs[i]);
|
|
}
|
|
|
|
HeapFree(GetProcessHeap(), 0, paull->rgpaulLangs);
|
|
|
|
done:
|
|
ZeroMemory(paull, sizeof(AU_LANGLIST));
|
|
return hr;
|
|
}
|
|
|