1 /* 2 * PROJECT: ReactOS Shell 3 * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later) 4 * PURPOSE: Implement shell property bags 5 * COPYRIGHT: Copyright 2023 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com> 6 */ 7 8 #define _ATL_NO_EXCEPTIONS 9 #include "precomp.h" 10 #include <shlwapi.h> 11 #include <shlwapi_undoc.h> 12 #include <shlobj_undoc.h> 13 #include <shlguid_undoc.h> 14 #include <atlstr.h> // for CStringW 15 #include <atlsimpcoll.h> // for CSimpleMap 16 #include <atlcomcli.h> // for CComVariant 17 #include <atlconv.h> // for CA2W and CW2A 18 #include <strsafe.h> // for StringC... functions 19 20 WINE_DEFAULT_DEBUG_CHANNEL(shell); 21 22 #define MODE_CAN_READ(dwMode) \ 23 (((dwMode) & (STGM_READ | STGM_WRITE | STGM_READWRITE)) != STGM_WRITE) 24 #define MODE_CAN_WRITE(dwMode) \ 25 (((dwMode) & (STGM_READ | STGM_WRITE | STGM_READWRITE)) != STGM_READ) 26 27 class CBasePropertyBag 28 : public IPropertyBag 29 #if (_WIN32_WINNT < _WIN32_WINNT_VISTA) 30 , public IPropertyBag2 31 #endif 32 { 33 protected: 34 LONG m_cRefs; // reference count 35 DWORD m_dwMode; // STGM_* flags 36 37 public: 38 CBasePropertyBag(DWORD dwMode) 39 : m_cRefs(0) 40 , m_dwMode(dwMode) 41 { 42 } 43 44 virtual ~CBasePropertyBag() { } 45 46 // IUnknown interface 47 STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject) override 48 { 49 if (!ppvObject) 50 return E_POINTER; 51 52 #if (_WIN32_WINNT < _WIN32_WINNT_VISTA) 53 if (::IsEqualGUID(riid, IID_IPropertyBag2)) 54 { 55 AddRef(); 56 *ppvObject = static_cast<IPropertyBag2*>(this); 57 return S_OK; 58 } 59 #endif 60 if (::IsEqualGUID(riid, IID_IUnknown) || ::IsEqualGUID(riid, IID_IPropertyBag)) 61 { 62 AddRef(); 63 *ppvObject = static_cast<IPropertyBag*>(this); 64 return S_OK; 65 } 66 67 ERR("%p: %s: E_NOINTERFACE\n", this, debugstr_guid(&riid)); 68 return E_NOINTERFACE; 69 } 70 STDMETHODIMP_(ULONG) AddRef() override 71 { 72 return ::InterlockedIncrement(&m_cRefs); 73 } 74 STDMETHODIMP_(ULONG) Release() override 75 { 76 if (::InterlockedDecrement(&m_cRefs) == 0) 77 { 78 delete this; 79 return 0; 80 } 81 return m_cRefs; 82 } 83 84 #if (_WIN32_WINNT < _WIN32_WINNT_VISTA) 85 // IPropertyBag2 interface (stubs) 86 STDMETHODIMP Read( 87 _In_ ULONG cProperties, 88 _In_ PROPBAG2 *pPropBag, 89 _In_opt_ IErrorLog *pErrorLog, 90 _Out_ VARIANT *pvarValue, 91 _Out_ HRESULT *phrError) override 92 { 93 return E_NOTIMPL; 94 } 95 STDMETHODIMP Write( 96 _In_ ULONG cProperties, 97 _In_ PROPBAG2 *pPropBag, 98 _In_ VARIANT *pvarValue) 99 { 100 return E_NOTIMPL; 101 } 102 STDMETHODIMP CountProperties(_Out_ ULONG *pcProperties) override 103 { 104 return E_NOTIMPL; 105 } 106 STDMETHODIMP GetPropertyInfo( 107 _In_ ULONG iProperty, 108 _In_ ULONG cProperties, 109 _Out_ PROPBAG2 *pPropBag, 110 _Out_ ULONG *pcProperties) override 111 { 112 return E_NOTIMPL; 113 } 114 STDMETHODIMP LoadObject( 115 _In_z_ LPCWSTR pstrName, 116 _In_ DWORD dwHint, 117 _In_ IUnknown *pUnkObject, 118 _In_opt_ IErrorLog *pErrorLog) override 119 { 120 return E_NOTIMPL; 121 } 122 #endif 123 }; 124 125 struct CPropMapEqual 126 { 127 static bool IsEqualKey(const ATL::CStringW& k1, const ATL::CStringW& k2) 128 { 129 return k1.CompareNoCase(k2) == 0; 130 } 131 132 static bool IsEqualValue(const ATL::CComVariant& v1, const ATL::CComVariant& v2) 133 { 134 return false; 135 } 136 }; 137 138 class CMemPropertyBag : public CBasePropertyBag 139 { 140 protected: 141 ATL::CSimpleMap<ATL::CStringW, ATL::CComVariant, CPropMapEqual> m_PropMap; 142 143 public: 144 CMemPropertyBag(DWORD dwMode) : CBasePropertyBag(dwMode) { } 145 146 STDMETHODIMP Read(_In_z_ LPCWSTR pszPropName, _Inout_ VARIANT *pvari, 147 _Inout_opt_ IErrorLog *pErrorLog) override; 148 STDMETHODIMP Write(_In_z_ LPCWSTR pszPropName, _In_ VARIANT *pvari) override; 149 }; 150 151 STDMETHODIMP 152 CMemPropertyBag::Read( 153 _In_z_ LPCWSTR pszPropName, 154 _Inout_ VARIANT *pvari, 155 _Inout_opt_ IErrorLog *pErrorLog) 156 { 157 UNREFERENCED_PARAMETER(pErrorLog); 158 159 TRACE("%p: %s %p %p\n", this, debugstr_w(pszPropName), pvari, pErrorLog); 160 161 VARTYPE vt = V_VT(pvari); 162 163 ::VariantInit(pvari); 164 165 #if (_WIN32_WINNT < _WIN32_WINNT_VISTA) 166 if (!MODE_CAN_READ(m_dwMode)) 167 { 168 ERR("%p: 0x%X\n", this, m_dwMode); 169 return E_ACCESSDENIED; 170 } 171 #endif 172 173 if (!pszPropName || !pvari) 174 { 175 ERR("%p: %s %p %p\n", this, debugstr_w(pszPropName), pvari, pErrorLog); 176 return E_INVALIDARG; 177 } 178 179 INT iItem = m_PropMap.FindKey(pszPropName); 180 if (iItem == -1) 181 { 182 ERR("%p: %s %p %p\n", this, debugstr_w(pszPropName), pvari, pErrorLog); 183 return E_FAIL; 184 } 185 186 HRESULT hr = ::VariantCopy(pvari, &m_PropMap.GetValueAt(iItem)); 187 if (FAILED(hr)) 188 { 189 ERR("%p: 0x%08X %p\n", this, hr, pvari); 190 return hr; 191 } 192 193 hr = ::VariantChangeTypeForRead(pvari, vt); 194 if (FAILED(hr)) 195 { 196 ERR("%p: 0x%08X %p\n", this, hr, pvari); 197 return hr; 198 } 199 200 return hr; 201 } 202 203 STDMETHODIMP 204 CMemPropertyBag::Write( 205 _In_z_ LPCWSTR pszPropName, 206 _In_ VARIANT *pvari) 207 { 208 TRACE("%p: %s %p\n", this, debugstr_w(pszPropName), pvari); 209 210 #if (_WIN32_WINNT < _WIN32_WINNT_VISTA) 211 if (!MODE_CAN_WRITE(m_dwMode)) 212 { 213 ERR("%p: 0x%X\n", this, m_dwMode); 214 return E_ACCESSDENIED; 215 } 216 #endif 217 218 if (!pszPropName || !pvari) 219 { 220 ERR("%p: %s %p\n", this, debugstr_w(pszPropName), pvari); 221 return E_INVALIDARG; 222 } 223 224 ATL::CComVariant vari; 225 HRESULT hr = vari.Copy(pvari); 226 if (FAILED(hr)) 227 { 228 ERR("%p: %s %p: 0x%08X\n", this, debugstr_w(pszPropName), pvari, hr); 229 return hr; 230 } 231 232 if (!m_PropMap.SetAt(pszPropName, vari)) 233 { 234 ERR("%p: %s %p\n", this, debugstr_w(pszPropName), pvari); 235 return E_FAIL; 236 } 237 238 return hr; 239 } 240 241 /************************************************************************** 242 * SHCreatePropertyBagOnMemory (SHLWAPI.477) 243 * 244 * Creates a property bag object on memory. 245 * 246 * @param dwMode Specifies either STGM_READ, STGM_WRITE or STGM_READWRITE. Ignored on Vista+. 247 * @param riid Specifies either IID_IUnknown, IID_IPropertyBag or IID_IPropertyBag2. 248 * Vista+ rejects IID_IPropertyBag2. 249 * @param ppvObj Receives an IPropertyBag pointer. 250 * @return An HRESULT value. S_OK on success, non-zero on failure. 251 * @see http://undoc.airesoft.co.uk/shlwapi.dll/SHCreatePropertyBagOnMemory.php 252 */ 253 EXTERN_C HRESULT WINAPI 254 SHCreatePropertyBagOnMemory(_In_ DWORD dwMode, _In_ REFIID riid, _Out_ void **ppvObj) 255 { 256 TRACE("0x%08X, %s, %p\n", dwMode, debugstr_guid(&riid), ppvObj); 257 258 *ppvObj = NULL; 259 260 CComPtr<CMemPropertyBag> pMemBag(new CMemPropertyBag(dwMode)); 261 262 return pMemBag->QueryInterface(riid, ppvObj); 263 } 264 265 class CRegPropertyBag : public CBasePropertyBag 266 { 267 protected: 268 HKEY m_hKey; 269 270 HRESULT _ReadDword(LPCWSTR pszPropName, VARIANT *pvari); 271 HRESULT _ReadString(LPCWSTR pszPropName, VARIANTARG *pvarg, UINT len); 272 HRESULT _ReadBinary(LPCWSTR pszPropName, VARIANT *pvari, VARTYPE vt, DWORD uBytes); 273 HRESULT _ReadStream(VARIANT *pvari, BYTE *pInit, UINT cbInit); 274 HRESULT _CopyStreamIntoBuff(IStream *pStream, void *pv, ULONG cb); 275 HRESULT _GetStreamSize(IStream *pStream, LPDWORD pcbSize); 276 HRESULT _WriteStream(LPCWSTR pszPropName, IStream *pStream); 277 278 public: 279 CRegPropertyBag(DWORD dwMode) 280 : CBasePropertyBag(dwMode) 281 , m_hKey(NULL) 282 { 283 } 284 285 ~CRegPropertyBag() override 286 { 287 if (m_hKey) 288 ::RegCloseKey(m_hKey); 289 } 290 291 HRESULT Init(HKEY hKey, LPCWSTR lpSubKey); 292 293 STDMETHODIMP Read(_In_z_ LPCWSTR pszPropName, _Inout_ VARIANT *pvari, 294 _Inout_opt_ IErrorLog *pErrorLog) override; 295 STDMETHODIMP Write(_In_z_ LPCWSTR pszPropName, _In_ VARIANT *pvari) override; 296 }; 297 298 HRESULT CRegPropertyBag::Init(HKEY hKey, LPCWSTR lpSubKey) 299 { 300 REGSAM nAccess = 0; 301 if (MODE_CAN_READ(m_dwMode)) 302 nAccess |= KEY_READ; 303 if (MODE_CAN_WRITE(m_dwMode)) 304 nAccess |= KEY_WRITE; 305 306 LONG error; 307 if (m_dwMode & STGM_CREATE) 308 error = ::RegCreateKeyExW(hKey, lpSubKey, 0, NULL, 0, nAccess, NULL, &m_hKey, NULL); 309 else 310 error = ::RegOpenKeyExW(hKey, lpSubKey, 0, nAccess, &m_hKey); 311 312 if (error != ERROR_SUCCESS) 313 { 314 ERR("%p %s 0x%08X\n", hKey, debugstr_w(lpSubKey), error); 315 return HRESULT_FROM_WIN32(error); 316 } 317 318 return S_OK; 319 } 320 321 HRESULT CRegPropertyBag::_ReadDword(LPCWSTR pszPropName, VARIANT *pvari) 322 { 323 DWORD cbData = sizeof(DWORD); 324 LONG error = SHGetValueW(m_hKey, NULL, pszPropName, NULL, &V_UI4(pvari), &cbData); 325 if (error) 326 return E_FAIL; 327 328 V_VT(pvari) = VT_UI4; 329 return S_OK; 330 } 331 332 HRESULT CRegPropertyBag::_ReadString(LPCWSTR pszPropName, VARIANTARG *pvarg, UINT len) 333 { 334 BSTR bstr = ::SysAllocStringByteLen(NULL, len); 335 V_BSTR(pvarg) = bstr; 336 if (!bstr) 337 return E_OUTOFMEMORY; 338 339 V_VT(pvarg) = VT_BSTR; 340 LONG error = SHGetValueW(m_hKey, NULL, pszPropName, NULL, bstr, (LPDWORD)&len); 341 if (error) 342 { 343 ::VariantClear(pvarg); 344 return E_FAIL; 345 } 346 347 return S_OK; 348 } 349 350 HRESULT CRegPropertyBag::_ReadStream(VARIANT *pvari, BYTE *pInit, UINT cbInit) 351 { 352 IStream *pStream = SHCreateMemStream(pInit, cbInit); 353 V_UNKNOWN(pvari) = pStream; 354 if (!pStream) 355 return E_OUTOFMEMORY; 356 V_VT(pvari) = VT_UNKNOWN; 357 return S_OK; 358 } 359 360 HRESULT 361 CRegPropertyBag::_ReadBinary( 362 LPCWSTR pszPropName, 363 VARIANT *pvari, 364 VARTYPE vt, 365 DWORD uBytes) 366 { 367 HRESULT hr = E_FAIL; 368 if (vt != VT_UNKNOWN || uBytes < sizeof(GUID)) 369 return hr; 370 371 LPBYTE pbData = (LPBYTE)::LocalAlloc(LMEM_ZEROINIT, uBytes); 372 if (!pbData) 373 return hr; 374 375 if (!SHGetValueW(m_hKey, NULL, pszPropName, NULL, pbData, &uBytes) && 376 memcmp(&GUID_NULL, pbData, sizeof(GUID)) == 0) 377 { 378 hr = _ReadStream(pvari, pbData + sizeof(GUID), uBytes - sizeof(GUID)); 379 } 380 381 ::LocalFree(pbData); 382 383 return hr; 384 } 385 386 HRESULT CRegPropertyBag::_CopyStreamIntoBuff(IStream *pStream, void *pv, ULONG cb) 387 { 388 LARGE_INTEGER li; 389 li.QuadPart = 0; 390 HRESULT hr = pStream->Seek(li, 0, NULL); 391 if (FAILED(hr)) 392 return hr; 393 return pStream->Read(pv, cb, NULL); 394 } 395 396 HRESULT CRegPropertyBag::_GetStreamSize(IStream *pStream, LPDWORD pcbSize) 397 { 398 *pcbSize = 0; 399 400 ULARGE_INTEGER ui; 401 HRESULT hr = IStream_Size(pStream, &ui); 402 if (FAILED(hr)) 403 return hr; 404 405 if (ui.DUMMYSTRUCTNAME.HighPart) 406 return E_FAIL; /* 64-bit value is not supported */ 407 408 *pcbSize = ui.DUMMYSTRUCTNAME.LowPart; 409 return hr; 410 } 411 412 STDMETHODIMP 413 CRegPropertyBag::Read( 414 _In_z_ LPCWSTR pszPropName, 415 _Inout_ VARIANT *pvari, 416 _Inout_opt_ IErrorLog *pErrorLog) 417 { 418 UNREFERENCED_PARAMETER(pErrorLog); 419 420 TRACE("%p: %s %p %p\n", this, debugstr_w(pszPropName), pvari, pErrorLog); 421 422 if (!MODE_CAN_READ(m_dwMode)) 423 { 424 ERR("%p: 0x%X\n", this, m_dwMode); 425 ::VariantInit(pvari); 426 return E_ACCESSDENIED; 427 } 428 429 VARTYPE vt = V_VT(pvari); 430 VariantInit(pvari); 431 432 HRESULT hr; 433 DWORD dwType, cbValue; 434 LONG error = SHGetValueW(m_hKey, NULL, pszPropName, &dwType, NULL, &cbValue); 435 if (error != ERROR_SUCCESS) 436 hr = E_FAIL; 437 else if (dwType == REG_SZ) 438 hr = _ReadString(pszPropName, pvari, cbValue); 439 else if (dwType == REG_BINARY) 440 hr = _ReadBinary(pszPropName, pvari, vt, cbValue); 441 else if (dwType == REG_DWORD) 442 hr = _ReadDword(pszPropName, pvari); 443 else 444 hr = E_FAIL; 445 446 if (FAILED(hr)) 447 { 448 ERR("%p: 0x%08X %ld: %s %p\n", this, hr, dwType, debugstr_w(pszPropName), pvari); 449 ::VariantInit(pvari); 450 return hr; 451 } 452 453 hr = ::VariantChangeTypeForRead(pvari, vt); 454 if (FAILED(hr)) 455 { 456 ERR("%p: 0x%08X %ld: %s %p\n", this, hr, dwType, debugstr_w(pszPropName), pvari); 457 ::VariantInit(pvari); 458 } 459 460 return hr; 461 } 462 463 HRESULT 464 CRegPropertyBag::_WriteStream(LPCWSTR pszPropName, IStream *pStream) 465 { 466 DWORD cbData; 467 HRESULT hr = _GetStreamSize(pStream, &cbData); 468 if (FAILED(hr) || !cbData) 469 return hr; 470 471 DWORD cbBinary = cbData + sizeof(GUID); 472 LPBYTE pbBinary = (LPBYTE)::LocalAlloc(LMEM_ZEROINIT, cbBinary); 473 if (!pbBinary) 474 return E_OUTOFMEMORY; 475 476 hr = _CopyStreamIntoBuff(pStream, pbBinary + sizeof(GUID), cbData); 477 if (SUCCEEDED(hr)) 478 { 479 if (SHSetValueW(m_hKey, NULL, pszPropName, REG_BINARY, pbBinary, cbBinary)) 480 hr = E_FAIL; 481 } 482 483 ::LocalFree(pbBinary); 484 return hr; 485 } 486 487 STDMETHODIMP 488 CRegPropertyBag::Write(_In_z_ LPCWSTR pszPropName, _In_ VARIANT *pvari) 489 { 490 TRACE("%p: %s %p\n", this, debugstr_w(pszPropName), pvari); 491 492 if (!MODE_CAN_WRITE(m_dwMode)) 493 { 494 ERR("%p: 0x%X\n", this, m_dwMode); 495 return E_ACCESSDENIED; 496 } 497 498 HRESULT hr; 499 LONG error; 500 VARIANTARG vargTemp = { 0 }; 501 switch (V_VT(pvari)) 502 { 503 case VT_EMPTY: 504 SHDeleteValueW(m_hKey, NULL, pszPropName); 505 hr = S_OK; 506 break; 507 508 case VT_BOOL: 509 case VT_I1: 510 case VT_I2: 511 case VT_I4: 512 case VT_UI1: 513 case VT_UI2: 514 case VT_UI4: 515 case VT_INT: 516 case VT_UINT: 517 { 518 hr = ::VariantChangeType(&vargTemp, pvari, 0, VT_UI4); 519 if (FAILED(hr)) 520 return hr; 521 522 error = SHSetValueW(m_hKey, NULL, pszPropName, REG_DWORD, &V_UI4(&vargTemp), sizeof(DWORD)); 523 if (error) 524 hr = E_FAIL; 525 526 ::VariantClear(&vargTemp); 527 break; 528 } 529 530 case VT_UNKNOWN: 531 { 532 CComPtr<IStream> pStream; 533 hr = V_UNKNOWN(pvari)->QueryInterface(IID_IStream, (void **)&pStream); 534 if (FAILED(hr)) 535 return hr; 536 537 hr = _WriteStream(pszPropName, pStream); 538 break; 539 } 540 541 default: 542 { 543 hr = ::VariantChangeType(&vargTemp, pvari, 0, VT_BSTR); 544 if (FAILED(hr)) 545 return hr; 546 547 int cch = lstrlenW(V_BSTR(&vargTemp)); 548 DWORD cb = (cch + 1) * sizeof(WCHAR); 549 error = SHSetValueW(m_hKey, NULL, pszPropName, REG_SZ, V_BSTR(&vargTemp), cb); 550 if (error) 551 hr = E_FAIL; 552 553 ::VariantClear(&vargTemp); 554 break; 555 } 556 } 557 558 return hr; 559 } 560 561 /************************************************************************** 562 * SHCreatePropertyBagOnRegKey (SHLWAPI.471) 563 * 564 * Creates a property bag object on registry key. 565 * 566 * @param hKey The registry key. 567 * @param pszSubKey The path of the sub-key. 568 * @param dwMode The combination of STGM_READ, STGM_WRITE, STGM_READWRITE, and STGM_CREATE. 569 * @param riid Specifies either IID_IUnknown, IID_IPropertyBag or IID_IPropertyBag2. 570 * @param ppvObj Receives an IPropertyBag pointer. 571 * @return An HRESULT value. S_OK on success, non-zero on failure. 572 * @see https://source.winehq.org/WineAPI/SHCreatePropertyBagOnRegKey.html 573 */ 574 EXTERN_C HRESULT WINAPI 575 SHCreatePropertyBagOnRegKey( 576 _In_ HKEY hKey, 577 _In_z_ LPCWSTR pszSubKey, 578 _In_ DWORD dwMode, 579 _In_ REFIID riid, 580 _Out_ void **ppvObj) 581 { 582 TRACE("%p, %s, 0x%08X, %s, %p\n", hKey, debugstr_w(pszSubKey), dwMode, 583 debugstr_guid(&riid), ppvObj); 584 585 *ppvObj = NULL; 586 587 CComPtr<CRegPropertyBag> pRegBag(new CRegPropertyBag(dwMode)); 588 589 HRESULT hr = pRegBag->Init(hKey, pszSubKey); 590 if (FAILED(hr)) 591 return hr; 592 593 return pRegBag->QueryInterface(riid, ppvObj); 594 } 595 596 /************************************************************************** 597 * SHGetIniStringW (SHLWAPI.294) 598 * 599 * @see https://source.winehq.org/WineAPI/SHGetIniStringW.html 600 */ 601 EXTERN_C DWORD WINAPI 602 SHGetIniStringW( 603 _In_z_ LPCWSTR appName, 604 _In_z_ LPCWSTR keyName, 605 _Out_writes_to_(outLen, return + 1) LPWSTR out, 606 _In_ DWORD outLen, 607 _In_z_ LPCWSTR filename) 608 { 609 TRACE("(%s,%s,%p,%08x,%s)\n", debugstr_w(appName), debugstr_w(keyName), 610 out, outLen, debugstr_w(filename)); 611 612 if (outLen == 0) 613 return 0; 614 615 // Try ".W"-appended section name. See also SHSetIniStringW 616 CStringW szSection(appName); 617 szSection += L".W"; 618 CStringW pszWideBuff; 619 const INT cchWideMax = 4 * MAX_PATH; // UTF-7 needs 4 times length buffer. 620 GetPrivateProfileStringW(szSection, keyName, NULL, 621 pszWideBuff.GetBuffer(cchWideMax), cchWideMax, filename); 622 pszWideBuff.ReleaseBuffer(); 623 624 if (pszWideBuff.IsEmpty()) // It's empty or not found 625 { 626 // Try the normal section name 627 return GetPrivateProfileStringW(appName, keyName, NULL, out, outLen, filename); 628 } 629 630 // Okay, now ".W" version is valid. Its value is a UTF-7 string in UTF-16 631 CW2A wide2utf7(pszWideBuff); 632 MultiByteToWideChar(CP_UTF7, 0, wide2utf7, -1, out, outLen); 633 out[outLen - 1] = UNICODE_NULL; 634 635 return lstrlenW(out); 636 } 637 638 static BOOL Is7BitClean(LPCWSTR psz) 639 { 640 if (!psz) 641 return TRUE; 642 643 while (*psz) 644 { 645 if (*psz > 0x7F) 646 return FALSE; 647 ++psz; 648 } 649 return TRUE; 650 } 651 652 /************************************************************************** 653 * SHSetIniStringW (SHLWAPI.295) 654 * 655 * @see https://source.winehq.org/WineAPI/SHSetIniStringW.html 656 */ 657 EXTERN_C BOOL WINAPI 658 SHSetIniStringW( 659 _In_z_ LPCWSTR appName, 660 _In_z_ LPCWSTR keyName, 661 _In_opt_z_ LPCWSTR str, 662 _In_z_ LPCWSTR filename) 663 { 664 TRACE("(%s, %p, %s, %s)\n", debugstr_w(appName), keyName, debugstr_w(str), 665 debugstr_w(filename)); 666 667 // Write a normal profile string. If str was NULL, then key will be deleted 668 if (!WritePrivateProfileStringW(appName, keyName, str, filename)) 669 return FALSE; 670 671 if (Is7BitClean(str)) 672 { 673 // Delete ".A" version 674 CStringW szSection(appName); 675 szSection += L".A"; 676 WritePrivateProfileStringW(szSection, keyName, NULL, filename); 677 678 // Delete ".W" version 679 szSection = appName; 680 szSection += L".W"; 681 WritePrivateProfileStringW(szSection, keyName, NULL, filename); 682 683 return TRUE; 684 } 685 686 // Now str is not 7-bit clean. It needs UTF-7 encoding in UTF-16. 687 // We write ".A" and ".W"-appended sections 688 CW2A wide2utf7(str, CP_UTF7); 689 CA2W utf72wide(wide2utf7, CP_ACP); 690 691 BOOL ret = TRUE; 692 693 // Write ".A" version 694 CStringW szSection(appName); 695 szSection += L".A"; 696 if (!WritePrivateProfileStringW(szSection, keyName, str, filename)) 697 ret = FALSE; 698 699 // Write ".W" version 700 szSection = appName; 701 szSection += L".W"; 702 if (!WritePrivateProfileStringW(szSection, keyName, utf72wide, filename)) 703 ret = FALSE; 704 705 return ret; 706 } 707 708 /************************************************************************** 709 * SHGetIniStringUTF7W (SHLWAPI.473) 710 * 711 * Retrieves a string value from an INI file. 712 * 713 * @param lpAppName The section name. 714 * @param lpKeyName The key name. 715 * If this string begins from '@', the value will be interpreted as UTF-7. 716 * @param lpReturnedString Receives a wide string value. 717 * @param nSize The number of characters in lpReturnedString. 718 * @param lpFileName The INI file. 719 * @return The number of characters copied to the buffer if succeeded. 720 */ 721 EXTERN_C DWORD WINAPI 722 SHGetIniStringUTF7W( 723 _In_opt_z_ LPCWSTR lpAppName, 724 _In_z_ LPCWSTR lpKeyName, 725 _Out_writes_to_(nSize, return + 1) _Post_z_ LPWSTR lpReturnedString, 726 _In_ DWORD nSize, 727 _In_z_ LPCWSTR lpFileName) 728 { 729 if (*lpKeyName == L'@') // UTF-7 730 return SHGetIniStringW(lpAppName, lpKeyName + 1, lpReturnedString, nSize, lpFileName); 731 732 return GetPrivateProfileStringW(lpAppName, lpKeyName, L"", lpReturnedString, nSize, lpFileName); 733 } 734 735 /************************************************************************** 736 * SHSetIniStringUTF7W (SHLWAPI.474) 737 * 738 * Sets a string value on an INI file. 739 * 740 * @param lpAppName The section name. 741 * @param lpKeyName The key name. 742 * If this begins from '@', the value will be stored as UTF-7. 743 * @param lpString The wide string value to be set. 744 * @param lpFileName The INI file. 745 * @return TRUE if successful. FALSE if failed. 746 */ 747 EXTERN_C BOOL WINAPI 748 SHSetIniStringUTF7W( 749 _In_z_ LPCWSTR lpAppName, 750 _In_z_ LPCWSTR lpKeyName, 751 _In_opt_z_ LPCWSTR lpString, 752 _In_z_ LPCWSTR lpFileName) 753 { 754 if (*lpKeyName == L'@') // UTF-7 755 return SHSetIniStringW(lpAppName, lpKeyName + 1, lpString, lpFileName); 756 757 return WritePrivateProfileStringW(lpAppName, lpKeyName, lpString, lpFileName); 758 } 759 760 class CIniPropertyBag : public CBasePropertyBag 761 { 762 protected: 763 LPWSTR m_pszFileName; 764 LPWSTR m_pszSection; 765 BOOL m_bAlternateStream; // ADS (Alternate Data Stream) 766 767 static BOOL LooksLikeAnAlternateStream(LPCWSTR pszStart) 768 { 769 LPCWSTR pch = StrRChrW(pszStart, NULL, L'\\'); 770 if (!pch) 771 pch = pszStart; 772 return StrChrW(pch, L':') != NULL; 773 } 774 775 HRESULT 776 _GetSectionAndName( 777 LPCWSTR pszStart, 778 LPWSTR pszSection, 779 UINT cchSectionMax, 780 LPWSTR pszName, 781 UINT cchNameMax); 782 783 public: 784 CIniPropertyBag(DWORD dwMode) 785 : CBasePropertyBag(dwMode) 786 , m_pszFileName(NULL) 787 , m_pszSection(NULL) 788 , m_bAlternateStream(FALSE) 789 { 790 } 791 792 ~CIniPropertyBag() override 793 { 794 ::LocalFree(m_pszFileName); 795 ::LocalFree(m_pszSection); 796 } 797 798 HRESULT Init(LPCWSTR pszIniFile, LPCWSTR pszSection); 799 800 STDMETHODIMP Read( 801 _In_z_ LPCWSTR pszPropName, 802 _Inout_ VARIANT *pvari, 803 _Inout_opt_ IErrorLog *pErrorLog) override; 804 805 STDMETHODIMP Write(_In_z_ LPCWSTR pszPropName, _In_ VARIANT *pvari) override; 806 }; 807 808 HRESULT CIniPropertyBag::Init(LPCWSTR pszIniFile, LPCWSTR pszSection) 809 { 810 m_pszFileName = StrDupW(pszIniFile); 811 if (!m_pszFileName) 812 return E_OUTOFMEMORY; 813 814 // Is it an ADS (Alternate Data Stream) pathname? 815 m_bAlternateStream = LooksLikeAnAlternateStream(m_pszFileName); 816 817 if (pszSection) 818 { 819 m_pszSection = StrDupW(pszSection); 820 if (!m_pszSection) 821 return E_OUTOFMEMORY; 822 } 823 824 return S_OK; 825 } 826 827 HRESULT 828 CIniPropertyBag::_GetSectionAndName( 829 LPCWSTR pszStart, 830 LPWSTR pszSection, 831 UINT cchSectionMax, 832 LPWSTR pszName, 833 UINT cchNameMax) 834 { 835 LPCWSTR pchSep = StrChrW(pszStart, L'\\'); 836 if (pchSep) 837 { 838 UINT cchSep = (UINT)(pchSep - pszStart + 1); 839 StrCpyNW(pszSection, pszStart, min(cchSep, cchSectionMax)); 840 StrCpyNW(pszName, pchSep + 1, cchNameMax); 841 return S_OK; 842 } 843 844 if (m_pszSection) 845 { 846 StrCpyNW(pszSection, m_pszSection, cchSectionMax); 847 StrCpyNW(pszName, pszStart, cchNameMax); 848 return S_OK; 849 } 850 851 ERR("%p: %s\n", this, debugstr_w(pszStart)); 852 return E_INVALIDARG; 853 } 854 855 STDMETHODIMP 856 CIniPropertyBag::Read( 857 _In_z_ LPCWSTR pszPropName, 858 _Inout_ VARIANT *pvari, 859 _Inout_opt_ IErrorLog *pErrorLog) 860 { 861 UNREFERENCED_PARAMETER(pErrorLog); 862 863 TRACE("%p: %s %p %p\n", this, debugstr_w(pszPropName), pvari, pErrorLog); 864 865 VARTYPE vt = V_VT(pvari); 866 867 ::VariantInit(pvari); 868 869 if (!MODE_CAN_READ(m_dwMode)) 870 { 871 ERR("%p: 0x%X\n", this, m_dwMode); 872 return E_ACCESSDENIED; 873 } 874 875 WCHAR szSection[64], szName[64]; 876 HRESULT hr = 877 _GetSectionAndName(pszPropName, szSection, _countof(szSection), szName, _countof(szName)); 878 if (FAILED(hr)) 879 return hr; 880 881 const INT cchBuffMax = 4 * MAX_PATH; // UTF-7 needs 4 times length buffer. 882 CComHeapPtr<WCHAR> pszBuff; 883 if (!pszBuff.Allocate(cchBuffMax * sizeof(WCHAR))) 884 return E_OUTOFMEMORY; 885 886 if (!SHGetIniStringUTF7W(szSection, szName, pszBuff, cchBuffMax, m_pszFileName)) 887 return E_FAIL; 888 889 BSTR bstr = ::SysAllocString(pszBuff); 890 V_BSTR(pvari) = bstr; 891 if (!bstr) 892 return E_OUTOFMEMORY; 893 894 V_VT(pvari) = VT_BSTR; 895 return ::VariantChangeTypeForRead(pvari, vt); 896 } 897 898 STDMETHODIMP 899 CIniPropertyBag::Write(_In_z_ LPCWSTR pszPropName, _In_ VARIANT *pvari) 900 { 901 TRACE("%p: %s %p\n", this, debugstr_w(pszPropName), pvari); 902 903 if (!MODE_CAN_WRITE(m_dwMode)) 904 { 905 ERR("%p: 0x%X\n", this, m_dwMode); 906 return E_ACCESSDENIED; 907 } 908 909 HRESULT hr; 910 BSTR bstr; 911 VARIANTARG vargTemp = { 0 }; 912 switch (V_VT(pvari)) 913 { 914 case VT_EMPTY: 915 bstr = NULL; 916 break; 917 918 case VT_BSTR: 919 bstr = V_BSTR(pvari); 920 break; 921 922 default: 923 hr = ::VariantChangeType(&vargTemp, pvari, 0, VT_BSTR); 924 if (FAILED(hr)) 925 goto Quit; 926 927 bstr = V_BSTR(&vargTemp); 928 break; 929 } 930 931 WCHAR szSection[64], szName[64]; 932 hr = _GetSectionAndName(pszPropName, szSection, _countof(szSection), szName, _countof(szName)); 933 if (SUCCEEDED(hr)) 934 { 935 if (SHSetIniStringUTF7W(szSection, szName, bstr, m_pszFileName)) 936 { 937 if (!m_bAlternateStream) 938 SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATHW, m_pszFileName, NULL); 939 } 940 else 941 { 942 hr = E_FAIL; 943 } 944 } 945 946 Quit: 947 ::VariantClear(&vargTemp); 948 return hr; 949 } 950 951 /************************************************************************** 952 * SHCreatePropertyBagOnProfileSection (SHLWAPI.472) 953 * 954 * Creates a property bag object on INI file. 955 * 956 * @param lpFileName The INI filename. 957 * @param pszSection The optional section name. 958 * @param dwMode The combination of STGM_READ, STGM_WRITE, STGM_READWRITE, and STGM_CREATE. 959 * @param riid Specifies either IID_IUnknown, IID_IPropertyBag or IID_IPropertyBag2. 960 * @param ppvObj Receives an IPropertyBag pointer. 961 * @return An HRESULT value. S_OK on success, non-zero on failure. 962 * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/createonprofilesection.htm 963 */ 964 EXTERN_C HRESULT WINAPI 965 SHCreatePropertyBagOnProfileSection( 966 _In_z_ LPCWSTR lpFileName, 967 _In_opt_z_ LPCWSTR pszSection, 968 _In_ DWORD dwMode, 969 _In_ REFIID riid, 970 _Out_ void **ppvObj) 971 { 972 HANDLE hFile; 973 PWCHAR pchFileTitle; 974 WCHAR szBuff[MAX_PATH]; 975 976 if (dwMode & STGM_CREATE) 977 { 978 hFile = ::CreateFileW(lpFileName, 0, FILE_SHARE_DELETE, 0, CREATE_NEW, 979 FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM, NULL); 980 if (hFile != INVALID_HANDLE_VALUE) 981 { 982 pchFileTitle = PathFindFileNameW(lpFileName); 983 if (lstrcmpiW(pchFileTitle, L"desktop.ini") == 0) 984 { 985 StrCpyNW(szBuff, lpFileName, _countof(szBuff)); 986 if (PathRemoveFileSpecW(szBuff)) 987 PathMakeSystemFolderW(szBuff); 988 } 989 ::CloseHandle(hFile); 990 } 991 } 992 993 *ppvObj = NULL; 994 995 if (!PathFileExistsW(lpFileName)) 996 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); 997 998 CComPtr<CIniPropertyBag> pIniPB(new CIniPropertyBag(dwMode)); 999 1000 HRESULT hr = pIniPB->Init(lpFileName, pszSection); 1001 if (FAILED(hr)) 1002 { 1003 ERR("0x%08X\n", hr); 1004 return hr; 1005 } 1006 1007 return pIniPB->QueryInterface(riid, ppvObj); 1008 } 1009 1010 class CDesktopUpgradePropertyBag : public CBasePropertyBag 1011 { 1012 protected: 1013 BOOL _AlreadyUpgraded(HKEY hKey); 1014 VOID _MarkAsUpgraded(HKEY hkey); 1015 HRESULT _ReadFlags(VARIANT *pvari); 1016 HRESULT _ReadItemPositions(VARIANT *pvari); 1017 IStream* _GetOldDesktopViewStream(); 1018 IStream* _NewStreamFromOld(IStream *pOldStream); 1019 1020 public: 1021 CDesktopUpgradePropertyBag() : CBasePropertyBag(STGM_READ) { } 1022 1023 STDMETHODIMP Read( 1024 _In_z_ LPCWSTR pszPropName, 1025 _Inout_ VARIANT *pvari, 1026 _Inout_opt_ IErrorLog *pErrorLog) override; 1027 1028 STDMETHODIMP Write(_In_z_ LPCWSTR pszPropName, _In_ VARIANT *pvari) override 1029 { 1030 ERR("%p: %s: Read-only\n", this, debugstr_w(pszPropName)); 1031 return E_NOTIMPL; 1032 } 1033 }; 1034 1035 VOID CDesktopUpgradePropertyBag::_MarkAsUpgraded(HKEY hkey) 1036 { 1037 DWORD dwValue = TRUE; 1038 SHSetValueW(hkey, NULL, L"Upgrade", REG_DWORD, &dwValue, sizeof(dwValue)); 1039 } 1040 1041 BOOL CDesktopUpgradePropertyBag::_AlreadyUpgraded(HKEY hKey) 1042 { 1043 // Check the existence of the value written in _MarkAsUpgraded. 1044 DWORD dwValue, cbData = sizeof(dwValue); 1045 return SHGetValueW(hKey, NULL, L"Upgrade", NULL, &dwValue, &cbData) == ERROR_SUCCESS; 1046 } 1047 1048 typedef DWORDLONG DESKVIEW_FLAGS; // 64-bit data 1049 1050 HRESULT CDesktopUpgradePropertyBag::_ReadFlags(VARIANT *pvari) 1051 { 1052 DESKVIEW_FLAGS Flags; 1053 DWORD cbValue = sizeof(Flags); 1054 if (SHGetValueW(HKEY_CURRENT_USER, 1055 L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\DeskView", 1056 L"Settings", 1057 NULL, 1058 &Flags, 1059 &cbValue) != ERROR_SUCCESS || cbValue < sizeof(Flags)) 1060 { 1061 return E_FAIL; 1062 } 1063 1064 V_UINT(pvari) = ((UINT)(Flags >> 32)) | 0x220; // FIXME: Magic number 1065 V_VT(pvari) = VT_UINT; 1066 return S_OK; 1067 } 1068 1069 typedef struct tagOLD_STREAM_HEADER 1070 { 1071 WORD wMagic; 1072 WORD awUnknown[6]; 1073 WORD wSize; 1074 } OLD_STREAM_HEADER, *POLD_STREAM_HEADER; 1075 1076 IStream* CDesktopUpgradePropertyBag::_NewStreamFromOld(IStream *pOldStream) 1077 { 1078 OLD_STREAM_HEADER Header; 1079 HRESULT hr = pOldStream->Read(&Header, sizeof(Header), NULL); 1080 if (FAILED(hr) || Header.wMagic != 28) 1081 return NULL; 1082 1083 // Move stream pointer 1084 LARGE_INTEGER li; 1085 li.QuadPart = Header.wSize - sizeof(Header); 1086 hr = pOldStream->Seek(li, STREAM_SEEK_CUR, NULL); 1087 if (FAILED(hr)) 1088 return NULL; 1089 1090 // Get the size 1091 ULARGE_INTEGER uli; 1092 hr = IStream_Size(pOldStream, &uli); 1093 if (FAILED(hr)) 1094 return NULL; 1095 1096 // Create new stream and attach 1097 CComPtr<IStream> pNewStream; 1098 pNewStream.Attach(SHCreateMemStream(NULL, 0)); 1099 if (!pNewStream) 1100 return NULL; 1101 1102 // Subtract Header.wSize from the size 1103 uli.QuadPart -= Header.wSize; 1104 1105 // Copy to pNewStream 1106 hr = pOldStream->CopyTo(pNewStream, uli, NULL, NULL); 1107 if (FAILED(hr)) 1108 return NULL; 1109 1110 li.QuadPart = 0; 1111 pNewStream->Seek(li, STREAM_SEEK_SET, NULL); 1112 1113 return pNewStream.Detach(); 1114 } 1115 1116 IStream* CDesktopUpgradePropertyBag::_GetOldDesktopViewStream() 1117 { 1118 HKEY hKey = SHGetShellKey(SHKEY_Root_HKCU, L"Streams\\Desktop", FALSE); 1119 if (!hKey) 1120 return NULL; 1121 1122 CComPtr<IStream> pOldStream; 1123 if (!_AlreadyUpgraded(hKey)) 1124 { 1125 pOldStream.Attach(SHOpenRegStream2W(hKey, NULL, L"ViewView2", 0)); 1126 if (pOldStream) 1127 { 1128 ULARGE_INTEGER uli; 1129 HRESULT hr = IStream_Size(pOldStream, &uli); 1130 if (SUCCEEDED(hr) && !uli.QuadPart) 1131 pOldStream.Release(); 1132 } 1133 1134 if (!pOldStream) 1135 pOldStream.Attach(SHOpenRegStream2W(hKey, NULL, L"ViewView", 0)); 1136 1137 _MarkAsUpgraded(hKey); 1138 } 1139 1140 ::RegCloseKey(hKey); 1141 return pOldStream.Detach(); 1142 } 1143 1144 HRESULT CDesktopUpgradePropertyBag::_ReadItemPositions(VARIANT *pvari) 1145 { 1146 CComPtr<IStream> pOldStream; 1147 pOldStream.Attach(_GetOldDesktopViewStream()); 1148 if (!pOldStream) 1149 return E_FAIL; 1150 1151 HRESULT hr = E_FAIL; 1152 IStream *pNewStream = _NewStreamFromOld(pOldStream); 1153 if (pNewStream) 1154 { 1155 V_UNKNOWN(pvari) = pNewStream; 1156 V_VT(pvari) = VT_UNKNOWN; 1157 hr = S_OK; 1158 } 1159 1160 return hr; 1161 } 1162 1163 STDMETHODIMP 1164 CDesktopUpgradePropertyBag::Read( 1165 _In_z_ LPCWSTR pszPropName, 1166 _Inout_ VARIANT *pvari, 1167 _Inout_opt_ IErrorLog *pErrorLog) 1168 { 1169 UNREFERENCED_PARAMETER(pErrorLog); 1170 1171 VARTYPE vt = V_VT(pvari); 1172 1173 HRESULT hr = E_FAIL; 1174 if (StrCmpW(L"FFlags", pszPropName) == 0) 1175 hr = _ReadFlags(pvari); 1176 else if (StrCmpNW(L"ItemPos", pszPropName, 7) == 0) 1177 hr = _ReadItemPositions(pvari); 1178 1179 if (FAILED(hr)) 1180 { 1181 ::VariantInit(pvari); 1182 return hr; 1183 } 1184 1185 return ::VariantChangeType(pvari, pvari, 0, vt); 1186 } 1187 1188 /************************************************************************** 1189 * SHGetDesktopUpgradePropertyBag (Internal) 1190 * 1191 * Creates or gets a property bag object for desktop upgrade 1192 * 1193 * @param riid Specifies either IID_IUnknown, IID_IPropertyBag or IID_IPropertyBag2. 1194 * @param ppvObj Receives an IPropertyBag pointer. 1195 * @return An HRESULT value. S_OK on success, non-zero on failure. 1196 */ 1197 HRESULT SHGetDesktopUpgradePropertyBag(REFIID riid, void **ppvObj) 1198 { 1199 *ppvObj = NULL; 1200 CComPtr<CDesktopUpgradePropertyBag> pPropBag(new CDesktopUpgradePropertyBag()); 1201 return pPropBag->QueryInterface(riid, ppvObj); 1202 } 1203 1204 class CViewStatePropertyBag : public CBasePropertyBag 1205 { 1206 protected: 1207 LPITEMIDLIST m_pidl = NULL; 1208 LPWSTR m_pszPath = NULL; 1209 DWORD m_dwVspbFlags = 0; // SHGVSPB_... flags 1210 CComPtr<IPropertyBag> m_pPidlBag; 1211 CComPtr<IPropertyBag> m_pUpgradeBag; 1212 CComPtr<IPropertyBag> m_pInheritBag; 1213 CComPtr<IPropertyBag> m_pUserDefaultsBag; 1214 CComPtr<IPropertyBag> m_pFolderDefaultsBag; 1215 CComPtr<IPropertyBag> m_pGlobalDefaultsBag; 1216 CComPtr<IPropertyBag> m_pReadBag; 1217 CComPtr<IPropertyBag> m_pWriteBag; 1218 BOOL m_bPidlBag = FALSE; 1219 BOOL m_bUpgradeBag = FALSE; 1220 BOOL m_bInheritBag = FALSE; 1221 BOOL m_bUserDefaultsBag = FALSE; 1222 BOOL m_bFolderDefaultsBag = FALSE; 1223 BOOL m_bGlobalDefaultsBag = FALSE; 1224 BOOL m_bReadBag = FALSE; 1225 BOOL m_bWriteBag = FALSE; 1226 1227 BOOL _IsSamePidl(LPCITEMIDLIST pidlOther) const; 1228 BOOL _IsSystemFolder() const; 1229 BOOL _CanAccessPidlBag() const; 1230 BOOL _CanAccessUserDefaultsBag() const; 1231 BOOL _CanAccessFolderDefaultsBag() const; 1232 BOOL _CanAccessGlobalDefaultsBag() const; 1233 BOOL _CanAccessInheritBag() const; 1234 BOOL _CanAccessUpgradeBag() const; 1235 1236 HKEY _GetHKey(DWORD dwVspbFlags); 1237 1238 UINT _GetMRUSize(HKEY hKey); 1239 1240 HRESULT _GetMRUSlots( 1241 LPCITEMIDLIST pidl, 1242 DWORD dwMode, 1243 HKEY hKey, 1244 UINT *puSlots, 1245 UINT cSlots, 1246 UINT *pcSlots); 1247 1248 HRESULT _GetMRUSlot(LPCITEMIDLIST pidl, DWORD dwMode, HKEY hKey, UINT *pSlot); 1249 1250 HRESULT _GetRegKey( 1251 LPCITEMIDLIST pidl, 1252 LPCWSTR pszBagName, 1253 DWORD dwFlags, 1254 DWORD dwMode, 1255 HKEY hKey, 1256 LPWSTR pszDest, 1257 INT cchDest); 1258 1259 HRESULT _CreateBag( 1260 LPITEMIDLIST pidl, 1261 LPCWSTR pszPath, 1262 DWORD dwVspbFlags, 1263 DWORD dwMode, 1264 REFIID riid, 1265 IPropertyBag **pppb); 1266 1267 HRESULT _FindNearestInheritBag(REFIID riid, IPropertyBag **pppb); 1268 1269 void _ResetTryAgainFlag(); 1270 1271 BOOL _EnsureReadBag(DWORD dwMode, REFIID riid); 1272 BOOL _EnsurePidlBag(DWORD dwMode, REFIID riid); 1273 BOOL _EnsureInheritBag(DWORD dwMode, REFIID riid); 1274 BOOL _EnsureUpgradeBag(DWORD dwMode, REFIID riid); 1275 BOOL _EnsureUserDefaultsBag(DWORD dwMode, REFIID riid); 1276 BOOL _EnsureFolderDefaultsBag(DWORD dwMode, REFIID riid); 1277 BOOL _EnsureGlobalDefaultsBag(DWORD dwMode, REFIID riid); 1278 BOOL _EnsureWriteBag(DWORD dwMode, REFIID riid); 1279 HRESULT _ReadPidlBag(LPCWSTR pszPropName, VARIANT *pvari, IErrorLog *pErrorLog); 1280 HRESULT _ReadInheritBag(LPCWSTR pszPropName, VARIANT *pvari, IErrorLog *pErrorLog); 1281 HRESULT _ReadUpgradeBag(LPCWSTR pszPropName, VARIANT *pvari, IErrorLog *pErrorLog); 1282 HRESULT _ReadUserDefaultsBag(LPCWSTR pszPropName, VARIANT *pvari, IErrorLog *pErrorLog); 1283 HRESULT _ReadFolderDefaultsBag(LPCWSTR pszPropName, VARIANT *pvari, IErrorLog *pErrorLog); 1284 HRESULT _ReadGlobalDefaultsBag(LPCWSTR pszPropName, VARIANT *pvari, IErrorLog *pErrorLog); 1285 void _PruneMRUTree(); 1286 1287 public: 1288 CViewStatePropertyBag() : CBasePropertyBag(STGM_READ) { } 1289 1290 ~CViewStatePropertyBag() override 1291 { 1292 ::ILFree(m_pidl); 1293 ::LocalFree(m_pszPath); 1294 } 1295 1296 HRESULT Init(_In_opt_ LPCITEMIDLIST pidl, _In_opt_ LPCWSTR pszPath, _In_ DWORD dwVspbFlags); 1297 BOOL IsSameBag(LPCITEMIDLIST pidl, LPCWSTR pszPath, DWORD dwVspbFlags) const; 1298 1299 STDMETHODIMP Read( 1300 _In_z_ LPCWSTR pszPropName, 1301 _Inout_ VARIANT *pvari, 1302 _Inout_opt_ IErrorLog *pErrorLog) override; 1303 1304 STDMETHODIMP Write(_In_z_ LPCWSTR pszPropName, _In_ VARIANT *pvari) override; 1305 }; 1306 1307 // CViewStatePropertyBag is cached 1308 CComPtr<CViewStatePropertyBag> g_pCachedBag; 1309 extern "C" 1310 { 1311 CRITICAL_SECTION g_csBagCacheLock; 1312 } 1313 1314 HRESULT 1315 CViewStatePropertyBag::Init( 1316 _In_opt_ LPCITEMIDLIST pidl, 1317 _In_opt_ LPCWSTR pszPath, 1318 _In_ DWORD dwVspbFlags) 1319 { 1320 if (pidl) 1321 { 1322 m_pidl = ILClone(pidl); 1323 if (!m_pidl) 1324 return E_OUTOFMEMORY; 1325 } 1326 1327 if (pszPath) 1328 { 1329 m_pszPath = StrDupW(pszPath); 1330 if (!m_pszPath) 1331 return E_OUTOFMEMORY; 1332 1333 m_dwVspbFlags = dwVspbFlags; 1334 } 1335 1336 return S_OK; 1337 } 1338 1339 BOOL CViewStatePropertyBag::_IsSamePidl(LPCITEMIDLIST pidlOther) const 1340 { 1341 if (!pidlOther && !m_pidl) 1342 return TRUE; 1343 1344 return (pidlOther && m_pidl && ILIsEqual(pidlOther, m_pidl)); 1345 } 1346 1347 BOOL CViewStatePropertyBag::IsSameBag(LPCITEMIDLIST pidl, LPCWSTR pszPath, DWORD dwVspbFlags) const 1348 { 1349 return (dwVspbFlags == m_dwVspbFlags && StrCmpW(pszPath, m_pszPath) == 0 && _IsSamePidl(pidl)); 1350 } 1351 1352 BOOL CViewStatePropertyBag::_IsSystemFolder() const 1353 { 1354 LPCITEMIDLIST ppidlLast; 1355 CComPtr<IShellFolder> psf; 1356 1357 HRESULT hr = SHBindToParent(m_pidl, IID_IShellFolder, (void **)&psf, &ppidlLast); 1358 if (FAILED(hr)) 1359 return FALSE; 1360 1361 WIN32_FIND_DATAW FindData; 1362 hr = SHGetDataFromIDListW(psf, ppidlLast, SHGDFIL_FINDDATA, &FindData, sizeof(FindData)); 1363 if (FAILED(hr)) 1364 return FALSE; 1365 1366 return PathIsSystemFolderW(NULL, FindData.dwFileAttributes); 1367 } 1368 1369 BOOL CViewStatePropertyBag::_CanAccessPidlBag() const 1370 { 1371 return ((m_dwVspbFlags & SHGVSPB_FOLDER) == SHGVSPB_FOLDER); 1372 } 1373 1374 BOOL CViewStatePropertyBag::_CanAccessUserDefaultsBag() const 1375 { 1376 if (_CanAccessPidlBag()) 1377 return TRUE; 1378 1379 return ((m_dwVspbFlags & SHGVSPB_USERDEFAULTS) == SHGVSPB_USERDEFAULTS); 1380 } 1381 1382 BOOL CViewStatePropertyBag::_CanAccessFolderDefaultsBag() const 1383 { 1384 if (_CanAccessUserDefaultsBag()) 1385 return TRUE; 1386 1387 return ((m_dwVspbFlags & SHGVSPB_ALLUSERS) && (m_dwVspbFlags & SHGVSPB_PERFOLDER)); 1388 } 1389 1390 BOOL CViewStatePropertyBag::_CanAccessGlobalDefaultsBag() const 1391 { 1392 if (_CanAccessFolderDefaultsBag()) 1393 return TRUE; 1394 1395 return ((m_dwVspbFlags & SHGVSPB_GLOBALDEAFAULTS) == SHGVSPB_GLOBALDEAFAULTS); 1396 } 1397 1398 BOOL CViewStatePropertyBag::_CanAccessInheritBag() const 1399 { 1400 return (_CanAccessPidlBag() || (m_dwVspbFlags & SHGVSPB_INHERIT)); 1401 } 1402 1403 BOOL CViewStatePropertyBag::_CanAccessUpgradeBag() const 1404 { 1405 return StrCmpW(m_pszPath, L"Desktop") == 0; 1406 } 1407 1408 void CViewStatePropertyBag::_ResetTryAgainFlag() 1409 { 1410 if (m_dwVspbFlags & SHGVSPB_NOAUTODEFAULTS) 1411 m_bReadBag = FALSE; 1412 else if ((m_dwVspbFlags & SHGVSPB_FOLDER) == SHGVSPB_FOLDER) 1413 m_bPidlBag = FALSE; 1414 else if (m_dwVspbFlags & SHGVSPB_INHERIT) 1415 m_bInheritBag = FALSE; 1416 else if ((m_dwVspbFlags & SHGVSPB_USERDEFAULTS) == SHGVSPB_USERDEFAULTS) 1417 m_bUserDefaultsBag = FALSE; 1418 else if ((m_dwVspbFlags & SHGVSPB_ALLUSERS) && (m_dwVspbFlags & SHGVSPB_PERFOLDER)) 1419 m_bFolderDefaultsBag = FALSE; 1420 else if ((m_dwVspbFlags & SHGVSPB_GLOBALDEAFAULTS) == SHGVSPB_GLOBALDEAFAULTS) 1421 m_bGlobalDefaultsBag = FALSE; 1422 } 1423 1424 HKEY CViewStatePropertyBag::_GetHKey(DWORD dwVspbFlags) 1425 { 1426 if (!(dwVspbFlags & (SHGVSPB_INHERIT | SHGVSPB_PERUSER))) 1427 return SHGetShellKey((SHKEY_Key_Shell | SHKEY_Root_HKLM), NULL, TRUE); 1428 1429 if ((m_dwVspbFlags & SHGVSPB_ROAM) && (dwVspbFlags & SHGVSPB_PERFOLDER)) 1430 return SHGetShellKey((SHKEY_Key_Shell | SHKEY_Root_HKCU), NULL, TRUE); 1431 1432 return SHGetShellKey(SHKEY_Key_ShellNoRoam | SHKEY_Root_HKCU, NULL, TRUE); 1433 } 1434 1435 UINT CViewStatePropertyBag::_GetMRUSize(HKEY hKey) 1436 { 1437 DWORD dwValue, cbValue = sizeof(dwValue); 1438 1439 if (SHGetValueW(hKey, NULL, L"BagMRU Size", NULL, &dwValue, &cbValue) != ERROR_SUCCESS) 1440 return 400; // The default size of the MRU (most recently used) list 1441 1442 return (UINT)dwValue; 1443 } 1444 1445 HRESULT 1446 CViewStatePropertyBag::_GetMRUSlots( 1447 LPCITEMIDLIST pidl, 1448 DWORD dwMode, 1449 HKEY hKey, 1450 UINT *puSlots, 1451 UINT cSlots, 1452 UINT *pcSlots) 1453 { 1454 CComPtr<IMruPidlList> pMruList; 1455 HRESULT hr = ::CoCreateInstance(CLSID_MruPidlList, NULL, CLSCTX_INPROC_SERVER, 1456 IID_IMruPidlList, (void**)&pMruList); 1457 if (FAILED(hr)) 1458 return hr; 1459 1460 UINT cMRUSize = _GetMRUSize(hKey); 1461 hr = pMruList->InitList(cMRUSize, hKey, L"BagMRU"); 1462 if (FAILED(hr)) 1463 return hr; 1464 1465 hr = pMruList->QueryPidl(pidl, cSlots, puSlots, pcSlots); 1466 if (hr == S_OK || MODE_CAN_WRITE(dwMode)) // FIXME: HACK! (Without this, a new pidl can never be saved) 1467 hr = pMruList->UsePidl(pidl, puSlots); 1468 else if (cSlots == 1) 1469 hr = E_FAIL; 1470 1471 return hr; 1472 } 1473 1474 HRESULT 1475 CViewStatePropertyBag::_GetMRUSlot(LPCITEMIDLIST pidl, DWORD dwMode, HKEY hKey, UINT *pSlot) 1476 { 1477 UINT cSlots; 1478 return _GetMRUSlots(pidl, dwMode, hKey, pSlot, 1, &cSlots); 1479 } 1480 1481 HRESULT 1482 CViewStatePropertyBag::_GetRegKey( 1483 LPCITEMIDLIST pidl, 1484 LPCWSTR pszBagName, 1485 DWORD dwFlags, 1486 DWORD dwMode, 1487 HKEY hKey, 1488 LPWSTR pszDest, 1489 INT cchDest) 1490 { 1491 HRESULT hr = S_OK; 1492 UINT nSlot; 1493 1494 if (dwFlags & (SHGVSPB_INHERIT | SHGVSPB_PERFOLDER)) 1495 { 1496 hr = _GetMRUSlot(pidl, dwMode, hKey, &nSlot); 1497 if (SUCCEEDED(hr)) 1498 { 1499 if (dwFlags & SHGVSPB_INHERIT) 1500 StringCchPrintfW(pszDest, cchDest, L"Bags\\%d\\%s\\Inherit", nSlot, pszBagName); 1501 else 1502 StringCchPrintfW(pszDest, cchDest, L"Bags\\%d\\%s", nSlot, pszBagName); 1503 } 1504 } 1505 else 1506 { 1507 StringCchPrintfW(pszDest, cchDest, L"Bags\\AllFolders\\%s", pszBagName); 1508 } 1509 1510 return hr; 1511 } 1512 1513 static HRESULT BindCtx_CreateWithMode(DWORD dwMode, IBindCtx **ppbc) 1514 { 1515 HRESULT hr = ::CreateBindCtx(0, ppbc); 1516 if (FAILED(hr)) 1517 return hr; 1518 1519 IBindCtx *pbc = *ppbc; 1520 1521 BIND_OPTS opts = { sizeof(opts) }; 1522 opts.grfMode = dwMode; 1523 hr = pbc->SetBindOptions(&opts); 1524 if (FAILED(hr)) 1525 { 1526 pbc->Release(); 1527 *ppbc = NULL; 1528 } 1529 1530 return hr; 1531 } 1532 1533 HRESULT 1534 CViewStatePropertyBag::_CreateBag( 1535 LPITEMIDLIST pidl, 1536 LPCWSTR pszPath, 1537 DWORD dwVspbFlags, 1538 DWORD dwMode, 1539 REFIID riid, 1540 IPropertyBag **pppb) 1541 { 1542 HRESULT hr; 1543 HKEY hKey; 1544 CComPtr<IBindCtx> pBC; 1545 CComPtr<IShellFolder> psf; 1546 WCHAR szBuff[64]; 1547 1548 if (MODE_CAN_WRITE(dwMode)) 1549 dwMode |= STGM_CREATE; 1550 1551 if ((dwVspbFlags & SHGVSPB_ALLUSERS) && (dwVspbFlags & SHGVSPB_PERFOLDER)) 1552 { 1553 hr = BindCtx_CreateWithMode(dwMode, &pBC); 1554 if (SUCCEEDED(hr)) 1555 { 1556 hr = SHGetDesktopFolder(&psf); 1557 if (SUCCEEDED(hr)) 1558 { 1559 hr = psf->BindToObject(m_pidl, pBC, riid, (void **)pppb); 1560 if (SUCCEEDED(hr) && !*pppb) 1561 hr = E_FAIL; 1562 } 1563 } 1564 } 1565 else 1566 { 1567 hKey = _GetHKey(dwVspbFlags); 1568 if (!hKey) 1569 return E_FAIL; 1570 1571 hr = _GetRegKey(pidl, pszPath, dwVspbFlags, dwMode, hKey, szBuff, _countof(szBuff)); 1572 if (SUCCEEDED(hr)) 1573 hr = SHCreatePropertyBagOnRegKey(hKey, szBuff, dwMode, riid, (void**)pppb); 1574 1575 ::RegCloseKey(hKey); 1576 } 1577 1578 return hr; 1579 } 1580 1581 HRESULT 1582 CViewStatePropertyBag::_FindNearestInheritBag(REFIID riid, IPropertyBag **pppb) 1583 { 1584 *pppb = NULL; 1585 1586 HKEY hKey = _GetHKey(SHGVSPB_INHERIT); 1587 if (!hKey) 1588 return E_FAIL; 1589 1590 UINT cSlots, anSlots[64]; 1591 if (FAILED(_GetMRUSlots(m_pidl, 0, hKey, anSlots, _countof(anSlots), &cSlots)) || !cSlots) 1592 { 1593 ::RegCloseKey(hKey); 1594 return E_FAIL; 1595 } 1596 1597 HRESULT hr = E_FAIL; 1598 WCHAR szBuff[64]; 1599 for (UINT iSlot = 0; iSlot < cSlots; ++iSlot) 1600 { 1601 StringCchPrintfW(szBuff, _countof(szBuff), L"Bags\\%d\\%s\\Inherit", anSlots[iSlot], 1602 m_pszPath); 1603 hr = SHCreatePropertyBagOnRegKey(hKey, szBuff, STGM_READ, riid, (void**)pppb); 1604 if (SUCCEEDED(hr)) 1605 break; 1606 } 1607 1608 ::RegCloseKey(hKey); 1609 return hr; 1610 } 1611 1612 BOOL CViewStatePropertyBag::_EnsureReadBag(DWORD dwMode, REFIID riid) 1613 { 1614 if (!m_pReadBag && !m_bReadBag) 1615 { 1616 m_bReadBag = TRUE; 1617 _CreateBag(m_pidl, m_pszPath, m_dwVspbFlags, dwMode, riid, &m_pReadBag); 1618 } 1619 return (m_pReadBag != NULL); 1620 } 1621 1622 BOOL CViewStatePropertyBag::_EnsurePidlBag(DWORD dwMode, REFIID riid) 1623 { 1624 if (!m_pPidlBag && !m_bPidlBag && _CanAccessPidlBag()) 1625 { 1626 m_bPidlBag = TRUE; 1627 _CreateBag(m_pidl, m_pszPath, SHGVSPB_FOLDER, dwMode, riid, &m_pPidlBag); 1628 } 1629 return (m_pPidlBag != NULL); 1630 } 1631 1632 BOOL CViewStatePropertyBag::_EnsureInheritBag(DWORD dwMode, REFIID riid) 1633 { 1634 if (!m_pInheritBag && !m_bInheritBag && _CanAccessInheritBag()) 1635 { 1636 m_bInheritBag = TRUE; 1637 _FindNearestInheritBag(riid, &m_pInheritBag); 1638 } 1639 return (m_pInheritBag != NULL); 1640 } 1641 1642 BOOL CViewStatePropertyBag::_EnsureUpgradeBag(DWORD dwMode, REFIID riid) 1643 { 1644 if (!m_pUpgradeBag && !m_bUpgradeBag && _CanAccessUpgradeBag()) 1645 { 1646 m_bUpgradeBag = TRUE; 1647 SHGetDesktopUpgradePropertyBag(riid, (void**)&m_pUpgradeBag); 1648 } 1649 return (m_pUpgradeBag != NULL); 1650 } 1651 1652 BOOL CViewStatePropertyBag::_EnsureUserDefaultsBag(DWORD dwMode, REFIID riid) 1653 { 1654 if (!m_pUserDefaultsBag && !m_bUserDefaultsBag && _CanAccessUserDefaultsBag()) 1655 { 1656 m_bUserDefaultsBag = TRUE; 1657 _CreateBag(NULL, m_pszPath, SHGVSPB_USERDEFAULTS, dwMode, riid, &m_pUserDefaultsBag); 1658 } 1659 return (m_pUserDefaultsBag != NULL); 1660 } 1661 1662 BOOL CViewStatePropertyBag::_EnsureFolderDefaultsBag(DWORD dwMode, REFIID riid) 1663 { 1664 if (!m_pFolderDefaultsBag && !m_bFolderDefaultsBag && _CanAccessFolderDefaultsBag()) 1665 { 1666 m_bFolderDefaultsBag = TRUE; 1667 if (_IsSystemFolder()) 1668 { 1669 _CreateBag(m_pidl, m_pszPath, SHGVSPB_PERFOLDER | SHGVSPB_ALLUSERS, 1670 dwMode, riid, &m_pFolderDefaultsBag); 1671 } 1672 } 1673 return (m_pFolderDefaultsBag != NULL); 1674 } 1675 1676 BOOL CViewStatePropertyBag::_EnsureGlobalDefaultsBag(DWORD dwMode, REFIID riid) 1677 { 1678 if (!m_pGlobalDefaultsBag && !m_bGlobalDefaultsBag && _CanAccessGlobalDefaultsBag()) 1679 { 1680 m_bGlobalDefaultsBag = TRUE; 1681 _CreateBag(NULL, m_pszPath, SHGVSPB_GLOBALDEAFAULTS, dwMode, riid, &m_pGlobalDefaultsBag); 1682 } 1683 return (m_pGlobalDefaultsBag != NULL); 1684 } 1685 1686 HRESULT 1687 CViewStatePropertyBag::_ReadPidlBag( 1688 LPCWSTR pszPropName, 1689 VARIANT *pvari, 1690 IErrorLog *pErrorLog) 1691 { 1692 if (!_EnsurePidlBag(STGM_READ, IID_IPropertyBag)) 1693 return E_FAIL; 1694 1695 return m_pPidlBag->Read(pszPropName, pvari, pErrorLog); 1696 } 1697 1698 HRESULT 1699 CViewStatePropertyBag::_ReadInheritBag( 1700 LPCWSTR pszPropName, 1701 VARIANT *pvari, 1702 IErrorLog *pErrorLog) 1703 { 1704 if (!_EnsureInheritBag(STGM_READ, IID_IPropertyBag)) 1705 return E_FAIL; 1706 1707 return m_pInheritBag->Read(pszPropName, pvari, pErrorLog); 1708 } 1709 1710 HRESULT 1711 CViewStatePropertyBag::_ReadUpgradeBag( 1712 LPCWSTR pszPropName, 1713 VARIANT *pvari, 1714 IErrorLog *pErrorLog) 1715 { 1716 if (!_EnsureUpgradeBag(STGM_READ, IID_IPropertyBag)) 1717 return E_FAIL; 1718 1719 return m_pUpgradeBag->Read(pszPropName, pvari, pErrorLog); 1720 } 1721 1722 HRESULT 1723 CViewStatePropertyBag::_ReadUserDefaultsBag( 1724 LPCWSTR pszPropName, 1725 VARIANT *pvari, 1726 IErrorLog *pErrorLog) 1727 { 1728 if (!_EnsureUserDefaultsBag(STGM_READ, IID_IPropertyBag)) 1729 return E_FAIL; 1730 1731 return m_pUserDefaultsBag->Read(pszPropName, pvari, pErrorLog); 1732 } 1733 1734 HRESULT 1735 CViewStatePropertyBag::_ReadFolderDefaultsBag( 1736 LPCWSTR pszPropName, 1737 VARIANT *pvari, 1738 IErrorLog *pErrorLog) 1739 { 1740 if (!_EnsureFolderDefaultsBag(STGM_READ, IID_IPropertyBag)) 1741 return E_FAIL; 1742 1743 return m_pFolderDefaultsBag->Read(pszPropName, pvari, pErrorLog); 1744 } 1745 1746 HRESULT 1747 CViewStatePropertyBag::_ReadGlobalDefaultsBag( 1748 LPCWSTR pszPropName, 1749 VARIANT *pvari, 1750 IErrorLog *pErrorLog) 1751 { 1752 if (!_EnsureGlobalDefaultsBag(STGM_READ, IID_IPropertyBag)) 1753 return E_FAIL; 1754 1755 return m_pGlobalDefaultsBag->Read(pszPropName, pvari, pErrorLog); 1756 } 1757 1758 STDMETHODIMP 1759 CViewStatePropertyBag::Read( 1760 _In_z_ LPCWSTR pszPropName, 1761 _Inout_ VARIANT *pvari, 1762 _Inout_opt_ IErrorLog *pErrorLog) 1763 { 1764 if ((m_dwVspbFlags & SHGVSPB_NOAUTODEFAULTS) || (m_dwVspbFlags & SHGVSPB_INHERIT)) 1765 { 1766 if (!_EnsureReadBag(STGM_READ, IID_IPropertyBag)) 1767 return E_FAIL; 1768 1769 return m_pReadBag->Read(pszPropName, pvari, pErrorLog); 1770 } 1771 1772 HRESULT hr = _ReadPidlBag(pszPropName, pvari, pErrorLog); 1773 if (SUCCEEDED(hr)) 1774 return hr; 1775 1776 hr = _ReadInheritBag(pszPropName, pvari, pErrorLog); 1777 if (SUCCEEDED(hr)) 1778 return hr; 1779 1780 hr = _ReadUpgradeBag(pszPropName, pvari, pErrorLog); 1781 if (SUCCEEDED(hr)) 1782 return hr; 1783 1784 hr = _ReadUserDefaultsBag(pszPropName, pvari, pErrorLog); 1785 if (SUCCEEDED(hr)) 1786 return hr; 1787 1788 hr = _ReadFolderDefaultsBag(pszPropName, pvari, pErrorLog); 1789 if (SUCCEEDED(hr)) 1790 return hr; 1791 1792 return _ReadGlobalDefaultsBag(pszPropName, pvari, pErrorLog); 1793 } 1794 1795 void CViewStatePropertyBag::_PruneMRUTree() 1796 { 1797 HKEY hKey = _GetHKey(SHGVSPB_INHERIT); 1798 if (!hKey) 1799 return; 1800 1801 CComPtr<IMruPidlList> pMruList; 1802 HRESULT hr = ::CoCreateInstance(CLSID_MruPidlList, NULL, CLSCTX_INPROC_SERVER, 1803 IID_IMruPidlList, (void**)&pMruList); 1804 if (SUCCEEDED(hr)) 1805 { 1806 hr = pMruList->InitList(200, hKey, L"BagMRU"); 1807 if (SUCCEEDED(hr)) 1808 pMruList->PruneKids(m_pidl); 1809 } 1810 1811 ::RegCloseKey(hKey); 1812 } 1813 1814 BOOL CViewStatePropertyBag::_EnsureWriteBag(DWORD dwMode, REFIID riid) 1815 { 1816 if (!m_pWriteBag && !m_bWriteBag) 1817 { 1818 m_bWriteBag = TRUE; 1819 _CreateBag(m_pidl, m_pszPath, m_dwVspbFlags, dwMode, riid, &m_pWriteBag); 1820 if (m_pWriteBag) 1821 { 1822 _ResetTryAgainFlag(); 1823 if (m_dwVspbFlags & SHGVSPB_INHERIT) 1824 _PruneMRUTree(); 1825 } 1826 } 1827 return (m_pWriteBag != NULL); 1828 } 1829 1830 STDMETHODIMP CViewStatePropertyBag::Write(_In_z_ LPCWSTR pszPropName, _In_ VARIANT *pvari) 1831 { 1832 if (!_EnsureWriteBag(STGM_WRITE, IID_IPropertyBag)) 1833 return E_FAIL; 1834 1835 return m_pWriteBag->Write(pszPropName, pvari); 1836 } 1837 1838 static BOOL SHIsRemovableDrive(LPCITEMIDLIST pidl) 1839 { 1840 STRRET strret; 1841 CComPtr<IShellFolder> psf; 1842 WCHAR szBuff[MAX_PATH]; 1843 LPCITEMIDLIST ppidlLast; 1844 INT iDrive, nType; 1845 HRESULT hr; 1846 1847 hr = SHBindToParent(pidl, IID_IShellFolder, (void **)&psf, &ppidlLast); 1848 if (FAILED(hr)) 1849 return FALSE; 1850 1851 hr = psf->GetDisplayNameOf(ppidlLast, SHGDN_FORPARSING, &strret); 1852 if (FAILED(hr)) 1853 return FALSE; 1854 1855 hr = StrRetToBufW(&strret, ppidlLast, szBuff, _countof(szBuff)); 1856 if (FAILED(hr)) 1857 return FALSE; 1858 1859 iDrive = PathGetDriveNumberW(szBuff); 1860 if (iDrive < 0) 1861 return FALSE; 1862 1863 nType = RealDriveType(iDrive, FALSE); 1864 return (nType == DRIVE_REMOVABLE || nType == DRIVE_CDROM); 1865 } 1866 1867 /************************************************************************** 1868 * SHGetViewStatePropertyBag (SHLWAPI.515) 1869 * 1870 * Retrieves a property bag in which the view state information of a folder 1871 * can be stored. 1872 * 1873 * @param pidl PIDL of the folder requested 1874 * @param bag_name Name of the property bag requested 1875 * @param flags Optional SHGVSPB_... flags 1876 * @param riid IID of requested property bag interface 1877 * @param ppv Address to receive pointer to the new interface 1878 * @return An HRESULT value. S_OK on success, non-zero on failure. 1879 * @see https://learn.microsoft.com/en-us/windows/win32/api/shlwapi/nf-shlwapi-shgetviewstatepropertybag 1880 */ 1881 EXTERN_C HRESULT WINAPI 1882 SHGetViewStatePropertyBag( 1883 _In_opt_ PCIDLIST_ABSOLUTE pidl, 1884 _In_opt_ LPCWSTR bag_name, 1885 _In_ DWORD flags, 1886 _In_ REFIID riid, 1887 _Outptr_ void **ppv) 1888 { 1889 HRESULT hr; 1890 1891 TRACE("%p %s 0x%X %p %p\n", pidl, debugstr_w(bag_name), flags, &riid, ppv); 1892 1893 *ppv = NULL; 1894 1895 ::EnterCriticalSection(&g_csBagCacheLock); 1896 1897 if (g_pCachedBag && g_pCachedBag->IsSameBag(pidl, bag_name, flags)) 1898 { 1899 hr = g_pCachedBag->QueryInterface(riid, ppv); 1900 ::LeaveCriticalSection(&g_csBagCacheLock); 1901 return hr; 1902 } 1903 1904 if (SHIsRemovableDrive(pidl)) 1905 { 1906 TRACE("pidl %p is removable\n", pidl); 1907 ::LeaveCriticalSection(&g_csBagCacheLock); 1908 return E_FAIL; 1909 } 1910 1911 CComPtr<CViewStatePropertyBag> pBag(new CViewStatePropertyBag()); 1912 1913 hr = pBag->Init(pidl, bag_name, flags); 1914 if (FAILED(hr)) 1915 { 1916 ERR("0x%08X\n", hr); 1917 ::LeaveCriticalSection(&g_csBagCacheLock); 1918 return hr; 1919 } 1920 g_pCachedBag = pBag; 1921 ::LeaveCriticalSection(&g_csBagCacheLock); 1922 return pBag->QueryInterface(riid, ppv); 1923 } 1924 1925 EXTERN_C VOID FreeViewStatePropertyBagCache(VOID) 1926 { 1927 ::EnterCriticalSection(&g_csBagCacheLock); 1928 g_pCachedBag.Release(); 1929 ::LeaveCriticalSection(&g_csBagCacheLock); 1930 } 1931 1932 /************************************************************************** 1933 * SHGetPerScreenResName (SHLWAPI.533) 1934 * 1935 * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/propbag/getperscreenresname.htm 1936 */ 1937 EXTERN_C INT WINAPI 1938 SHGetPerScreenResName( 1939 _Out_writes_(cchBuffer) LPWSTR pszBuffer, 1940 _In_ INT cchBuffer, 1941 _In_ DWORD dwReserved) 1942 { 1943 if (dwReserved) 1944 return 0; 1945 1946 HDC hDC = ::GetDC(NULL); 1947 INT cxWidth = ::GetDeviceCaps(hDC, HORZRES); 1948 INT cyHeight = ::GetDeviceCaps(hDC, VERTRES); 1949 INT cMonitors = ::GetSystemMetrics(SM_CMONITORS); 1950 ::ReleaseDC(NULL, hDC); 1951 1952 StringCchPrintfW(pszBuffer, cchBuffer, L"%dx%d(%d)", cxWidth, cyHeight, cMonitors); 1953 return lstrlenW(pszBuffer); 1954 } 1955 1956 /************************************************************************** 1957 * IUnknown_QueryServicePropertyBag (SHLWAPI.536) 1958 * 1959 * @param punk An IUnknown interface. 1960 * @param flags The SHGVSPB_... flags of SHGetViewStatePropertyBag. 1961 * @param riid IID of requested property bag interface. 1962 * @param ppvObj Address to receive pointer to the new interface. 1963 * @return An HRESULT value. S_OK on success, non-zero on failure. 1964 * @see https://geoffchappell.com/studies/windows/shell/shlwapi/api/util/iunknown/queryservicepropertybag.htm 1965 */ 1966 EXTERN_C HRESULT WINAPI 1967 IUnknown_QueryServicePropertyBag( 1968 _In_ IUnknown *punk, 1969 _In_ long flags, 1970 _In_ REFIID riid, 1971 _Outptr_ void **ppvObj) 1972 { 1973 TRACE("%p 0x%x %p %p\n", punk, flags, &riid, ppvObj); 1974 1975 CComPtr<IShellBrowserService> pService; 1976 HRESULT hr = IUnknown_QueryService(punk, SID_STopLevelBrowser, IID_IShellBrowserService, 1977 (void **)&pService); 1978 if (FAILED(hr)) 1979 { 1980 ERR("0x%X\n", hr); 1981 return hr; 1982 } 1983 1984 return pService->GetPropertyBag(flags, riid, ppvObj); 1985 } 1986