/****************************************************************************** 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 ("-") 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: // // // _ // 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; }