Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986/* * PROJECT: ReactOS Shell * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later) * PURPOSE: Implement shell property bags * COPYRIGHT: Copyright 2023 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com> */
#define _ATL_NO_EXCEPTIONS#include "precomp.h"#include <shlwapi.h>#include <shlwapi_undoc.h>#include <shlobj_undoc.h>#include <shlguid_undoc.h>#include <atlstr.h> // for CStringW#include <atlsimpcoll.h> // for CSimpleMap#include <atlcomcli.h> // for CComVariant#include <atlconv.h> // for CA2W and CW2A#include <strsafe.h> // for StringC... functions
WINE_DEFAULT_DEBUG_CHANNEL(shell);
#define MODE_CAN_READ(dwMode) \ (((dwMode) & (STGM_READ | STGM_WRITE | STGM_READWRITE)) != STGM_WRITE)#define MODE_CAN_WRITE(dwMode) \ (((dwMode) & (STGM_READ | STGM_WRITE | STGM_READWRITE)) != STGM_READ)
class CBasePropertyBag : public IPropertyBag#if (_WIN32_WINNT < _WIN32_WINNT_VISTA) , public IPropertyBag2#endif{protected: LONG m_cRefs; // reference count DWORD m_dwMode; // STGM_* flags
public: CBasePropertyBag(DWORD dwMode) : m_cRefs(0) , m_dwMode(dwMode) { }
virtual ~CBasePropertyBag() { }
// IUnknown interface STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject) override { if (!ppvObject) return E_POINTER;
#if (_WIN32_WINNT < _WIN32_WINNT_VISTA) if (::IsEqualGUID(riid, IID_IPropertyBag2)) { AddRef(); *ppvObject = static_cast<IPropertyBag2*>(this); return S_OK; }#endif if (::IsEqualGUID(riid, IID_IUnknown) || ::IsEqualGUID(riid, IID_IPropertyBag)) { AddRef(); *ppvObject = static_cast<IPropertyBag*>(this); return S_OK; }
ERR("%p: %s: E_NOINTERFACE\n", this, debugstr_guid(&riid)); return E_NOINTERFACE; } STDMETHODIMP_(ULONG) AddRef() override { return ::InterlockedIncrement(&m_cRefs); } STDMETHODIMP_(ULONG) Release() override { if (::InterlockedDecrement(&m_cRefs) == 0) { delete this; return 0; } return m_cRefs; }
#if (_WIN32_WINNT < _WIN32_WINNT_VISTA) // IPropertyBag2 interface (stubs) STDMETHODIMP Read( _In_ ULONG cProperties, _In_ PROPBAG2 *pPropBag, _In_opt_ IErrorLog *pErrorLog, _Out_ VARIANT *pvarValue, _Out_ HRESULT *phrError) override { return E_NOTIMPL; } STDMETHODIMP Write( _In_ ULONG cProperties, _In_ PROPBAG2 *pPropBag, _In_ VARIANT *pvarValue) { return E_NOTIMPL; } STDMETHODIMP CountProperties(_Out_ ULONG *pcProperties) override { return E_NOTIMPL; } STDMETHODIMP GetPropertyInfo( _In_ ULONG iProperty, _In_ ULONG cProperties, _Out_ PROPBAG2 *pPropBag, _Out_ ULONG *pcProperties) override { return E_NOTIMPL; } STDMETHODIMP LoadObject( _In_z_ LPCWSTR pstrName, _In_ DWORD dwHint, _In_ IUnknown *pUnkObject, _In_opt_ IErrorLog *pErrorLog) override { return E_NOTIMPL; }#endif};
struct CPropMapEqual{ static bool IsEqualKey(const ATL::CStringW& k1, const ATL::CStringW& k2) { return k1.CompareNoCase(k2) == 0; }
static bool IsEqualValue(const ATL::CComVariant& v1, const ATL::CComVariant& v2) { return false; }};
class CMemPropertyBag : public CBasePropertyBag{protected: ATL::CSimpleMap<ATL::CStringW, ATL::CComVariant, CPropMapEqual> m_PropMap;
public: CMemPropertyBag(DWORD dwMode) : CBasePropertyBag(dwMode) { }
STDMETHODIMP Read(_In_z_ LPCWSTR pszPropName, _Inout_ VARIANT *pvari, _Inout_opt_ IErrorLog *pErrorLog) override; STDMETHODIMP Write(_In_z_ LPCWSTR pszPropName, _In_ VARIANT *pvari) override;};
STDMETHODIMPCMemPropertyBag::Read( _In_z_ LPCWSTR pszPropName, _Inout_ VARIANT *pvari, _Inout_opt_ IErrorLog *pErrorLog){ UNREFERENCED_PARAMETER(pErrorLog);
TRACE("%p: %s %p %p\n", this, debugstr_w(pszPropName), pvari, pErrorLog);
VARTYPE vt = V_VT(pvari);
::VariantInit(pvari);
#if (_WIN32_WINNT < _WIN32_WINNT_VISTA) if (!MODE_CAN_READ(m_dwMode)) { ERR("%p: 0x%X\n", this, m_dwMode); return E_ACCESSDENIED; }#endif
if (!pszPropName || !pvari) { ERR("%p: %s %p %p\n", this, debugstr_w(pszPropName), pvari, pErrorLog); return E_INVALIDARG; }
INT iItem = m_PropMap.FindKey(pszPropName); if (iItem == -1) { ERR("%p: %s %p %p\n", this, debugstr_w(pszPropName), pvari, pErrorLog); return E_FAIL; }
HRESULT hr = ::VariantCopy(pvari, &m_PropMap.GetValueAt(iItem)); if (FAILED(hr)) { ERR("%p: 0x%08X %p\n", this, hr, pvari); return hr; }
hr = ::VariantChangeTypeForRead(pvari, vt); if (FAILED(hr)) { ERR("%p: 0x%08X %p\n", this, hr, pvari); return hr; }
return hr;}
STDMETHODIMPCMemPropertyBag::Write( _In_z_ LPCWSTR pszPropName, _In_ VARIANT *pvari){ TRACE("%p: %s %p\n", this, debugstr_w(pszPropName), pvari);
#if (_WIN32_WINNT < _WIN32_WINNT_VISTA) if (!MODE_CAN_WRITE(m_dwMode)) { ERR("%p: 0x%X\n", this, m_dwMode); return E_ACCESSDENIED; }#endif
if (!pszPropName || !pvari) { ERR("%p: %s %p\n", this, debugstr_w(pszPropName), pvari); return E_INVALIDARG; }
ATL::CComVariant vari; HRESULT hr = vari.Copy(pvari); if (FAILED(hr)) { ERR("%p: %s %p: 0x%08X\n", this, debugstr_w(pszPropName), pvari, hr); return hr; }
if (!m_PropMap.SetAt(pszPropName, vari)) { ERR("%p: %s %p\n", this, debugstr_w(pszPropName), pvari); return E_FAIL; }
return hr;}
/************************************************************************** * SHCreatePropertyBagOnMemory (SHLWAPI.477) * * Creates a property bag object on memory. * * @param dwMode Specifies either STGM_READ, STGM_WRITE or STGM_READWRITE. Ignored on Vista+. * @param riid Specifies either IID_IUnknown, IID_IPropertyBag or IID_IPropertyBag2. * Vista+ rejects IID_IPropertyBag2. * @param ppvObj Receives an IPropertyBag pointer. * @return An HRESULT value. S_OK on success, non-zero on failure. * @see http://undoc.airesoft.co.uk/shlwapi.dll/SHCreatePropertyBagOnMemory.php */EXTERN_C HRESULT WINAPISHCreatePropertyBagOnMemory(_In_ DWORD dwMode, _In_ REFIID riid, _Out_ void **ppvObj){ TRACE("0x%08X, %s, %p\n", dwMode, debugstr_guid(&riid), ppvObj);
*ppvObj = NULL;
CComPtr<CMemPropertyBag> pMemBag(new CMemPropertyBag(dwMode));
return pMemBag->QueryInterface(riid, ppvObj);}
class CRegPropertyBag : public CBasePropertyBag{protected: HKEY m_hKey;
HRESULT _ReadDword(LPCWSTR pszPropName, VARIANT *pvari); HRESULT _ReadString(LPCWSTR pszPropName, VARIANTARG *pvarg, UINT len); HRESULT _ReadBinary(LPCWSTR pszPropName, VARIANT *pvari, VARTYPE vt, DWORD uBytes); HRESULT _ReadStream(VARIANT *pvari, BYTE *pInit, UINT cbInit); HRESULT _CopyStreamIntoBuff(IStream *pStream, void *pv, ULONG cb); HRESULT _GetStreamSize(IStream *pStream, LPDWORD pcbSize); HRESULT _WriteStream(LPCWSTR pszPropName, IStream *pStream);
public: CRegPropertyBag(DWORD dwMode) : CBasePropertyBag(dwMode) , m_hKey(NULL) { }
~CRegPropertyBag() override { if (m_hKey) ::RegCloseKey(m_hKey); }
HRESULT Init(HKEY hKey, LPCWSTR lpSubKey);
STDMETHODIMP Read(_In_z_ LPCWSTR pszPropName, _Inout_ VARIANT *pvari, _Inout_opt_ IErrorLog *pErrorLog) override; STDMETHODIMP Write(_In_z_ LPCWSTR pszPropName, _In_ VARIANT *pvari) override;};
HRESULT CRegPropertyBag::Init(HKEY hKey, LPCWSTR lpSubKey){ REGSAM nAccess = 0; if (MODE_CAN_READ(m_dwMode)) nAccess |= KEY_READ; if (MODE_CAN_WRITE(m_dwMode)) nAccess |= KEY_WRITE;
LONG error; if (m_dwMode & STGM_CREATE) error = ::RegCreateKeyExW(hKey, lpSubKey, 0, NULL, 0, nAccess, NULL, &m_hKey, NULL); else error = ::RegOpenKeyExW(hKey, lpSubKey, 0, nAccess, &m_hKey);
if (error != ERROR_SUCCESS) { ERR("%p %s 0x%08X\n", hKey, debugstr_w(lpSubKey), error); return HRESULT_FROM_WIN32(error); }
return S_OK;}
HRESULT CRegPropertyBag::_ReadDword(LPCWSTR pszPropName, VARIANT *pvari){ DWORD cbData = sizeof(DWORD); LONG error = SHGetValueW(m_hKey, NULL, pszPropName, NULL, &V_UI4(pvari), &cbData); if (error) return E_FAIL;
V_VT(pvari) = VT_UI4; return S_OK;}
HRESULT CRegPropertyBag::_ReadString(LPCWSTR pszPropName, VARIANTARG *pvarg, UINT len){ BSTR bstr = ::SysAllocStringByteLen(NULL, len); V_BSTR(pvarg) = bstr; if (!bstr) return E_OUTOFMEMORY;
V_VT(pvarg) = VT_BSTR; LONG error = SHGetValueW(m_hKey, NULL, pszPropName, NULL, bstr, (LPDWORD)&len); if (error) { ::VariantClear(pvarg); return E_FAIL; }
return S_OK;}
HRESULT CRegPropertyBag::_ReadStream(VARIANT *pvari, BYTE *pInit, UINT cbInit){ IStream *pStream = SHCreateMemStream(pInit, cbInit); V_UNKNOWN(pvari) = pStream; if (!pStream) return E_OUTOFMEMORY; V_VT(pvari) = VT_UNKNOWN; return S_OK;}
HRESULTCRegPropertyBag::_ReadBinary( LPCWSTR pszPropName, VARIANT *pvari, VARTYPE vt, DWORD uBytes){ HRESULT hr = E_FAIL; if (vt != VT_UNKNOWN || uBytes < sizeof(GUID)) return hr;
LPBYTE pbData = (LPBYTE)::LocalAlloc(LMEM_ZEROINIT, uBytes); if (!pbData) return hr;
if (!SHGetValueW(m_hKey, NULL, pszPropName, NULL, pbData, &uBytes) && memcmp(&GUID_NULL, pbData, sizeof(GUID)) == 0) { hr = _ReadStream(pvari, pbData + sizeof(GUID), uBytes - sizeof(GUID)); }
::LocalFree(pbData);
return hr;}
HRESULT CRegPropertyBag::_CopyStreamIntoBuff(IStream *pStream, void *pv, ULONG cb){ LARGE_INTEGER li; li.QuadPart = 0; HRESULT hr = pStream->Seek(li, 0, NULL); if (FAILED(hr)) return hr; return pStream->Read(pv, cb, NULL);}
HRESULT CRegPropertyBag::_GetStreamSize(IStream *pStream, LPDWORD pcbSize){ *pcbSize = 0;
ULARGE_INTEGER ui; HRESULT hr = IStream_Size(pStream, &ui); if (FAILED(hr)) return hr;
if (ui.DUMMYSTRUCTNAME.HighPart) return E_FAIL; /* 64-bit value is not supported */
*pcbSize = ui.DUMMYSTRUCTNAME.LowPart; return hr;}
STDMETHODIMPCRegPropertyBag::Read( _In_z_ LPCWSTR pszPropName, _Inout_ VARIANT *pvari, _Inout_opt_ IErrorLog *pErrorLog){ UNREFERENCED_PARAMETER(pErrorLog);
TRACE("%p: %s %p %p\n", this, debugstr_w(pszPropName), pvari, pErrorLog);
if (!MODE_CAN_READ(m_dwMode)) { ERR("%p: 0x%X\n", this, m_dwMode); ::VariantInit(pvari); return E_ACCESSDENIED; }
VARTYPE vt = V_VT(pvari); VariantInit(pvari);
HRESULT hr; DWORD dwType, cbValue; LONG error = SHGetValueW(m_hKey, NULL, pszPropName, &dwType, NULL, &cbValue); if (error != ERROR_SUCCESS) hr = E_FAIL; else if (dwType == REG_SZ) hr = _ReadString(pszPropName, pvari, cbValue); else if (dwType == REG_BINARY) hr = _ReadBinary(pszPropName, pvari, vt, cbValue); else if (dwType == REG_DWORD) hr = _ReadDword(pszPropName, pvari); else hr = E_FAIL;
if (FAILED(hr)) { ERR("%p: 0x%08X %ld: %s %p\n", this, hr, dwType, debugstr_w(pszPropName), pvari); ::VariantInit(pvari); return hr; }
hr = ::VariantChangeTypeForRead(pvari, vt); if (FAILED(hr)) { ERR("%p: 0x%08X %ld: %s %p\n", this, hr, dwType, debugstr_w(pszPropName), pvari); ::VariantInit(pvari); }
return hr;}
HRESULTCRegPropertyBag::_WriteStream(LPCWSTR pszPropName, IStream *pStream){ DWORD cbData; HRESULT hr = _GetStreamSize(pStream, &cbData); if (FAILED(hr) || !cbData) return hr;
DWORD cbBinary = cbData + sizeof(GUID); LPBYTE pbBinary = (LPBYTE)::LocalAlloc(LMEM_ZEROINIT, cbBinary); if (!pbBinary) return E_OUTOFMEMORY;
hr = _CopyStreamIntoBuff(pStream, pbBinary + sizeof(GUID), cbData); if (SUCCEEDED(hr)) { if (SHSetValueW(m_hKey, NULL, pszPropName, REG_BINARY, pbBinary, cbBinary)) hr = E_FAIL; }
::LocalFree(pbBinary); return hr;}
STDMETHODIMPCRegPropertyBag::Write(_In_z_ LPCWSTR pszPropName, _In_ VARIANT *pvari){ TRACE("%p: %s %p\n", this, debugstr_w(pszPropName), pvari);
if (!MODE_CAN_WRITE(m_dwMode)) { ERR("%p: 0x%X\n", this, m_dwMode); return E_ACCESSDENIED; }
HRESULT hr; LONG error; VARIANTARG vargTemp = { 0 }; switch (V_VT(pvari)) { case VT_EMPTY: SHDeleteValueW(m_hKey, NULL, pszPropName); hr = S_OK; break;
case VT_BOOL: case VT_I1: case VT_I2: case VT_I4: case VT_UI1: case VT_UI2: case VT_UI4: case VT_INT: case VT_UINT: { hr = ::VariantChangeType(&vargTemp, pvari, 0, VT_UI4); if (FAILED(hr)) return hr;
error = SHSetValueW(m_hKey, NULL, pszPropName, REG_DWORD, &V_UI4(&vargTemp), sizeof(DWORD)); if (error) hr = E_FAIL;
::VariantClear(&vargTemp); break; }
case VT_UNKNOWN: { CComPtr<IStream> pStream; hr = V_UNKNOWN(pvari)->QueryInterface(IID_IStream, (void **)&pStream); if (FAILED(hr)) return hr;
hr = _WriteStream(pszPropName, pStream); break; }
default: { hr = ::VariantChangeType(&vargTemp, pvari, 0, VT_BSTR); if (FAILED(hr)) return hr;
int cch = lstrlenW(V_BSTR(&vargTemp)); DWORD cb = (cch + 1) * sizeof(WCHAR); error = SHSetValueW(m_hKey, NULL, pszPropName, REG_SZ, V_BSTR(&vargTemp), cb); if (error) hr = E_FAIL;
::VariantClear(&vargTemp); break; } }
return hr;}
/************************************************************************** * SHCreatePropertyBagOnRegKey (SHLWAPI.471) * * Creates a property bag object on registry key. * * @param hKey The registry key. * @param pszSubKey The path of the sub-key. * @param dwMode The combination of STGM_READ, STGM_WRITE, STGM_READWRITE, and STGM_CREATE. * @param riid Specifies either IID_IUnknown, IID_IPropertyBag or IID_IPropertyBag2. * @param ppvObj Receives an IPropertyBag pointer. * @return An HRESULT value. S_OK on success, non-zero on failure. * @see https://source.winehq.org/WineAPI/SHCreatePropertyBagOnRegKey.html */EXTERN_C HRESULT WINAPISHCreatePropertyBagOnRegKey( _In_ HKEY hKey, _In_z_ LPCWSTR pszSubKey, _In_ DWORD dwMode, _In_ REFIID riid, _Out_ void **ppvObj){ TRACE("%p, %s, 0x%08X, %s, %p\n", hKey, debugstr_w(pszSubKey), dwMode, debugstr_guid(&riid), ppvObj);
*ppvObj = NULL;
CComPtr<CRegPropertyBag> pRegBag(new CRegPropertyBag(dwMode));
HRESULT hr = pRegBag->Init(hKey, pszSubKey); if (FAILED(hr)) return hr;
return pRegBag->QueryInterface(riid, ppvObj);}
/************************************************************************** * SHGetIniStringW (SHLWAPI.294) * * @see https://source.winehq.org/WineAPI/SHGetIniStringW.html */EXTERN_C DWORD WINAPISHGetIniStringW( _In_z_ LPCWSTR appName, _In_z_ LPCWSTR keyName, _Out_writes_to_(outLen, return + 1) LPWSTR out, _In_ DWORD outLen, _In_z_ LPCWSTR filename){ TRACE("(%s,%s,%p,%08x,%s)\n", debugstr_w(appName), debugstr_w(keyName), out, outLen, debugstr_w(filename));
if (outLen == 0) return 0;
// Try ".W"-appended section name. See also SHSetIniStringW CStringW szSection(appName); szSection += L".W"; CStringW pszWideBuff; const INT cchWideMax = 4 * MAX_PATH; // UTF-7 needs 4 times length buffer. GetPrivateProfileStringW(szSection, keyName, NULL, pszWideBuff.GetBuffer(cchWideMax), cchWideMax, filename); pszWideBuff.ReleaseBuffer();
if (pszWideBuff.IsEmpty()) // It's empty or not found { // Try the normal section name return GetPrivateProfileStringW(appName, keyName, NULL, out, outLen, filename); }
// Okay, now ".W" version is valid. Its value is a UTF-7 string in UTF-16 CW2A wide2utf7(pszWideBuff); MultiByteToWideChar(CP_UTF7, 0, wide2utf7, -1, out, outLen); out[outLen - 1] = UNICODE_NULL;
return lstrlenW(out);}
static BOOL Is7BitClean(LPCWSTR psz){ if (!psz) return TRUE;
while (*psz) { if (*psz > 0x7F) return FALSE; ++psz; } return TRUE;}
/************************************************************************** * SHSetIniStringW (SHLWAPI.295) * * @see https://source.winehq.org/WineAPI/SHSetIniStringW.html */EXTERN_C BOOL WINAPISHSetIniStringW( _In_z_ LPCWSTR appName, _In_z_ LPCWSTR keyName, _In_opt_z_ LPCWSTR str, _In_z_ LPCWSTR filename){ TRACE("(%s, %p, %s, %s)\n", debugstr_w(appName), keyName, debugstr_w(str), debugstr_w(filename));
// Write a normal profile string. If str was NULL, then key will be deleted if (!WritePrivateProfileStringW(appName, keyName, str, filename)) return FALSE;
if (Is7BitClean(str)) { // Delete ".A" version CStringW szSection(appName); szSection += L".A"; WritePrivateProfileStringW(szSection, keyName, NULL, filename);
// Delete ".W" version szSection = appName; szSection += L".W"; WritePrivateProfileStringW(szSection, keyName, NULL, filename);
return TRUE; }
// Now str is not 7-bit clean. It needs UTF-7 encoding in UTF-16. // We write ".A" and ".W"-appended sections CW2A wide2utf7(str, CP_UTF7); CA2W utf72wide(wide2utf7, CP_ACP);
BOOL ret = TRUE;
// Write ".A" version CStringW szSection(appName); szSection += L".A"; if (!WritePrivateProfileStringW(szSection, keyName, str, filename)) ret = FALSE;
// Write ".W" version szSection = appName; szSection += L".W"; if (!WritePrivateProfileStringW(szSection, keyName, utf72wide, filename)) ret = FALSE;
return ret;}
/************************************************************************** * SHGetIniStringUTF7W (SHLWAPI.473) * * Retrieves a string value from an INI file. * * @param lpAppName The section name. * @param lpKeyName The key name. * If this string begins from '@', the value will be interpreted as UTF-7. * @param lpReturnedString Receives a wide string value. * @param nSize The number of characters in lpReturnedString. * @param lpFileName The INI file. * @return The number of characters copied to the buffer if succeeded. */EXTERN_C DWORD WINAPISHGetIniStringUTF7W( _In_opt_z_ LPCWSTR lpAppName, _In_z_ LPCWSTR lpKeyName, _Out_writes_to_(nSize, return + 1) _Post_z_ LPWSTR lpReturnedString, _In_ DWORD nSize, _In_z_ LPCWSTR lpFileName){ if (*lpKeyName == L'@') // UTF-7 return SHGetIniStringW(lpAppName, lpKeyName + 1, lpReturnedString, nSize, lpFileName);
return GetPrivateProfileStringW(lpAppName, lpKeyName, L"", lpReturnedString, nSize, lpFileName);}
/************************************************************************** * SHSetIniStringUTF7W (SHLWAPI.474) * * Sets a string value on an INI file. * * @param lpAppName The section name. * @param lpKeyName The key name. * If this begins from '@', the value will be stored as UTF-7. * @param lpString The wide string value to be set. * @param lpFileName The INI file. * @return TRUE if successful. FALSE if failed. */EXTERN_C BOOL WINAPISHSetIniStringUTF7W( _In_z_ LPCWSTR lpAppName, _In_z_ LPCWSTR lpKeyName, _In_opt_z_ LPCWSTR lpString, _In_z_ LPCWSTR lpFileName){ if (*lpKeyName == L'@') // UTF-7 return SHSetIniStringW(lpAppName, lpKeyName + 1, lpString, lpFileName);
return WritePrivateProfileStringW(lpAppName, lpKeyName, lpString, lpFileName);}
class CIniPropertyBag : public CBasePropertyBag{protected: LPWSTR m_pszFileName; LPWSTR m_pszSection; BOOL m_bAlternateStream; // ADS (Alternate Data Stream)
static BOOL LooksLikeAnAlternateStream(LPCWSTR pszStart) { LPCWSTR pch = StrRChrW(pszStart, NULL, L'\\'); if (!pch) pch = pszStart; return StrChrW(pch, L':') != NULL; }
HRESULT _GetSectionAndName( LPCWSTR pszStart, LPWSTR pszSection, UINT cchSectionMax, LPWSTR pszName, UINT cchNameMax);
public: CIniPropertyBag(DWORD dwMode) : CBasePropertyBag(dwMode) , m_pszFileName(NULL) , m_pszSection(NULL) , m_bAlternateStream(FALSE) { }
~CIniPropertyBag() override { ::LocalFree(m_pszFileName); ::LocalFree(m_pszSection); }
HRESULT Init(LPCWSTR pszIniFile, LPCWSTR pszSection);
STDMETHODIMP Read( _In_z_ LPCWSTR pszPropName, _Inout_ VARIANT *pvari, _Inout_opt_ IErrorLog *pErrorLog) override;
STDMETHODIMP Write(_In_z_ LPCWSTR pszPropName, _In_ VARIANT *pvari) override;};
HRESULT CIniPropertyBag::Init(LPCWSTR pszIniFile, LPCWSTR pszSection){ m_pszFileName = StrDupW(pszIniFile); if (!m_pszFileName) return E_OUTOFMEMORY;
// Is it an ADS (Alternate Data Stream) pathname? m_bAlternateStream = LooksLikeAnAlternateStream(m_pszFileName);
if (pszSection) { m_pszSection = StrDupW(pszSection); if (!m_pszSection) return E_OUTOFMEMORY; }
return S_OK;}
HRESULTCIniPropertyBag::_GetSectionAndName( LPCWSTR pszStart, LPWSTR pszSection, UINT cchSectionMax, LPWSTR pszName, UINT cchNameMax){ LPCWSTR pchSep = StrChrW(pszStart, L'\\'); if (pchSep) { UINT cchSep = (UINT)(pchSep - pszStart + 1); StrCpyNW(pszSection, pszStart, min(cchSep, cchSectionMax)); StrCpyNW(pszName, pchSep + 1, cchNameMax); return S_OK; }
if (m_pszSection) { StrCpyNW(pszSection, m_pszSection, cchSectionMax); StrCpyNW(pszName, pszStart, cchNameMax); return S_OK; }
ERR("%p: %s\n", this, debugstr_w(pszStart)); return E_INVALIDARG;}
STDMETHODIMPCIniPropertyBag::Read( _In_z_ LPCWSTR pszPropName, _Inout_ VARIANT *pvari, _Inout_opt_ IErrorLog *pErrorLog){ UNREFERENCED_PARAMETER(pErrorLog);
TRACE("%p: %s %p %p\n", this, debugstr_w(pszPropName), pvari, pErrorLog);
VARTYPE vt = V_VT(pvari);
::VariantInit(pvari);
if (!MODE_CAN_READ(m_dwMode)) { ERR("%p: 0x%X\n", this, m_dwMode); return E_ACCESSDENIED; }
WCHAR szSection[64], szName[64]; HRESULT hr = _GetSectionAndName(pszPropName, szSection, _countof(szSection), szName, _countof(szName)); if (FAILED(hr)) return hr;
const INT cchBuffMax = 4 * MAX_PATH; // UTF-7 needs 4 times length buffer. CComHeapPtr<WCHAR> pszBuff; if (!pszBuff.Allocate(cchBuffMax * sizeof(WCHAR))) return E_OUTOFMEMORY;
if (!SHGetIniStringUTF7W(szSection, szName, pszBuff, cchBuffMax, m_pszFileName)) return E_FAIL;
BSTR bstr = ::SysAllocString(pszBuff); V_BSTR(pvari) = bstr; if (!bstr) return E_OUTOFMEMORY;
V_VT(pvari) = VT_BSTR; return ::VariantChangeTypeForRead(pvari, vt);}
STDMETHODIMPCIniPropertyBag::Write(_In_z_ LPCWSTR pszPropName, _In_ VARIANT *pvari){ TRACE("%p: %s %p\n", this, debugstr_w(pszPropName), pvari);
if (!MODE_CAN_WRITE(m_dwMode)) { ERR("%p: 0x%X\n", this, m_dwMode); return E_ACCESSDENIED; }
HRESULT hr; BSTR bstr; VARIANTARG vargTemp = { 0 }; switch (V_VT(pvari)) { case VT_EMPTY: bstr = NULL; break;
case VT_BSTR: bstr = V_BSTR(pvari); break;
default: hr = ::VariantChangeType(&vargTemp, pvari, 0, VT_BSTR); if (FAILED(hr)) goto Quit;
bstr = V_BSTR(&vargTemp); break; }
WCHAR szSection[64], szName[64]; hr = _GetSectionAndName(pszPropName, szSection, _countof(szSection), szName, _countof(szName)); if (SUCCEEDED(hr)) { if (SHSetIniStringUTF7W(szSection, szName, bstr, m_pszFileName)) { if (!m_bAlternateStream) SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATHW, m_pszFileName, NULL); } else { hr = E_FAIL; } }
Quit: ::VariantClear(&vargTemp); return hr;}
/************************************************************************** * SHCreatePropertyBagOnProfileSection (SHLWAPI.472) * * Creates a property bag object on INI file. * * @param lpFileName The INI filename. * @param pszSection The optional section name. * @param dwMode The combination of STGM_READ, STGM_WRITE, STGM_READWRITE, and STGM_CREATE. * @param riid Specifies either IID_IUnknown, IID_IPropertyBag or IID_IPropertyBag2. * @param ppvObj Receives an IPropertyBag pointer. * @return An HRESULT value. S_OK on success, non-zero on failure. * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/createonprofilesection.htm */EXTERN_C HRESULT WINAPISHCreatePropertyBagOnProfileSection( _In_z_ LPCWSTR lpFileName, _In_opt_z_ LPCWSTR pszSection, _In_ DWORD dwMode, _In_ REFIID riid, _Out_ void **ppvObj){ HANDLE hFile; PWCHAR pchFileTitle; WCHAR szBuff[MAX_PATH];
if (dwMode & STGM_CREATE) { hFile = ::CreateFileW(lpFileName, 0, FILE_SHARE_DELETE, 0, CREATE_NEW, FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM, NULL); if (hFile != INVALID_HANDLE_VALUE) { pchFileTitle = PathFindFileNameW(lpFileName); if (lstrcmpiW(pchFileTitle, L"desktop.ini") == 0) { StrCpyNW(szBuff, lpFileName, _countof(szBuff)); if (PathRemoveFileSpecW(szBuff)) PathMakeSystemFolderW(szBuff); } ::CloseHandle(hFile); } }
*ppvObj = NULL;
if (!PathFileExistsW(lpFileName)) return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
CComPtr<CIniPropertyBag> pIniPB(new CIniPropertyBag(dwMode));
HRESULT hr = pIniPB->Init(lpFileName, pszSection); if (FAILED(hr)) { ERR("0x%08X\n", hr); return hr; }
return pIniPB->QueryInterface(riid, ppvObj);}
class CDesktopUpgradePropertyBag : public CBasePropertyBag{protected: BOOL _AlreadyUpgraded(HKEY hKey); VOID _MarkAsUpgraded(HKEY hkey); HRESULT _ReadFlags(VARIANT *pvari); HRESULT _ReadItemPositions(VARIANT *pvari); IStream* _GetOldDesktopViewStream(); IStream* _NewStreamFromOld(IStream *pOldStream);
public: CDesktopUpgradePropertyBag() : CBasePropertyBag(STGM_READ) { }
STDMETHODIMP Read( _In_z_ LPCWSTR pszPropName, _Inout_ VARIANT *pvari, _Inout_opt_ IErrorLog *pErrorLog) override;
STDMETHODIMP Write(_In_z_ LPCWSTR pszPropName, _In_ VARIANT *pvari) override { ERR("%p: %s: Read-only\n", this, debugstr_w(pszPropName)); return E_NOTIMPL; }};
VOID CDesktopUpgradePropertyBag::_MarkAsUpgraded(HKEY hkey){ DWORD dwValue = TRUE; SHSetValueW(hkey, NULL, L"Upgrade", REG_DWORD, &dwValue, sizeof(dwValue));}
BOOL CDesktopUpgradePropertyBag::_AlreadyUpgraded(HKEY hKey){ // Check the existence of the value written in _MarkAsUpgraded. DWORD dwValue, cbData = sizeof(dwValue); return SHGetValueW(hKey, NULL, L"Upgrade", NULL, &dwValue, &cbData) == ERROR_SUCCESS;}
typedef DWORDLONG DESKVIEW_FLAGS; // 64-bit data
HRESULT CDesktopUpgradePropertyBag::_ReadFlags(VARIANT *pvari){ DESKVIEW_FLAGS Flags; DWORD cbValue = sizeof(Flags); if (SHGetValueW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\DeskView", L"Settings", NULL, &Flags, &cbValue) != ERROR_SUCCESS || cbValue < sizeof(Flags)) { return E_FAIL; }
V_UINT(pvari) = ((UINT)(Flags >> 32)) | 0x220; // FIXME: Magic number V_VT(pvari) = VT_UINT; return S_OK;}
typedef struct tagOLD_STREAM_HEADER{ WORD wMagic; WORD awUnknown[6]; WORD wSize;} OLD_STREAM_HEADER, *POLD_STREAM_HEADER;
IStream* CDesktopUpgradePropertyBag::_NewStreamFromOld(IStream *pOldStream){ OLD_STREAM_HEADER Header; HRESULT hr = pOldStream->Read(&Header, sizeof(Header), NULL); if (FAILED(hr) || Header.wMagic != 28) return NULL;
// Move stream pointer LARGE_INTEGER li; li.QuadPart = Header.wSize - sizeof(Header); hr = pOldStream->Seek(li, STREAM_SEEK_CUR, NULL); if (FAILED(hr)) return NULL;
// Get the size ULARGE_INTEGER uli; hr = IStream_Size(pOldStream, &uli); if (FAILED(hr)) return NULL;
// Create new stream and attach CComPtr<IStream> pNewStream; pNewStream.Attach(SHCreateMemStream(NULL, 0)); if (!pNewStream) return NULL;
// Subtract Header.wSize from the size uli.QuadPart -= Header.wSize;
// Copy to pNewStream hr = pOldStream->CopyTo(pNewStream, uli, NULL, NULL); if (FAILED(hr)) return NULL;
li.QuadPart = 0; pNewStream->Seek(li, STREAM_SEEK_SET, NULL);
return pNewStream.Detach();}
IStream* CDesktopUpgradePropertyBag::_GetOldDesktopViewStream(){ HKEY hKey = SHGetShellKey(SHKEY_Root_HKCU, L"Streams\\Desktop", FALSE); if (!hKey) return NULL;
CComPtr<IStream> pOldStream; if (!_AlreadyUpgraded(hKey)) { pOldStream.Attach(SHOpenRegStream2W(hKey, NULL, L"ViewView2", 0)); if (pOldStream) { ULARGE_INTEGER uli; HRESULT hr = IStream_Size(pOldStream, &uli); if (SUCCEEDED(hr) && !uli.QuadPart) pOldStream.Release(); }
if (!pOldStream) pOldStream.Attach(SHOpenRegStream2W(hKey, NULL, L"ViewView", 0));
_MarkAsUpgraded(hKey); }
::RegCloseKey(hKey); return pOldStream.Detach();}
HRESULT CDesktopUpgradePropertyBag::_ReadItemPositions(VARIANT *pvari){ CComPtr<IStream> pOldStream; pOldStream.Attach(_GetOldDesktopViewStream()); if (!pOldStream) return E_FAIL;
HRESULT hr = E_FAIL; IStream *pNewStream = _NewStreamFromOld(pOldStream); if (pNewStream) { V_UNKNOWN(pvari) = pNewStream; V_VT(pvari) = VT_UNKNOWN; hr = S_OK; }
return hr;}
STDMETHODIMPCDesktopUpgradePropertyBag::Read( _In_z_ LPCWSTR pszPropName, _Inout_ VARIANT *pvari, _Inout_opt_ IErrorLog *pErrorLog){ UNREFERENCED_PARAMETER(pErrorLog);
VARTYPE vt = V_VT(pvari);
HRESULT hr = E_FAIL; if (StrCmpW(L"FFlags", pszPropName) == 0) hr = _ReadFlags(pvari); else if (StrCmpNW(L"ItemPos", pszPropName, 7) == 0) hr = _ReadItemPositions(pvari);
if (FAILED(hr)) { ::VariantInit(pvari); return hr; }
return ::VariantChangeType(pvari, pvari, 0, vt);}
/************************************************************************** * SHGetDesktopUpgradePropertyBag (Internal) * * Creates or gets a property bag object for desktop upgrade * * @param riid Specifies either IID_IUnknown, IID_IPropertyBag or IID_IPropertyBag2. * @param ppvObj Receives an IPropertyBag pointer. * @return An HRESULT value. S_OK on success, non-zero on failure. */HRESULT SHGetDesktopUpgradePropertyBag(REFIID riid, void **ppvObj){ *ppvObj = NULL; CComPtr<CDesktopUpgradePropertyBag> pPropBag(new CDesktopUpgradePropertyBag()); return pPropBag->QueryInterface(riid, ppvObj);}
class CViewStatePropertyBag : public CBasePropertyBag{protected: LPITEMIDLIST m_pidl = NULL; LPWSTR m_pszPath = NULL; DWORD m_dwVspbFlags = 0; // SHGVSPB_... flags CComPtr<IPropertyBag> m_pPidlBag; CComPtr<IPropertyBag> m_pUpgradeBag; CComPtr<IPropertyBag> m_pInheritBag; CComPtr<IPropertyBag> m_pUserDefaultsBag; CComPtr<IPropertyBag> m_pFolderDefaultsBag; CComPtr<IPropertyBag> m_pGlobalDefaultsBag; CComPtr<IPropertyBag> m_pReadBag; CComPtr<IPropertyBag> m_pWriteBag; BOOL m_bPidlBag = FALSE; BOOL m_bUpgradeBag = FALSE; BOOL m_bInheritBag = FALSE; BOOL m_bUserDefaultsBag = FALSE; BOOL m_bFolderDefaultsBag = FALSE; BOOL m_bGlobalDefaultsBag = FALSE; BOOL m_bReadBag = FALSE; BOOL m_bWriteBag = FALSE;
BOOL _IsSamePidl(LPCITEMIDLIST pidlOther) const; BOOL _IsSystemFolder() const; BOOL _CanAccessPidlBag() const; BOOL _CanAccessUserDefaultsBag() const; BOOL _CanAccessFolderDefaultsBag() const; BOOL _CanAccessGlobalDefaultsBag() const; BOOL _CanAccessInheritBag() const; BOOL _CanAccessUpgradeBag() const;
HKEY _GetHKey(DWORD dwVspbFlags);
UINT _GetMRUSize(HKEY hKey);
HRESULT _GetMRUSlots( LPCITEMIDLIST pidl, DWORD dwMode, HKEY hKey, UINT *puSlots, UINT cSlots, UINT *pcSlots);
HRESULT _GetMRUSlot(LPCITEMIDLIST pidl, DWORD dwMode, HKEY hKey, UINT *pSlot);
HRESULT _GetRegKey( LPCITEMIDLIST pidl, LPCWSTR pszBagName, DWORD dwFlags, DWORD dwMode, HKEY hKey, LPWSTR pszDest, INT cchDest);
HRESULT _CreateBag( LPITEMIDLIST pidl, LPCWSTR pszPath, DWORD dwVspbFlags, DWORD dwMode, REFIID riid, IPropertyBag **pppb);
HRESULT _FindNearestInheritBag(REFIID riid, IPropertyBag **pppb);
void _ResetTryAgainFlag();
BOOL _EnsureReadBag(DWORD dwMode, REFIID riid); BOOL _EnsurePidlBag(DWORD dwMode, REFIID riid); BOOL _EnsureInheritBag(DWORD dwMode, REFIID riid); BOOL _EnsureUpgradeBag(DWORD dwMode, REFIID riid); BOOL _EnsureUserDefaultsBag(DWORD dwMode, REFIID riid); BOOL _EnsureFolderDefaultsBag(DWORD dwMode, REFIID riid); BOOL _EnsureGlobalDefaultsBag(DWORD dwMode, REFIID riid); BOOL _EnsureWriteBag(DWORD dwMode, REFIID riid); HRESULT _ReadPidlBag(LPCWSTR pszPropName, VARIANT *pvari, IErrorLog *pErrorLog); HRESULT _ReadInheritBag(LPCWSTR pszPropName, VARIANT *pvari, IErrorLog *pErrorLog); HRESULT _ReadUpgradeBag(LPCWSTR pszPropName, VARIANT *pvari, IErrorLog *pErrorLog); HRESULT _ReadUserDefaultsBag(LPCWSTR pszPropName, VARIANT *pvari, IErrorLog *pErrorLog); HRESULT _ReadFolderDefaultsBag(LPCWSTR pszPropName, VARIANT *pvari, IErrorLog *pErrorLog); HRESULT _ReadGlobalDefaultsBag(LPCWSTR pszPropName, VARIANT *pvari, IErrorLog *pErrorLog); void _PruneMRUTree();
public: CViewStatePropertyBag() : CBasePropertyBag(STGM_READ) { }
~CViewStatePropertyBag() override { ::ILFree(m_pidl); ::LocalFree(m_pszPath); }
HRESULT Init(_In_opt_ LPCITEMIDLIST pidl, _In_opt_ LPCWSTR pszPath, _In_ DWORD dwVspbFlags); BOOL IsSameBag(LPCITEMIDLIST pidl, LPCWSTR pszPath, DWORD dwVspbFlags) const;
STDMETHODIMP Read( _In_z_ LPCWSTR pszPropName, _Inout_ VARIANT *pvari, _Inout_opt_ IErrorLog *pErrorLog) override;
STDMETHODIMP Write(_In_z_ LPCWSTR pszPropName, _In_ VARIANT *pvari) override;};
// CViewStatePropertyBag is cachedCComPtr<CViewStatePropertyBag> g_pCachedBag;extern "C"{ CRITICAL_SECTION g_csBagCacheLock;}
HRESULTCViewStatePropertyBag::Init( _In_opt_ LPCITEMIDLIST pidl, _In_opt_ LPCWSTR pszPath, _In_ DWORD dwVspbFlags){ if (pidl) { m_pidl = ILClone(pidl); if (!m_pidl) return E_OUTOFMEMORY; }
if (pszPath) { m_pszPath = StrDupW(pszPath); if (!m_pszPath) return E_OUTOFMEMORY;
m_dwVspbFlags = dwVspbFlags; }
return S_OK;}
BOOL CViewStatePropertyBag::_IsSamePidl(LPCITEMIDLIST pidlOther) const{ if (!pidlOther && !m_pidl) return TRUE;
return (pidlOther && m_pidl && ILIsEqual(pidlOther, m_pidl));}
BOOL CViewStatePropertyBag::IsSameBag(LPCITEMIDLIST pidl, LPCWSTR pszPath, DWORD dwVspbFlags) const{ return (dwVspbFlags == m_dwVspbFlags && StrCmpW(pszPath, m_pszPath) == 0 && _IsSamePidl(pidl));}
BOOL CViewStatePropertyBag::_IsSystemFolder() const{ LPCITEMIDLIST ppidlLast; CComPtr<IShellFolder> psf;
HRESULT hr = SHBindToParent(m_pidl, IID_IShellFolder, (void **)&psf, &ppidlLast); if (FAILED(hr)) return FALSE;
WIN32_FIND_DATAW FindData; hr = SHGetDataFromIDListW(psf, ppidlLast, SHGDFIL_FINDDATA, &FindData, sizeof(FindData)); if (FAILED(hr)) return FALSE;
return PathIsSystemFolderW(NULL, FindData.dwFileAttributes);}
BOOL CViewStatePropertyBag::_CanAccessPidlBag() const{ return ((m_dwVspbFlags & SHGVSPB_FOLDER) == SHGVSPB_FOLDER);}
BOOL CViewStatePropertyBag::_CanAccessUserDefaultsBag() const{ if (_CanAccessPidlBag()) return TRUE;
return ((m_dwVspbFlags & SHGVSPB_USERDEFAULTS) == SHGVSPB_USERDEFAULTS);}
BOOL CViewStatePropertyBag::_CanAccessFolderDefaultsBag() const{ if (_CanAccessUserDefaultsBag()) return TRUE;
return ((m_dwVspbFlags & SHGVSPB_ALLUSERS) && (m_dwVspbFlags & SHGVSPB_PERFOLDER));}
BOOL CViewStatePropertyBag::_CanAccessGlobalDefaultsBag() const{ if (_CanAccessFolderDefaultsBag()) return TRUE;
return ((m_dwVspbFlags & SHGVSPB_GLOBALDEFAULTS) == SHGVSPB_GLOBALDEFAULTS);}
BOOL CViewStatePropertyBag::_CanAccessInheritBag() const{ return (_CanAccessPidlBag() || (m_dwVspbFlags & SHGVSPB_INHERIT));}
BOOL CViewStatePropertyBag::_CanAccessUpgradeBag() const{ return StrCmpW(m_pszPath, L"Desktop") == 0;}
void CViewStatePropertyBag::_ResetTryAgainFlag(){ if (m_dwVspbFlags & SHGVSPB_NOAUTODEFAULTS) m_bReadBag = FALSE; else if ((m_dwVspbFlags & SHGVSPB_FOLDER) == SHGVSPB_FOLDER) m_bPidlBag = FALSE; else if (m_dwVspbFlags & SHGVSPB_INHERIT) m_bInheritBag = FALSE; else if ((m_dwVspbFlags & SHGVSPB_USERDEFAULTS) == SHGVSPB_USERDEFAULTS) m_bUserDefaultsBag = FALSE; else if ((m_dwVspbFlags & SHGVSPB_ALLUSERS) && (m_dwVspbFlags & SHGVSPB_PERFOLDER)) m_bFolderDefaultsBag = FALSE; else if ((m_dwVspbFlags & SHGVSPB_GLOBALDEFAULTS) == SHGVSPB_GLOBALDEFAULTS) m_bGlobalDefaultsBag = FALSE;}
HKEY CViewStatePropertyBag::_GetHKey(DWORD dwVspbFlags){ if (!(dwVspbFlags & (SHGVSPB_INHERIT | SHGVSPB_PERUSER))) return SHGetShellKey((SHKEY_Key_Shell | SHKEY_Root_HKLM), NULL, TRUE);
if ((m_dwVspbFlags & SHGVSPB_ROAM) && (dwVspbFlags & SHGVSPB_PERFOLDER)) return SHGetShellKey((SHKEY_Key_Shell | SHKEY_Root_HKCU), NULL, TRUE);
return SHGetShellKey(SHKEY_Key_ShellNoRoam | SHKEY_Root_HKCU, NULL, TRUE);}
UINT CViewStatePropertyBag::_GetMRUSize(HKEY hKey){ DWORD dwValue, cbValue = sizeof(dwValue);
if (SHGetValueW(hKey, NULL, L"BagMRU Size", NULL, &dwValue, &cbValue) != ERROR_SUCCESS) return 400; // The default size of the MRU (most recently used) list
return (UINT)dwValue;}
HRESULTCViewStatePropertyBag::_GetMRUSlots( LPCITEMIDLIST pidl, DWORD dwMode, HKEY hKey, UINT *puSlots, UINT cSlots, UINT *pcSlots){ CComPtr<IMruPidlList> pMruList; HRESULT hr = ::CoCreateInstance(CLSID_MruPidlList, NULL, CLSCTX_INPROC_SERVER, IID_IMruPidlList, (void**)&pMruList); if (FAILED(hr)) return hr;
UINT cMRUSize = _GetMRUSize(hKey); hr = pMruList->InitList(cMRUSize, hKey, L"BagMRU"); if (FAILED(hr)) return hr;
hr = pMruList->QueryPidl(pidl, cSlots, puSlots, pcSlots); if (hr == S_OK || MODE_CAN_WRITE(dwMode)) // FIXME: HACK! (Without this, a new pidl can never be saved) hr = pMruList->UsePidl(pidl, puSlots); else if (cSlots == 1) hr = E_FAIL;
return hr;}
HRESULTCViewStatePropertyBag::_GetMRUSlot(LPCITEMIDLIST pidl, DWORD dwMode, HKEY hKey, UINT *pSlot){ UINT cSlots; return _GetMRUSlots(pidl, dwMode, hKey, pSlot, 1, &cSlots);}
HRESULTCViewStatePropertyBag::_GetRegKey( LPCITEMIDLIST pidl, LPCWSTR pszBagName, DWORD dwFlags, DWORD dwMode, HKEY hKey, LPWSTR pszDest, INT cchDest){ HRESULT hr = S_OK; UINT nSlot;
if (dwFlags & (SHGVSPB_INHERIT | SHGVSPB_PERFOLDER)) { hr = _GetMRUSlot(pidl, dwMode, hKey, &nSlot); if (SUCCEEDED(hr)) { if (dwFlags & SHGVSPB_INHERIT) StringCchPrintfW(pszDest, cchDest, L"Bags\\%d\\%s\\Inherit", nSlot, pszBagName); else StringCchPrintfW(pszDest, cchDest, L"Bags\\%d\\%s", nSlot, pszBagName); } } else { StringCchPrintfW(pszDest, cchDest, L"Bags\\AllFolders\\%s", pszBagName); }
return hr;}
static HRESULT BindCtx_CreateWithMode(DWORD dwMode, IBindCtx **ppbc){ HRESULT hr = ::CreateBindCtx(0, ppbc); if (FAILED(hr)) return hr;
IBindCtx *pbc = *ppbc;
BIND_OPTS opts = { sizeof(opts) }; opts.grfMode = dwMode; hr = pbc->SetBindOptions(&opts); if (FAILED(hr)) { pbc->Release(); *ppbc = NULL; }
return hr;}
HRESULTCViewStatePropertyBag::_CreateBag( LPITEMIDLIST pidl, LPCWSTR pszPath, DWORD dwVspbFlags, DWORD dwMode, REFIID riid, IPropertyBag **pppb){ HRESULT hr; HKEY hKey; CComPtr<IBindCtx> pBC; CComPtr<IShellFolder> psf; WCHAR szBuff[64];
if (MODE_CAN_WRITE(dwMode)) dwMode |= STGM_CREATE;
if ((dwVspbFlags & SHGVSPB_ALLUSERS) && (dwVspbFlags & SHGVSPB_PERFOLDER)) { hr = BindCtx_CreateWithMode(dwMode, &pBC); if (SUCCEEDED(hr)) { hr = SHGetDesktopFolder(&psf); if (SUCCEEDED(hr)) { hr = psf->BindToObject(m_pidl, pBC, riid, (void **)pppb); if (SUCCEEDED(hr) && !*pppb) hr = E_FAIL; } } } else { hKey = _GetHKey(dwVspbFlags); if (!hKey) return E_FAIL;
hr = _GetRegKey(pidl, pszPath, dwVspbFlags, dwMode, hKey, szBuff, _countof(szBuff)); if (SUCCEEDED(hr)) hr = SHCreatePropertyBagOnRegKey(hKey, szBuff, dwMode, riid, (void**)pppb);
::RegCloseKey(hKey); }
return hr;}
HRESULTCViewStatePropertyBag::_FindNearestInheritBag(REFIID riid, IPropertyBag **pppb){ *pppb = NULL;
HKEY hKey = _GetHKey(SHGVSPB_INHERIT); if (!hKey) return E_FAIL;
UINT cSlots, anSlots[64]; if (FAILED(_GetMRUSlots(m_pidl, 0, hKey, anSlots, _countof(anSlots), &cSlots)) || !cSlots) { ::RegCloseKey(hKey); return E_FAIL; }
HRESULT hr = E_FAIL; WCHAR szBuff[64]; for (UINT iSlot = 0; iSlot < cSlots; ++iSlot) { StringCchPrintfW(szBuff, _countof(szBuff), L"Bags\\%d\\%s\\Inherit", anSlots[iSlot], m_pszPath); hr = SHCreatePropertyBagOnRegKey(hKey, szBuff, STGM_READ, riid, (void**)pppb); if (SUCCEEDED(hr)) break; }
::RegCloseKey(hKey); return hr;}
BOOL CViewStatePropertyBag::_EnsureReadBag(DWORD dwMode, REFIID riid){ if (!m_pReadBag && !m_bReadBag) { m_bReadBag = TRUE; _CreateBag(m_pidl, m_pszPath, m_dwVspbFlags, dwMode, riid, &m_pReadBag); } return (m_pReadBag != NULL);}
BOOL CViewStatePropertyBag::_EnsurePidlBag(DWORD dwMode, REFIID riid){ if (!m_pPidlBag && !m_bPidlBag && _CanAccessPidlBag()) { m_bPidlBag = TRUE; _CreateBag(m_pidl, m_pszPath, SHGVSPB_FOLDER, dwMode, riid, &m_pPidlBag); } return (m_pPidlBag != NULL);}
BOOL CViewStatePropertyBag::_EnsureInheritBag(DWORD dwMode, REFIID riid){ if (!m_pInheritBag && !m_bInheritBag && _CanAccessInheritBag()) { m_bInheritBag = TRUE; _FindNearestInheritBag(riid, &m_pInheritBag); } return (m_pInheritBag != NULL);}
BOOL CViewStatePropertyBag::_EnsureUpgradeBag(DWORD dwMode, REFIID riid){ if (!m_pUpgradeBag && !m_bUpgradeBag && _CanAccessUpgradeBag()) { m_bUpgradeBag = TRUE; SHGetDesktopUpgradePropertyBag(riid, (void**)&m_pUpgradeBag); } return (m_pUpgradeBag != NULL);}
BOOL CViewStatePropertyBag::_EnsureUserDefaultsBag(DWORD dwMode, REFIID riid){ if (!m_pUserDefaultsBag && !m_bUserDefaultsBag && _CanAccessUserDefaultsBag()) { m_bUserDefaultsBag = TRUE; _CreateBag(NULL, m_pszPath, SHGVSPB_USERDEFAULTS, dwMode, riid, &m_pUserDefaultsBag); } return (m_pUserDefaultsBag != NULL);}
BOOL CViewStatePropertyBag::_EnsureFolderDefaultsBag(DWORD dwMode, REFIID riid){ if (!m_pFolderDefaultsBag && !m_bFolderDefaultsBag && _CanAccessFolderDefaultsBag()) { m_bFolderDefaultsBag = TRUE; if (_IsSystemFolder()) { _CreateBag(m_pidl, m_pszPath, SHGVSPB_PERFOLDER | SHGVSPB_ALLUSERS, dwMode, riid, &m_pFolderDefaultsBag); } } return (m_pFolderDefaultsBag != NULL);}
BOOL CViewStatePropertyBag::_EnsureGlobalDefaultsBag(DWORD dwMode, REFIID riid){ if (!m_pGlobalDefaultsBag && !m_bGlobalDefaultsBag && _CanAccessGlobalDefaultsBag()) { m_bGlobalDefaultsBag = TRUE; _CreateBag(NULL, m_pszPath, SHGVSPB_GLOBALDEFAULTS, dwMode, riid, &m_pGlobalDefaultsBag); } return (m_pGlobalDefaultsBag != NULL);}
HRESULTCViewStatePropertyBag::_ReadPidlBag( LPCWSTR pszPropName, VARIANT *pvari, IErrorLog *pErrorLog){ if (!_EnsurePidlBag(STGM_READ, IID_IPropertyBag)) return E_FAIL;
return m_pPidlBag->Read(pszPropName, pvari, pErrorLog);}
HRESULTCViewStatePropertyBag::_ReadInheritBag( LPCWSTR pszPropName, VARIANT *pvari, IErrorLog *pErrorLog){ if (!_EnsureInheritBag(STGM_READ, IID_IPropertyBag)) return E_FAIL;
return m_pInheritBag->Read(pszPropName, pvari, pErrorLog);}
HRESULTCViewStatePropertyBag::_ReadUpgradeBag( LPCWSTR pszPropName, VARIANT *pvari, IErrorLog *pErrorLog){ if (!_EnsureUpgradeBag(STGM_READ, IID_IPropertyBag)) return E_FAIL;
return m_pUpgradeBag->Read(pszPropName, pvari, pErrorLog);}
HRESULTCViewStatePropertyBag::_ReadUserDefaultsBag( LPCWSTR pszPropName, VARIANT *pvari, IErrorLog *pErrorLog){ if (!_EnsureUserDefaultsBag(STGM_READ, IID_IPropertyBag)) return E_FAIL;
return m_pUserDefaultsBag->Read(pszPropName, pvari, pErrorLog);}
HRESULTCViewStatePropertyBag::_ReadFolderDefaultsBag( LPCWSTR pszPropName, VARIANT *pvari, IErrorLog *pErrorLog){ if (!_EnsureFolderDefaultsBag(STGM_READ, IID_IPropertyBag)) return E_FAIL;
return m_pFolderDefaultsBag->Read(pszPropName, pvari, pErrorLog);}
HRESULTCViewStatePropertyBag::_ReadGlobalDefaultsBag( LPCWSTR pszPropName, VARIANT *pvari, IErrorLog *pErrorLog){ if (!_EnsureGlobalDefaultsBag(STGM_READ, IID_IPropertyBag)) return E_FAIL;
return m_pGlobalDefaultsBag->Read(pszPropName, pvari, pErrorLog);}
STDMETHODIMPCViewStatePropertyBag::Read( _In_z_ LPCWSTR pszPropName, _Inout_ VARIANT *pvari, _Inout_opt_ IErrorLog *pErrorLog){ if ((m_dwVspbFlags & SHGVSPB_NOAUTODEFAULTS) || (m_dwVspbFlags & SHGVSPB_INHERIT)) { if (!_EnsureReadBag(STGM_READ, IID_IPropertyBag)) return E_FAIL;
return m_pReadBag->Read(pszPropName, pvari, pErrorLog); }
HRESULT hr = _ReadPidlBag(pszPropName, pvari, pErrorLog); if (SUCCEEDED(hr)) return hr;
hr = _ReadInheritBag(pszPropName, pvari, pErrorLog); if (SUCCEEDED(hr)) return hr;
hr = _ReadUpgradeBag(pszPropName, pvari, pErrorLog); if (SUCCEEDED(hr)) return hr;
hr = _ReadUserDefaultsBag(pszPropName, pvari, pErrorLog); if (SUCCEEDED(hr)) return hr;
hr = _ReadFolderDefaultsBag(pszPropName, pvari, pErrorLog); if (SUCCEEDED(hr)) return hr;
return _ReadGlobalDefaultsBag(pszPropName, pvari, pErrorLog);}
void CViewStatePropertyBag::_PruneMRUTree(){ HKEY hKey = _GetHKey(SHGVSPB_INHERIT); if (!hKey) return;
CComPtr<IMruPidlList> pMruList; HRESULT hr = ::CoCreateInstance(CLSID_MruPidlList, NULL, CLSCTX_INPROC_SERVER, IID_IMruPidlList, (void**)&pMruList); if (SUCCEEDED(hr)) { hr = pMruList->InitList(200, hKey, L"BagMRU"); if (SUCCEEDED(hr)) pMruList->PruneKids(m_pidl); }
::RegCloseKey(hKey);}
BOOL CViewStatePropertyBag::_EnsureWriteBag(DWORD dwMode, REFIID riid){ if (!m_pWriteBag && !m_bWriteBag) { m_bWriteBag = TRUE; _CreateBag(m_pidl, m_pszPath, m_dwVspbFlags, dwMode, riid, &m_pWriteBag); if (m_pWriteBag) { _ResetTryAgainFlag(); if (m_dwVspbFlags & SHGVSPB_INHERIT) _PruneMRUTree(); } } return (m_pWriteBag != NULL);}
STDMETHODIMP CViewStatePropertyBag::Write(_In_z_ LPCWSTR pszPropName, _In_ VARIANT *pvari){ if (!_EnsureWriteBag(STGM_WRITE, IID_IPropertyBag)) return E_FAIL;
return m_pWriteBag->Write(pszPropName, pvari);}
static BOOL SHIsRemovableDrive(LPCITEMIDLIST pidl){ STRRET strret; CComPtr<IShellFolder> psf; WCHAR szBuff[MAX_PATH]; LPCITEMIDLIST ppidlLast; INT iDrive, nType; HRESULT hr;
hr = SHBindToParent(pidl, IID_IShellFolder, (void **)&psf, &ppidlLast); if (FAILED(hr)) return FALSE;
hr = psf->GetDisplayNameOf(ppidlLast, SHGDN_FORPARSING, &strret); if (FAILED(hr)) return FALSE;
hr = StrRetToBufW(&strret, ppidlLast, szBuff, _countof(szBuff)); if (FAILED(hr)) return FALSE;
iDrive = PathGetDriveNumberW(szBuff); if (iDrive < 0) return FALSE;
nType = RealDriveType(iDrive, FALSE); return (nType == DRIVE_REMOVABLE || nType == DRIVE_CDROM);}
/************************************************************************** * SHGetViewStatePropertyBag (SHLWAPI.515) * * Retrieves a property bag in which the view state information of a folder * can be stored. * * @param pidl PIDL of the folder requested * @param bag_name Name of the property bag requested * @param flags Optional SHGVSPB_... flags * @param riid IID of requested property bag interface * @param ppv Address to receive pointer to the new interface * @return An HRESULT value. S_OK on success, non-zero on failure. * @see https://learn.microsoft.com/en-us/windows/win32/api/shlwapi/nf-shlwapi-shgetviewstatepropertybag */EXTERN_C HRESULT WINAPISHGetViewStatePropertyBag( _In_opt_ PCIDLIST_ABSOLUTE pidl, _In_opt_ LPCWSTR bag_name, _In_ DWORD flags, _In_ REFIID riid, _Outptr_ void **ppv){ HRESULT hr;
TRACE("%p %s 0x%X %p %p\n", pidl, debugstr_w(bag_name), flags, &riid, ppv);
*ppv = NULL;
::EnterCriticalSection(&g_csBagCacheLock);
if (g_pCachedBag && g_pCachedBag->IsSameBag(pidl, bag_name, flags)) { hr = g_pCachedBag->QueryInterface(riid, ppv); ::LeaveCriticalSection(&g_csBagCacheLock); return hr; }
if (SHIsRemovableDrive(pidl)) { TRACE("pidl %p is removable\n", pidl); ::LeaveCriticalSection(&g_csBagCacheLock); return E_FAIL; }
CComPtr<CViewStatePropertyBag> pBag(new CViewStatePropertyBag());
hr = pBag->Init(pidl, bag_name, flags); if (FAILED(hr)) { ERR("0x%08X\n", hr); ::LeaveCriticalSection(&g_csBagCacheLock); return hr; } g_pCachedBag = pBag; ::LeaveCriticalSection(&g_csBagCacheLock); return pBag->QueryInterface(riid, ppv);}
EXTERN_C VOID FreeViewStatePropertyBagCache(VOID){ ::EnterCriticalSection(&g_csBagCacheLock); g_pCachedBag.Release(); ::LeaveCriticalSection(&g_csBagCacheLock);}
/************************************************************************** * SHGetPerScreenResName (SHLWAPI.533) * * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/getperscreenresname.htm */EXTERN_C INT WINAPISHGetPerScreenResName( _Out_writes_(cchBuffer) LPWSTR pszBuffer, _In_ INT cchBuffer, _In_ DWORD dwReserved){ if (dwReserved) return 0;
HDC hDC = ::GetDC(NULL); INT cxWidth = ::GetDeviceCaps(hDC, HORZRES); INT cyHeight = ::GetDeviceCaps(hDC, VERTRES); INT cMonitors = ::GetSystemMetrics(SM_CMONITORS); ::ReleaseDC(NULL, hDC);
StringCchPrintfW(pszBuffer, cchBuffer, L"%dx%d(%d)", cxWidth, cyHeight, cMonitors); return lstrlenW(pszBuffer);}
/************************************************************************** * IUnknown_QueryServicePropertyBag (SHLWAPI.536) * * @param punk An IUnknown interface. * @param flags The SHGVSPB_... flags of SHGetViewStatePropertyBag. * @param riid IID of requested property bag interface. * @param ppvObj Address to receive pointer to the new interface. * @return An HRESULT value. S_OK on success, non-zero on failure. * @see https://geoffchappell.com/studies/windows/shell/shlwapi/api/util/iunknown/queryservicepropertybag.htm */EXTERN_C HRESULT WINAPIIUnknown_QueryServicePropertyBag( _In_ IUnknown *punk, _In_ long flags, _In_ REFIID riid, _Outptr_ void **ppvObj){ TRACE("%p 0x%x %p %p\n", punk, flags, &riid, ppvObj);
CComPtr<IShellBrowserService> pService; HRESULT hr = IUnknown_QueryService(punk, SID_STopLevelBrowser, IID_IShellBrowserService, (void **)&pService); if (FAILED(hr)) { ERR("0x%X\n", hr); return hr; }
return pService->GetPropertyBag(flags, riid, ppvObj);}