#include "priv.h" #include "unicwrap.h" /*****************************************************************************\ FUNCTION: SHLoadRegUIString DESCRIPTION: loads the data from the value given the hkey and pszValue. if the data is of the form: @[path\],- the string with id from will be loaded. if not explicit path is provided then the dll will be chosen according to pluggable UI specifications, if possible. if the value's data doesn't yield a successful string load, then the data itself is returned NOTE: These strings are always loaded with cross codepage support. WARNING: This function can end up calling LoadLibrary and FreeLibrary. Therefore, you must not call SHLoadRegUIString during process attach or process detach. PARAMETERS: hkey - hkey of where to look for pszValue pszValue - value with text string or indirector (see above) to use pszOutBuf - buffer in which to return the data or indirected string cchOutBuf - size of pszOutBuf \*****************************************************************************/ LANGID GetNormalizedLangId(DWORD dwFlag); STDAPI SHLoadRegUIStringW(HKEY hkey, LPCWSTR pszValue, LPWSTR pszOutBuf, UINT cchOutBuf) { HRESULT hr; RIP(hkey != NULL); RIP(hkey != INVALID_HANDLE_VALUE); RIP(NULL == pszValue || IS_VALID_STRING_PTRW(pszValue, -1)); RIP(IS_VALID_WRITE_BUFFER(pszOutBuf, WCHAR, cchOutBuf)); DEBUGWhackPathBufferW(pszOutBuf, cchOutBuf); // Lots of people (regfldr.cpp, for example) // assume they'll get back an empty string on failure, // so let's give the public what it wants if (cchOutBuf) pszOutBuf[0] = 0; hr = E_INVALIDARG; if (hkey != INVALID_HANDLE_VALUE && hkey != NULL && pszOutBuf != NULL) { DWORD cb; DWORD dwRet; WCHAR * pszValueDataBuf; hr = E_FAIL; // first try to get the indirected text which will // point to a string id in a dll somewhere... this // allows plugUI enabled registry UI strings pszValueDataBuf = pszOutBuf; cb = CbFromCchW(cchOutBuf); dwRet = SHQueryValueExW(hkey, pszValue, NULL, NULL, (LPBYTE)pszValueDataBuf, &cb); if (dwRet == ERROR_SUCCESS || dwRet == ERROR_MORE_DATA) { BOOL fAlloc; fAlloc = (dwRet == ERROR_MORE_DATA); // if we didn't have space, this is where we correct the problem. // we create a buffer big enough, load the data, and leave // ourselves with pszValueDataBuf pointing at a valid buffer // containing valid data, exactly what we hoped for in the // SHQueryValueExW above if (fAlloc) { pszValueDataBuf = new WCHAR[(cb+1)/2]; if (pszValueDataBuf != NULL) { // try to load again... overwriting dwRet on purpose // because we only need to know whether we successfully filled // the buffer at some point (whether then or now) dwRet = SHQueryValueExW(hkey, pszValue, NULL, NULL, (LPBYTE)pszValueDataBuf, &cb); } else { hr = E_OUTOFMEMORY; } } // proceed if we succesfully loaded something via one of the // two SHQueryValueExW calls. // we should have the data we want in a buffer pointed // to by pszValueDataBuf. if (dwRet == ERROR_SUCCESS) { hr = SHLoadIndirectString(pszValueDataBuf, pszOutBuf, cchOutBuf, NULL); } if (fAlloc && pszValueDataBuf != NULL) { delete [] pszValueDataBuf; } } } return hr; } STDAPI SHLoadRegUIStringA(HKEY hkey, LPCSTR pszValue, LPSTR pszOutBuf, UINT cchOutBuf) { HRESULT hr; RIP(hkey != NULL); RIP(hkey != INVALID_HANDLE_VALUE); RIP(IS_VALID_STRING_PTRA(pszValue, -1)); RIP(IS_VALID_WRITE_BUFFER(pszOutBuf, char, cchOutBuf)); CStrInW strV(pszValue); CStrOutW strOut(pszOutBuf, cchOutBuf); hr = SHLoadRegUIStringW(hkey, strV, strOut, strOut.BufSize()); return hr; } HRESULT _LoadDllString(LPCWSTR pszSource, LPWSTR pszOutBuf, UINT cchOutBuf) { HRESULT hr = E_FAIL; WCHAR * szParseBuf; int nStrId; UINT cchSource = lstrlenW(pszSource)+1; szParseBuf = new WCHAR[cchSource]; if (szParseBuf != NULL) { StrCpyW(szParseBuf, pszSource); // see if this is a special string reference. // such strings take the form [path\]dllname.dll,-123 // where 123 is the id of the string resource // note that reference by index is not permitted nStrId = PathParseIconLocationW(szParseBuf); nStrId *= -1; if (nStrId > 0) { LPWSTR pszDllName; HINSTANCE hinst; BOOL fUsedMLLoadLibrary = FALSE; pszDllName = PathFindFileNameW(szParseBuf); ASSERT(pszDllName >= szParseBuf); // try loading the dll with MLLoadLibrary, but // only if an explicit path was not provided. // we assume an explicit path means that // the caller knows precisely which dll is needed // use MLLoadLibrary first, otherwise we'll miss // out chance to have plugUI behavior hinst = NULL; if (pszDllName == szParseBuf) { if (StrStrI(pszDllName, L"LC.DLL")) { // note: using HINST_THISDLL (below) is sort of a hack because that's // techinically supposed to be the *parent* dll's hinstance... // however we get called from lots of places and therefore // don't know the parent dll, and the hinst for browseui.dll // is good enough since all the hinst is really used for is to // find the path to check if the install language is the // currently selected UI language. this will usually be // something like "\winnt\system32" hinst = MLLoadLibraryW(pszDllName, HINST_THISDLL, ML_CROSSCODEPAGE); fUsedMLLoadLibrary = (hinst != NULL); } else hinst = LoadLibraryExWrapW(pszDllName, NULL, LOAD_LIBRARY_AS_DATAFILE); } if (!hinst) { // our last chance to load something is if a full // path was provided... if there's a full path it // will start at the beginning of the szParseBuf buffer if (pszDllName > szParseBuf) { // don't bother if the file isn't there // failling in LoadLibrary is slow if (PathFileExistsW(szParseBuf)) { hinst = LoadLibraryExWrapW(szParseBuf, NULL, LOAD_LIBRARY_AS_DATAFILE); } } } if (hinst) { // dll found, so load the string if (LoadStringWrapW(hinst, nStrId, pszOutBuf, cchOutBuf)) { hr = S_OK; } else { TraceMsg(TF_WARNING, "SHLoadRegUIString(): Failure loading string %d from module %ws for valid load request %ws.", nStrId, szParseBuf, pszSource); } if (fUsedMLLoadLibrary) { MLFreeLibrary(hinst); } else { FreeLibrary(hinst); } } } delete [] szParseBuf; } else { hr = E_OUTOFMEMORY; } return hr; } inline BOOL _CanCacheMUI() { if (!g_bRunningOnNT) return (GetNormalizedLangId(ML_CROSSCODEPAGE_NT) == MLGetUILanguage()); return TRUE; } // Note: pszSource and pszOutBuf may be the same buffer LWSTDAPI SHLoadIndirectString(LPCWSTR pszSource, LPWSTR pszOutBuf, UINT cchOutBuf, void **ppvReserved) { HRESULT hr = E_FAIL; RIP(IS_VALID_WRITE_BUFFER(pszOutBuf, WCHAR, cchOutBuf)); RIP(!ppvReserved); if (pszSource[0] == L'@') // "@dllname,-id" or "@dllname,-id?lid,string" { LPWSTR pszResource = StrDupW(pszSource); if (pszResource) { LANGID lidUI =0; // the LidString is there to support our old caching model. // the new caching model doesnt require any work for the caller LPWSTR pszLidString = StrChrW(pszResource+1, L'?'); DWORD cchResource = lstrlen(pszResource); // used to use '@' as the second delimiter as well. // but it has collisions with filesystem paths. if (!pszLidString) pszLidString = StrChrW(pszResource+1, L'@'); if (pszLidString) { cchResource = (DWORD)(pszLidString - pszResource); // NULL terminate the dll,id just in case we need to actually load pszResource[cchResource] = 0; } DWORD cb = CbFromCchW(cchOutBuf); hr = SKGetValue(SHELLKEY_HKCULM_MUICACHE, NULL, pszResource, NULL, pszOutBuf, &cb); if (FAILED(hr)) { WCHAR wszDllId[MAX_PATH + 1 + 6]; // path + comma + -65536 SHExpandEnvironmentStringsW(pszResource+1, wszDllId, ARRAYSIZE(wszDllId)); hr = _LoadDllString(wszDllId, pszOutBuf, cchOutBuf); // Might as well write the new string out so we don't have to load the DLL next time through // but we don't write cross codepage string on Win9x if (SUCCEEDED(hr) && _CanCacheMUI()) { SKSetValue(SHELLKEY_HKCULM_MUICACHE, NULL, pszResource, REG_SZ, pszOutBuf, CbFromCchW(lstrlenW(pszOutBuf)+1)); } } LocalFree(pszResource); } else hr = E_OUTOFMEMORY; if (FAILED(hr)) { if (cchOutBuf) pszOutBuf[0] = L'\0'; // can't hand out an "@shell32.dll,-525" string } } else { if (pszOutBuf != pszSource) StrCpyN(pszOutBuf, pszSource, cchOutBuf); hr = S_OK; } return hr; }