|
|
#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\]<dllname>,-<strId>
the string with id <strId> from <dllname> 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; }
|