windows-nt/Source/XPSP1/NT/shell/shlwapi/regw.cpp
2020-09-26 16:20:57 +08:00

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;
}