339 lines
11 KiB
C++
339 lines
11 KiB
C++
#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;
|
|
}
|
|
|