1 /* 2 * PROJECT: ReactOS shdocvw 3 * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later) 4 * PURPOSE: Implement MRU List of shdocvw.dll 5 * COPYRIGHT: Copyright 2023 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com> 6 */ 7 8 #define COBJMACROS 9 10 #include "objects.h" 11 #include <tchar.h> 12 #include <strsafe.h> 13 14 #include <wine/debug.h> 15 WINE_DEFAULT_DEBUG_CHANNEL(shdocvw); 16 17 class CSafeMutex; 18 class CMruBase; 19 class CMruShortList; 20 class CMruLongList; 21 class CMruNode; 22 class CMruPidlList; 23 class CMruClassFactory; 24 25 // The flags for SLOTITEMDATA.dwFlags 26 #define SLOT_LOADED 0x1 27 #define SLOT_SET 0x2 28 29 // The flags for CMruBase.m_dwFlags 30 #define COMPARE_BY_MEMCMP 0x0 31 #define COMPARE_BY_STRCMPIW 0x1 32 #define COMPARE_BY_STRCMPW 0x2 33 #define COMPARE_BY_IEILISEQUAL 0x3 34 #define COMPARE_BY_MASK 0xF 35 36 class CSafeMutex 37 { 38 protected: 39 HANDLE m_hMutex; 40 41 public: 42 CSafeMutex() : m_hMutex(NULL) 43 { 44 } 45 ~CSafeMutex() 46 { 47 if (m_hMutex) 48 { 49 ::ReleaseMutex(m_hMutex); 50 m_hMutex = NULL; 51 } 52 } 53 54 HRESULT Enter(HANDLE hMutex) 55 { 56 DWORD wait = ::WaitForSingleObject(hMutex, 500); 57 if (wait != WAIT_OBJECT_0) 58 return E_FAIL; 59 60 m_hMutex = hMutex; 61 return S_OK; 62 } 63 }; 64 65 class CMruBase 66 : public IMruDataList 67 { 68 protected: 69 LONG m_cRefs = 1; // Reference count 70 DWORD m_dwFlags = 0; // The COMPARE_BY_... flags 71 BOOL m_bNeedSave = FALSE; // The flag that indicates whether it needs saving 72 BOOL m_bChecked = FALSE; // The checked flag 73 HKEY m_hKey = NULL; // A registry key 74 DWORD m_cSlotRooms = 0; // Rooms for slots 75 DWORD m_cSlots = 0; // The # of slots 76 SLOTCOMPARE m_fnCompare = NULL; // The comparison function 77 SLOTITEMDATA * m_pSlots = NULL; // Slot data 78 79 HRESULT _LoadItem(UINT iSlot); 80 HRESULT _AddItem(UINT iSlot, LPCVOID pvData, DWORD cbData); 81 HRESULT _GetItem(UINT iSlot, SLOTITEMDATA **ppItem); 82 void _DeleteItem(UINT iSlot); 83 84 HRESULT _GetSlotItem(UINT iSlot, SLOTITEMDATA **ppItem); 85 void _CheckUsedSlots(); 86 HRESULT _UseEmptySlot(UINT *piSlot); 87 88 public: 89 CMruBase(); 90 virtual ~CMruBase(); 91 92 // IUnknown methods 93 STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj) override; 94 STDMETHODIMP_(ULONG) AddRef() override 95 { 96 return ::InterlockedIncrement(&m_cRefs); 97 } 98 STDMETHODIMP_(ULONG) Release() override; 99 100 // IMruDataList methods 101 STDMETHODIMP InitData(UINT cCapacity, UINT flags, HKEY hKey, 102 LPCWSTR pszSubKey OPTIONAL, 103 SLOTCOMPARE fnCompare OPTIONAL) override; 104 STDMETHODIMP AddData(LPCVOID pvData, DWORD cbData, UINT *piSlot) override; 105 STDMETHODIMP FindData(LPCVOID pvData, DWORD cbData, UINT *piSlot) override; 106 STDMETHODIMP GetData(UINT iSlot, LPVOID pvData, DWORD cbData) override; 107 STDMETHODIMP QueryInfo(UINT iSlot, UINT *piGotSlot, DWORD *pcbData) override; 108 STDMETHODIMP Delete(UINT iSlot) override; 109 110 // Non-standard methods 111 virtual BOOL _IsEqual(const SLOTITEMDATA *pItem, LPCVOID pvData, UINT cbData) const; 112 virtual DWORD _DeleteValue(LPCWSTR pszValue); 113 virtual HRESULT _InitSlots() = 0; 114 virtual void _SaveSlots() = 0; 115 virtual UINT _UpdateSlots(UINT iSlot) = 0; 116 virtual void _SlotString(UINT iSlot, LPWSTR psz, DWORD cch) = 0; 117 virtual HRESULT _GetSlot(UINT iSlot, UINT *puSlot) = 0; 118 virtual HRESULT _RemoveSlot(UINT iSlot, UINT *puSlot) = 0; 119 120 static void* operator new(size_t size) 121 { 122 return ::LocalAlloc(LPTR, size); 123 } 124 static void operator delete(void *ptr) 125 { 126 ::LocalFree(ptr); 127 } 128 }; 129 130 CMruBase::CMruBase() 131 { 132 SHDOCVW_LockModule(); 133 } 134 135 CMruBase::~CMruBase() 136 { 137 if (m_hKey) 138 { 139 ::RegCloseKey(m_hKey); 140 m_hKey = NULL; 141 } 142 143 if (m_pSlots) 144 { 145 for (UINT iSlot = 0; iSlot < m_cSlots; ++iSlot) 146 { 147 m_pSlots[iSlot].pvData = ::LocalFree(m_pSlots[iSlot].pvData); 148 } 149 150 m_pSlots = (SLOTITEMDATA*)::LocalFree(m_pSlots); 151 } 152 153 SHDOCVW_UnlockModule(); 154 } 155 156 STDMETHODIMP CMruBase::QueryInterface(REFIID riid, void **ppvObj) 157 { 158 if (!ppvObj) 159 return E_POINTER; 160 if (IsEqualGUID(riid, IID_IMruDataList) || IsEqualGUID(riid, IID_IUnknown)) 161 { 162 *ppvObj = static_cast<IMruDataList*>(this); 163 AddRef(); 164 return S_OK; 165 } 166 ERR("%s: E_NOINTERFACE\n", debugstr_guid(&riid)); 167 return E_NOINTERFACE; 168 } 169 170 STDMETHODIMP_(ULONG) CMruBase::Release() 171 { 172 if (::InterlockedDecrement(&m_cRefs) == 0) 173 { 174 _SaveSlots(); 175 delete this; 176 return 0; 177 } 178 return m_cRefs; 179 } 180 181 HRESULT CMruBase::_LoadItem(UINT iSlot) 182 { 183 DWORD cbData; 184 WCHAR szValue[12]; 185 186 SLOTITEMDATA *pItem = &m_pSlots[iSlot]; 187 _SlotString(iSlot, szValue, _countof(szValue)); 188 189 if (SHGetValueW(m_hKey, NULL, szValue, NULL, NULL, &cbData) == ERROR_SUCCESS && 190 cbData > 0) 191 { 192 pItem->pvData = ::LocalAlloc(LPTR, cbData); 193 if (pItem->pvData) 194 { 195 pItem->cbData = cbData; 196 if (SHGetValueW(m_hKey, NULL, szValue, NULL, pItem->pvData, &cbData) != ERROR_SUCCESS) 197 pItem->pvData = ::LocalFree(pItem->pvData); 198 } 199 } 200 201 pItem->dwFlags |= SLOT_LOADED; 202 if (!pItem->pvData) 203 return E_FAIL; 204 205 return S_OK; 206 } 207 208 HRESULT CMruBase::_GetSlotItem(UINT iSlot, SLOTITEMDATA **ppItem) 209 { 210 if (!(m_pSlots[iSlot].dwFlags & SLOT_LOADED)) 211 _LoadItem(iSlot); 212 213 SLOTITEMDATA *pItem = &m_pSlots[iSlot]; 214 if (!pItem->pvData) 215 return E_OUTOFMEMORY; 216 217 *ppItem = pItem; 218 return S_OK; 219 } 220 221 HRESULT CMruBase::_GetItem(UINT iSlot, SLOTITEMDATA **ppItem) 222 { 223 HRESULT hr = _GetSlot(iSlot, &iSlot); 224 if (FAILED(hr)) 225 return hr; 226 return _GetSlotItem(iSlot, ppItem); 227 } 228 229 void CMruBase::_DeleteItem(UINT iSlot) 230 { 231 WCHAR szBuff[12]; 232 233 _SlotString(iSlot, szBuff, _countof(szBuff)); 234 _DeleteValue(szBuff); 235 236 m_pSlots[iSlot].pvData = ::LocalFree(m_pSlots[iSlot].pvData); 237 } 238 239 void CMruBase::_CheckUsedSlots() 240 { 241 UINT iGotSlot; 242 for (UINT iSlot = 0; iSlot < m_cSlots; ++iSlot) 243 _GetSlot(iSlot, &iGotSlot); 244 245 m_bChecked = TRUE; 246 } 247 248 HRESULT CMruBase::_AddItem(UINT iSlot, LPCVOID pvData, DWORD cbData) 249 { 250 SLOTITEMDATA *pItem = &m_pSlots[iSlot]; 251 252 WCHAR szBuff[12]; 253 _SlotString(iSlot, szBuff, _countof(szBuff)); 254 255 if (SHSetValueW(m_hKey, NULL, szBuff, REG_BINARY, pvData, cbData) != ERROR_SUCCESS) 256 return E_OUTOFMEMORY; 257 258 if (cbData >= pItem->cbData || !pItem->pvData) 259 { 260 ::LocalFree(pItem->pvData); 261 pItem->pvData = ::LocalAlloc(LPTR, cbData); 262 } 263 264 if (!pItem->pvData) 265 return E_FAIL; 266 267 pItem->cbData = cbData; 268 pItem->dwFlags = (SLOT_LOADED | SLOT_SET); 269 CopyMemory(pItem->pvData, pvData, cbData); 270 return S_OK; 271 } 272 273 STDMETHODIMP 274 CMruBase::InitData( 275 UINT cCapacity, 276 UINT flags, 277 HKEY hKey, 278 LPCWSTR pszSubKey OPTIONAL, 279 SLOTCOMPARE fnCompare OPTIONAL) 280 { 281 m_dwFlags = flags; 282 m_fnCompare = fnCompare; 283 m_cSlotRooms = cCapacity; 284 285 if (pszSubKey) 286 ::RegCreateKeyExWrapW(hKey, pszSubKey, 0, NULL, 0, MAXIMUM_ALLOWED, NULL, &m_hKey, NULL); 287 else 288 m_hKey = SHRegDuplicateHKey(hKey); 289 290 if (!m_hKey) 291 return E_FAIL; 292 293 m_pSlots = (SLOTITEMDATA*)::LocalAlloc(LPTR, m_cSlotRooms * sizeof(SLOTITEMDATA)); 294 if (!m_pSlots) 295 return E_OUTOFMEMORY; 296 297 return _InitSlots(); 298 } 299 300 STDMETHODIMP CMruBase::AddData(LPCVOID pvData, DWORD cbData, UINT *piSlot) 301 { 302 UINT iSlot; 303 HRESULT hr = FindData(pvData, cbData, &iSlot); 304 if (FAILED(hr)) 305 { 306 iSlot = _UpdateSlots(m_cSlots); 307 hr = _AddItem(iSlot, pvData, cbData); 308 if (FAILED(hr)) 309 return hr; 310 } 311 else 312 { 313 iSlot = _UpdateSlots(iSlot); 314 hr = S_OK; 315 } 316 317 if (piSlot) 318 *piSlot = iSlot; 319 320 return hr; 321 } 322 323 STDMETHODIMP CMruBase::FindData(LPCVOID pvData, DWORD cbData, UINT *piSlot) 324 { 325 if (m_cSlots <= 0) 326 return E_FAIL; 327 328 UINT iSlot = 0; 329 SLOTITEMDATA *pItem; 330 while (FAILED(_GetItem(iSlot, &pItem)) || !_IsEqual(pItem, pvData, cbData)) 331 { 332 if (++iSlot >= m_cSlots) 333 return E_FAIL; 334 } 335 336 *piSlot = iSlot; 337 return S_OK; 338 } 339 340 STDMETHODIMP CMruBase::GetData(UINT iSlot, LPVOID pvData, DWORD cbData) 341 { 342 SLOTITEMDATA *pItem; 343 HRESULT hr = _GetItem(iSlot, &pItem); 344 if (FAILED(hr)) 345 return hr; 346 347 if (cbData < pItem->cbData) 348 return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); 349 350 CopyMemory(pvData, pItem->pvData, pItem->cbData); 351 return hr; 352 } 353 354 STDMETHODIMP CMruBase::QueryInfo(UINT iSlot, UINT *piGotSlot, DWORD *pcbData) 355 { 356 UINT iGotSlot; 357 HRESULT hr = _GetSlot(iSlot, &iGotSlot); 358 if (FAILED(hr)) 359 return hr; 360 361 if (piGotSlot) 362 *piGotSlot = iGotSlot; 363 364 if (pcbData) 365 { 366 SLOTITEMDATA *pItem; 367 hr = _GetSlotItem(iGotSlot, &pItem); 368 if (SUCCEEDED(hr)) 369 *pcbData = pItem->cbData; 370 } 371 372 return hr; 373 } 374 375 STDMETHODIMP CMruBase::Delete(UINT iSlot) 376 { 377 UINT uSlot; 378 HRESULT hr = _RemoveSlot(iSlot, &uSlot); 379 if (FAILED(hr)) 380 return hr; 381 382 _DeleteItem(uSlot); 383 return hr; 384 } 385 386 BOOL CMruBase::_IsEqual(const SLOTITEMDATA *pItem, LPCVOID pvData, UINT cbData) const 387 { 388 if (m_fnCompare) 389 return m_fnCompare(pvData, pItem->pvData, cbData) == 0; 390 391 switch (m_dwFlags & COMPARE_BY_MASK) 392 { 393 case COMPARE_BY_MEMCMP: 394 if (pItem->cbData != cbData) 395 return FALSE; 396 return memcmp(pvData, pItem->pvData, cbData) == 0; 397 398 case COMPARE_BY_STRCMPIW: 399 return StrCmpIW((LPCWSTR)pvData, (LPCWSTR)pItem->pvData) == 0; 400 401 case COMPARE_BY_STRCMPW: 402 return StrCmpW((LPCWSTR)pvData, (LPCWSTR)pItem->pvData) == 0; 403 404 case COMPARE_BY_IEILISEQUAL: 405 return IEILIsEqual((LPCITEMIDLIST)pvData, (LPCITEMIDLIST)pItem->pvData, FALSE); 406 407 default: 408 ERR("0x%08X\n", m_dwFlags); 409 return FALSE; 410 } 411 } 412 413 DWORD CMruBase::_DeleteValue(LPCWSTR pszValue) 414 { 415 return SHDeleteValueW(m_hKey, NULL, pszValue); 416 } 417 418 HRESULT CMruBase::_UseEmptySlot(UINT *piSlot) 419 { 420 if (!m_bChecked) 421 _CheckUsedSlots(); 422 423 if (!m_cSlotRooms) 424 return E_FAIL; 425 426 UINT iSlot = 0; 427 for (SLOTITEMDATA *pItem = m_pSlots; (pItem->dwFlags & SLOT_SET); ++pItem) 428 { 429 if (++iSlot >= m_cSlotRooms) 430 return E_FAIL; 431 } 432 433 m_pSlots[iSlot].dwFlags |= SLOT_SET; 434 *piSlot = iSlot; 435 ++m_cSlots; 436 437 return S_OK; 438 } 439 440 class CMruShortList 441 : public CMruBase 442 { 443 protected: 444 LPWSTR m_pszSlotData = NULL; 445 446 HRESULT _InitSlots() override; 447 void _SaveSlots() override; 448 UINT _UpdateSlots(UINT iSlot) override; 449 void _SlotString(UINT iSlot, LPWSTR psz, DWORD cch) override; 450 HRESULT _GetSlot(UINT iSlot, UINT *puSlot) override; 451 HRESULT _RemoveSlot(UINT iSlot, UINT *puSlot) override; 452 friend class CMruLongList; 453 454 public: 455 CMruShortList() 456 { 457 } 458 459 ~CMruShortList() override 460 { 461 m_pszSlotData = (LPWSTR)::LocalFree(m_pszSlotData); 462 } 463 }; 464 465 HRESULT CMruShortList::_InitSlots() 466 { 467 DWORD cbData = (m_cSlotRooms + 1) * sizeof(WCHAR); 468 m_pszSlotData = (LPWSTR)LocalAlloc(LPTR, cbData); 469 if (!m_pszSlotData) 470 return E_OUTOFMEMORY; 471 472 if (SHGetValueW(m_hKey, NULL, L"MRUList", NULL, m_pszSlotData, &cbData) == ERROR_SUCCESS) 473 m_cSlots = (cbData / sizeof(WCHAR)) - 1; 474 475 m_pszSlotData[m_cSlots] = UNICODE_NULL; 476 return S_OK; 477 } 478 479 void CMruShortList::_SaveSlots() 480 { 481 if (m_bNeedSave) 482 { 483 DWORD cbData = (m_cSlots + 1) * sizeof(WCHAR); 484 SHSetValueW(m_hKey, NULL, L"MRUList", REG_SZ, m_pszSlotData, cbData); 485 m_bNeedSave = FALSE; 486 } 487 } 488 489 // NOTE: MRUList uses lowercase alphabet for history of most recently used items. 490 UINT CMruShortList::_UpdateSlots(UINT iSlot) 491 { 492 UINT iData, cDataToMove = iSlot; 493 494 if (iSlot == m_cSlots) 495 { 496 if (SUCCEEDED(_UseEmptySlot(&iData))) 497 { 498 ++cDataToMove; 499 } 500 else 501 { 502 // This code is getting the item index from a lowercase letter. 503 iData = m_pszSlotData[m_cSlots - 1] - L'a'; 504 --cDataToMove; 505 } 506 } 507 else 508 { 509 iData = m_pszSlotData[iSlot] - L'a'; 510 } 511 512 if (cDataToMove) 513 { 514 MoveMemory(m_pszSlotData + 1, m_pszSlotData, cDataToMove * sizeof(WCHAR)); 515 m_pszSlotData[0] = (WCHAR)(L'a' + iData); 516 m_bNeedSave = TRUE; 517 } 518 519 return iData; 520 } 521 522 void CMruShortList::_SlotString(UINT iSlot, LPWSTR psz, DWORD cch) 523 { 524 if (cch >= 2) 525 { 526 psz[0] = (WCHAR)(L'a' + iSlot); 527 psz[1] = UNICODE_NULL; 528 } 529 } 530 531 HRESULT CMruShortList::_GetSlot(UINT iSlot, UINT *puSlot) 532 { 533 if (iSlot >= m_cSlots) 534 return E_FAIL; 535 536 UINT iData = m_pszSlotData[iSlot] - L'a'; 537 if (iData >= m_cSlotRooms) 538 return E_FAIL; 539 540 *puSlot = iData; 541 m_pSlots[iData].dwFlags |= SLOT_SET; 542 return S_OK; 543 } 544 545 HRESULT CMruShortList::_RemoveSlot(UINT iSlot, UINT *puSlot) 546 { 547 HRESULT hr = _GetSlot(iSlot, puSlot); 548 if (FAILED(hr)) 549 return hr; 550 551 MoveMemory(&m_pszSlotData[iSlot], &m_pszSlotData[iSlot + 1], (m_cSlots - iSlot) * sizeof(WCHAR)); 552 --m_cSlots; 553 m_pSlots->dwFlags &= ~SLOT_SET; 554 m_bNeedSave = TRUE; 555 556 return hr; 557 } 558 559 class CMruLongList 560 : public CMruBase 561 { 562 protected: 563 UINT *m_puSlotData = NULL; // The slot data 564 565 void _ImportShortList(); 566 567 HRESULT _InitSlots() override; 568 void _SaveSlots() override; 569 UINT _UpdateSlots(UINT iSlot) override; 570 void _SlotString(UINT iSlot, LPWSTR psz, DWORD cch) override; 571 HRESULT _GetSlot(UINT iSlot, UINT *puSlot) override; 572 HRESULT _RemoveSlot(UINT iSlot, UINT *puSlot) override; 573 574 public: 575 CMruLongList() 576 { 577 } 578 579 ~CMruLongList() override 580 { 581 m_puSlotData = (UINT*)::LocalFree(m_puSlotData); 582 } 583 }; 584 585 HRESULT CMruLongList::_InitSlots() 586 { 587 DWORD cbData = (m_cSlotRooms + 1) * sizeof(UINT); 588 m_puSlotData = (UINT*)LocalAlloc(LPTR, cbData); 589 if (!m_puSlotData) 590 return E_OUTOFMEMORY; 591 592 if (SHGetValueW(m_hKey, NULL, L"MRUListEx", NULL, m_puSlotData, &cbData) == ERROR_SUCCESS) 593 m_cSlots = (cbData / sizeof(UINT)) - 1; 594 else 595 _ImportShortList(); 596 597 m_puSlotData[m_cSlots] = MAXDWORD; 598 return S_OK; 599 } 600 601 void CMruLongList::_SaveSlots() 602 { 603 if (m_bNeedSave) 604 { 605 SHSetValueW(m_hKey, NULL, L"MRUListEx", REG_BINARY, m_puSlotData, 606 (m_cSlots + 1) * sizeof(UINT)); 607 m_bNeedSave = FALSE; 608 } 609 } 610 611 UINT CMruLongList::_UpdateSlots(UINT iSlot) 612 { 613 UINT cSlotsToMove, uSlotData; 614 615 cSlotsToMove = iSlot; 616 if (iSlot == m_cSlots) 617 { 618 if (SUCCEEDED(_UseEmptySlot(&uSlotData))) 619 { 620 ++cSlotsToMove; 621 } 622 else 623 { 624 uSlotData = m_puSlotData[m_cSlots - 1]; 625 --cSlotsToMove; 626 } 627 } 628 else 629 { 630 uSlotData = m_puSlotData[iSlot]; 631 } 632 633 if (cSlotsToMove > 0) 634 { 635 MoveMemory(m_puSlotData + 1, m_puSlotData, cSlotsToMove * sizeof(UINT)); 636 m_puSlotData[0] = uSlotData; 637 m_bNeedSave = TRUE; 638 } 639 640 return uSlotData; 641 } 642 643 void CMruLongList::_SlotString(UINT iSlot, LPWSTR psz, DWORD cch) 644 { 645 StringCchPrintfW(psz, cch, L"%d", iSlot); 646 } 647 648 HRESULT CMruLongList::_GetSlot(UINT iSlot, UINT *puSlot) 649 { 650 if (iSlot >= m_cSlots) 651 return E_FAIL; 652 653 UINT uSlotData = m_puSlotData[iSlot]; 654 if (uSlotData >= m_cSlotRooms) 655 return E_FAIL; 656 657 *puSlot = uSlotData; 658 m_pSlots[uSlotData].dwFlags |= SLOT_SET; 659 return S_OK; 660 } 661 662 HRESULT CMruLongList::_RemoveSlot(UINT iSlot, UINT *puSlot) 663 { 664 HRESULT hr = _GetSlot(iSlot, puSlot); 665 if (FAILED(hr)) 666 return hr; 667 668 MoveMemory(&m_puSlotData[iSlot], &m_puSlotData[iSlot + 1], (m_cSlots - iSlot) * sizeof(UINT)); 669 --m_cSlots; 670 m_pSlots[0].dwFlags &= ~SLOT_SET; 671 m_bNeedSave = TRUE; 672 673 return hr; 674 } 675 676 void CMruLongList::_ImportShortList() 677 { 678 CMruShortList *pShortList = new CMruShortList(); 679 if (!pShortList) 680 return; 681 682 HRESULT hr = pShortList->InitData(m_cSlotRooms, 0, m_hKey, NULL, NULL); 683 if (SUCCEEDED(hr)) 684 { 685 for (;;) 686 { 687 UINT iSlot; 688 hr = pShortList->_GetSlot(m_cSlots, &iSlot); 689 if (FAILED(hr)) 690 break; 691 692 SLOTITEMDATA *pItem; 693 hr = pShortList->_GetSlotItem(iSlot, &pItem); 694 if (FAILED(hr)) 695 break; 696 697 _AddItem(iSlot, pItem->pvData, pItem->cbData); 698 pShortList->_DeleteItem(iSlot); 699 700 m_puSlotData[m_cSlots++] = iSlot; 701 } 702 703 m_bNeedSave = TRUE; 704 } 705 706 SHDeleteValueW(m_hKey, NULL, L"MRUList"); 707 pShortList->Release(); 708 } 709 710 EXTERN_C HRESULT 711 CMruLongList_CreateInstance(DWORD_PTR dwUnused1, void **ppv, DWORD_PTR dwUnused3) 712 { 713 UNREFERENCED_PARAMETER(dwUnused1); 714 UNREFERENCED_PARAMETER(dwUnused3); 715 716 TRACE("%p %p %p\n", dwUnused1, ppv, dwUnused3); 717 718 if (!ppv) 719 return E_POINTER; 720 721 CMruLongList *pMruList = new CMruLongList(); 722 *ppv = static_cast<IMruDataList*>(pMruList); 723 TRACE("%p\n", *ppv); 724 725 return S_OK; 726 } 727 728 class CMruNode 729 : public CMruLongList 730 { 731 protected: 732 UINT m_iSlot = 0; // The slot index 733 CMruNode *m_pParent = NULL; // The parent 734 IShellFolder *m_pShellFolder = NULL; // The shell folder 735 736 BOOL _InitLate(); 737 BOOL _IsEqual(SLOTITEMDATA *pItem, LPCVOID pvData, UINT cbData); 738 DWORD _DeleteValue(LPCWSTR pszValue) override; 739 740 HRESULT _CreateNode(UINT iSlot, CMruNode **ppNewNode); 741 HRESULT _AddPidl(UINT iSlot, LPCITEMIDLIST pidl); 742 HRESULT _FindPidl(LPCITEMIDLIST pidl, UINT *piSlot); 743 HRESULT _GetPidlSlot(LPCITEMIDLIST pidl, BOOL bAdd, UINT *piSlot); 744 745 public: 746 CMruNode() { } 747 CMruNode(CMruNode *pParent, UINT iSlot); 748 ~CMruNode() override; 749 750 CMruNode *GetParent(); 751 752 HRESULT BindToSlot(UINT iSlot, IShellFolder **ppSF); 753 HRESULT GetNode(BOOL bAdd, LPCITEMIDLIST pidl, CMruNode **pNewNode); 754 HRESULT GetNodeSlot(UINT *pnNodeSlot); 755 HRESULT SetNodeSlot(UINT nNodeSlot); 756 757 HRESULT RemoveLeast(UINT *pnNodeSlot); 758 HRESULT Clear(CMruPidlList *pList); 759 }; 760 761 CMruNode::CMruNode(CMruNode *pParent, UINT iSlot) 762 { 763 m_iSlot = iSlot; 764 m_pParent = pParent; 765 pParent->AddRef(); 766 } 767 768 CMruNode::~CMruNode() 769 { 770 if (m_pParent) 771 { 772 m_pParent->Release(); 773 m_pParent = NULL; 774 } 775 776 if (m_pShellFolder) 777 { 778 m_pShellFolder->Release(); 779 m_pShellFolder = NULL; 780 } 781 } 782 783 CMruNode *CMruNode::GetParent() 784 { 785 if (m_pParent) 786 m_pParent->AddRef(); 787 return m_pParent; 788 } 789 790 HRESULT CMruNode::_CreateNode(UINT iSlot, CMruNode **ppNewNode) 791 { 792 CMruNode *pNewNode = new CMruNode(this, iSlot); 793 if (!pNewNode) 794 return E_OUTOFMEMORY; 795 796 WCHAR szSubKey[12]; 797 _SlotString(iSlot, szSubKey, _countof(szSubKey)); 798 799 HRESULT hr = pNewNode->InitData(m_cSlotRooms, 0, m_hKey, szSubKey, NULL); 800 if (FAILED(hr)) 801 pNewNode->Release(); 802 else 803 *ppNewNode = pNewNode; 804 805 return hr; 806 } 807 808 HRESULT CMruNode::GetNode(BOOL bAdd, LPCITEMIDLIST pidl, CMruNode **ppNewNode) 809 { 810 if (!pidl || !pidl->mkid.cb) 811 { 812 *ppNewNode = this; 813 AddRef(); 814 return S_OK; 815 } 816 817 if (!_InitLate()) 818 return E_FAIL; 819 820 UINT iSlot; 821 HRESULT hr = _GetPidlSlot(pidl, bAdd, &iSlot); 822 if (FAILED(hr)) 823 { 824 if (!bAdd) 825 { 826 *ppNewNode = this; 827 AddRef(); 828 return S_FALSE; 829 } 830 return hr; 831 } 832 833 CMruNode *pNewNode; 834 hr = _CreateNode(iSlot, &pNewNode); 835 if (SUCCEEDED(hr)) 836 { 837 _SaveSlots(); 838 839 LPCITEMIDLIST pidl2 = (LPCITEMIDLIST)((LPBYTE)pidl + pidl->mkid.cb); 840 pNewNode->GetNode(bAdd, pidl2, ppNewNode); 841 pNewNode->Release(); 842 } 843 844 return hr; 845 } 846 847 HRESULT CMruNode::BindToSlot(UINT iSlot, IShellFolder **ppSF) 848 { 849 SLOTITEMDATA *pItem; 850 HRESULT hr = _GetSlotItem(iSlot, &pItem); 851 if (FAILED(hr)) 852 return hr; 853 854 return m_pShellFolder->BindToObject((LPITEMIDLIST)pItem->pvData, 855 NULL, 856 IID_IShellFolder, 857 (void **)ppSF); 858 } 859 860 BOOL CMruNode::_InitLate() 861 { 862 if (!m_pShellFolder) 863 { 864 if (m_pParent) 865 m_pParent->BindToSlot(m_iSlot, &m_pShellFolder); 866 else 867 SHGetDesktopFolder(&m_pShellFolder); 868 } 869 return !!m_pShellFolder; 870 } 871 872 BOOL CMruNode::_IsEqual(SLOTITEMDATA *pItem, LPCVOID pvData, UINT cbData) 873 { 874 return m_pShellFolder->CompareIDs(0x10000000, 875 (LPITEMIDLIST)pItem->pvData, 876 (LPCITEMIDLIST)pvData) == 0; 877 } 878 879 HRESULT CMruNode::GetNodeSlot(UINT *pnNodeSlot) 880 { 881 DWORD dwData, cbData = sizeof(dwData); 882 DWORD error = SHGetValueW(m_hKey, NULL, L"NodeSlot", NULL, &dwData, (pnNodeSlot ? &cbData : NULL)); 883 if (error != ERROR_SUCCESS) 884 return E_FAIL; 885 *pnNodeSlot = (UINT)dwData; 886 return S_OK; 887 } 888 889 HRESULT CMruNode::SetNodeSlot(UINT nNodeSlot) 890 { 891 DWORD dwData = nNodeSlot; 892 if (SHSetValueW(m_hKey, NULL, L"NodeSlot", REG_DWORD, &dwData, sizeof(dwData)) != ERROR_SUCCESS) 893 return E_FAIL; 894 return S_OK; 895 } 896 897 HRESULT CMruNode::_AddPidl(UINT iSlot, LPCITEMIDLIST pidl) 898 { 899 return CMruBase::_AddItem(iSlot, pidl, sizeof(WORD) + pidl->mkid.cb); 900 } 901 902 DWORD CMruNode::_DeleteValue(LPCWSTR pszValue) 903 { 904 CMruBase::_DeleteValue(pszValue); 905 return SHDeleteKeyW(m_hKey, pszValue); 906 } 907 908 HRESULT CMruNode::_FindPidl(LPCITEMIDLIST pidl, UINT *piSlot) 909 { 910 return FindData(pidl, sizeof(WORD) + pidl->mkid.cb, piSlot); 911 } 912 913 HRESULT CMruNode::_GetPidlSlot(LPCITEMIDLIST pidl, BOOL bAdd, UINT *piSlot) 914 { 915 LPITEMIDLIST pidlFirst = ILCloneFirst(pidl); 916 if (!pidlFirst) 917 return E_OUTOFMEMORY; 918 919 UINT iSlot; 920 HRESULT hr = _FindPidl(pidlFirst, &iSlot); 921 if (SUCCEEDED(hr)) 922 { 923 *piSlot = _UpdateSlots(iSlot); 924 hr = S_OK; 925 } 926 else if (bAdd) 927 { 928 *piSlot = _UpdateSlots(m_cSlots); 929 hr = _AddPidl(*piSlot, pidlFirst); 930 } 931 932 ILFree(pidlFirst); 933 return hr; 934 } 935 936 HRESULT CMruNode::RemoveLeast(UINT *pnNodeSlot) 937 { 938 if (!m_cSlots) 939 { 940 GetNodeSlot(pnNodeSlot); 941 return S_FALSE; 942 } 943 944 UINT uSlot; 945 HRESULT hr = _GetSlot(m_cSlots - 1, &uSlot); 946 if (FAILED(hr)) 947 return hr; 948 949 CMruNode *pNode; 950 hr = _CreateNode(uSlot, &pNode); 951 if (SUCCEEDED(hr)) 952 { 953 hr = pNode->RemoveLeast(pnNodeSlot); 954 pNode->Release(); 955 } 956 957 if (hr == S_FALSE) 958 { 959 Delete(m_cSlots - 1); 960 if (m_cSlots || SUCCEEDED(GetNodeSlot(0))) 961 return S_OK; 962 } 963 964 return hr; 965 } 966 967 class CMruPidlList 968 : public CMruNode 969 , public IMruPidlList 970 { 971 protected: 972 LPBYTE m_pbNodeSlots = NULL; // The node slots (contains SLOT_... flags) 973 DWORD m_cMaxNodeSlots = 0; // The upper bound of the node slot index 974 HANDLE m_hMutex = NULL; // The mutex (for sync) 975 976 BOOL _LoadNodeSlots(); 977 void _SaveNodeSlots(); 978 HRESULT _InitNodeSlots(); 979 980 public: 981 CMruPidlList() { } 982 ~CMruPidlList() override; 983 984 HRESULT GetEmptySlot(UINT *pnNodeSlot); 985 void EmptyNodeSlot(UINT nNodeSlot); 986 987 // IUnknown methods 988 STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj) override; 989 STDMETHODIMP_(ULONG) AddRef() override 990 { 991 return CMruBase::AddRef(); 992 } 993 STDMETHODIMP_(ULONG) Release() override 994 { 995 return CMruBase::Release(); 996 } 997 998 // IMruPidlList methods 999 STDMETHODIMP InitList(UINT cMRUSize, HKEY hKey, LPCWSTR pszSubKey) override; 1000 STDMETHODIMP UsePidl(LPCITEMIDLIST pidl, UINT *pnNodeSlot) override; 1001 STDMETHODIMP QueryPidl( 1002 LPCITEMIDLIST pidl, 1003 UINT cSlots, 1004 UINT *pnNodeSlots, 1005 UINT *pcNodeSlots) override; 1006 STDMETHODIMP PruneKids(LPCITEMIDLIST pidl) override; 1007 }; 1008 1009 CMruPidlList::~CMruPidlList() 1010 { 1011 m_pbNodeSlots = (LPBYTE)::LocalFree(m_pbNodeSlots); 1012 if (m_hMutex) 1013 { 1014 ::CloseHandle(m_hMutex); 1015 m_hMutex = NULL; 1016 } 1017 } 1018 1019 STDMETHODIMP CMruPidlList::QueryInterface(REFIID riid, void **ppvObj) 1020 { 1021 if (!ppvObj) 1022 return E_POINTER; 1023 1024 if (::IsEqualGUID(riid, IID_IMruPidlList) || ::IsEqualGUID(riid, IID_IUnknown)) 1025 { 1026 *ppvObj = static_cast<IMruPidlList*>(this); 1027 AddRef(); 1028 return S_OK; 1029 } 1030 1031 ERR("%s: E_NOINTERFACE\n", debugstr_guid(&riid)); 1032 return E_NOINTERFACE; 1033 } 1034 1035 BOOL CMruPidlList::_LoadNodeSlots() 1036 { 1037 DWORD cbNodeSlots = m_cSlotRooms * sizeof(BYTE); 1038 if (SHGetValueW(m_hKey, NULL, L"NodeSlots", NULL, m_pbNodeSlots, &cbNodeSlots) != ERROR_SUCCESS) 1039 return FALSE; 1040 m_cMaxNodeSlots = cbNodeSlots / sizeof(BYTE); 1041 return TRUE; 1042 } 1043 1044 void CMruPidlList::_SaveNodeSlots() 1045 { 1046 DWORD cbNodeSlots = m_cMaxNodeSlots * sizeof(BYTE); 1047 SHSetValueW(m_hKey, NULL, L"NodeSlots", REG_BINARY, m_pbNodeSlots, cbNodeSlots); 1048 } 1049 1050 HRESULT CMruPidlList::_InitNodeSlots() 1051 { 1052 m_pbNodeSlots = (BYTE*)LocalAlloc(LPTR, m_cSlotRooms * sizeof(BYTE)); 1053 if (!m_pbNodeSlots) 1054 return E_OUTOFMEMORY; 1055 1056 _LoadNodeSlots(); 1057 m_bNeedSave = TRUE; 1058 _SaveNodeSlots(); 1059 1060 return S_OK; 1061 } 1062 1063 HRESULT CMruPidlList::GetEmptySlot(UINT *pnNodeSlot) 1064 { 1065 *pnNodeSlot = 0; 1066 1067 if (!_LoadNodeSlots()) 1068 return E_FAIL; 1069 1070 if (m_cMaxNodeSlots < m_cSlotRooms) 1071 { 1072 m_pbNodeSlots[m_cMaxNodeSlots] = SLOT_SET; 1073 *pnNodeSlot = ++m_cMaxNodeSlots; 1074 _SaveNodeSlots(); 1075 return S_OK; 1076 } 1077 1078 for (UINT iNodeSlot = 0; iNodeSlot < m_cMaxNodeSlots; ++iNodeSlot) 1079 { 1080 if (m_pbNodeSlots[iNodeSlot] & SLOT_SET) 1081 continue; 1082 1083 m_pbNodeSlots[iNodeSlot] = SLOT_SET; 1084 *pnNodeSlot = iNodeSlot + 1; // nNodeSlot is 1-base 1085 _SaveNodeSlots(); 1086 return S_OK; 1087 } 1088 1089 HRESULT hr = E_FAIL; 1090 if (SUCCEEDED(RemoveLeast(pnNodeSlot)) && *pnNodeSlot) 1091 hr = S_OK; 1092 1093 _SaveNodeSlots(); 1094 return hr; 1095 } 1096 1097 STDMETHODIMP CMruPidlList::InitList(UINT cMRUSize, HKEY hKey, LPCWSTR pszSubKey) 1098 { 1099 TRACE("%p -> %u %p %s\n", this, cMRUSize, hKey, debugstr_w(pszSubKey)); 1100 1101 HRESULT hr = InitData(cMRUSize, 0, hKey, pszSubKey, NULL); 1102 if (FAILED(hr)) 1103 { 1104 ERR("0x%08lX\n", hr); 1105 return hr; 1106 } 1107 1108 hr = _InitNodeSlots(); 1109 if (FAILED(hr)) 1110 { 1111 ERR("0x%08lX\n", hr); 1112 return hr; 1113 } 1114 1115 m_hMutex = ::CreateMutexW(NULL, FALSE, L"Shell.CMruPidlList"); 1116 if (!m_hMutex) 1117 { 1118 hr = HRESULT_FROM_WIN32(GetLastError()); 1119 ERR("0x%08lX\n", hr); 1120 } 1121 1122 return hr; 1123 } 1124 1125 STDMETHODIMP CMruPidlList::UsePidl(LPCITEMIDLIST pidl, UINT *pnNodeSlot) 1126 { 1127 TRACE("%p -> %p %p\n", this, pidl, pnNodeSlot); 1128 1129 CSafeMutex mutex; 1130 HRESULT hr = mutex.Enter(m_hMutex); 1131 if (FAILED(hr)) 1132 { 1133 ERR("0x%08lX\n", hr); 1134 return hr; 1135 } 1136 1137 *pnNodeSlot = 0; 1138 1139 CMruNode *pNode; 1140 hr = GetNode(TRUE, pidl, &pNode); 1141 if (FAILED(hr)) 1142 { 1143 ERR("0x%08lX\n", hr); 1144 return hr; 1145 } 1146 1147 hr = pNode->GetNodeSlot(pnNodeSlot); 1148 if (FAILED(hr)) 1149 { 1150 hr = GetEmptySlot(pnNodeSlot); 1151 if (SUCCEEDED(hr)) 1152 { 1153 hr = pNode->SetNodeSlot(*pnNodeSlot); 1154 } 1155 } 1156 1157 pNode->Release(); 1158 return hr; 1159 } 1160 1161 STDMETHODIMP CMruPidlList::QueryPidl( 1162 LPCITEMIDLIST pidl, 1163 UINT cSlots, 1164 UINT *pnNodeSlots, 1165 UINT *pcNodeSlots) 1166 { 1167 TRACE("%p -> %p %u %p %p\n", this, pidl, cSlots, pnNodeSlots, pcNodeSlots); 1168 1169 CSafeMutex mutex; 1170 HRESULT hr = mutex.Enter(m_hMutex); 1171 if (FAILED(hr)) 1172 { 1173 ERR("0x%08lX\n", hr); 1174 return hr; 1175 } 1176 1177 *pcNodeSlots = 0; 1178 1179 CMruNode *pNode; 1180 hr = GetNode(FALSE, pidl, &pNode); 1181 if (FAILED(hr)) 1182 { 1183 ERR("0x%08lX\n", hr); 1184 return hr; 1185 } 1186 1187 while (pNode && *pcNodeSlots < cSlots) 1188 { 1189 CMruNode *pParent = pNode->GetParent(); 1190 if (SUCCEEDED(pNode->GetNodeSlot(&pnNodeSlots[*pcNodeSlots]))) 1191 ++(*pcNodeSlots); 1192 else if (hr == S_OK && !*pcNodeSlots) 1193 hr = S_FALSE; 1194 1195 pNode->Release(); 1196 pNode = pParent; 1197 } 1198 1199 if (pNode) 1200 pNode->Release(); 1201 1202 if (SUCCEEDED(hr) && !*pcNodeSlots) 1203 hr = E_FAIL; 1204 1205 return hr; 1206 } 1207 1208 STDMETHODIMP CMruPidlList::PruneKids(LPCITEMIDLIST pidl) 1209 { 1210 TRACE("%p -> %p\n", this, pidl); 1211 1212 CSafeMutex mutex; 1213 HRESULT hr = mutex.Enter(m_hMutex); 1214 if (FAILED(hr)) 1215 { 1216 ERR("0x%08lX\n", hr); 1217 return hr; 1218 } 1219 1220 if (!_LoadNodeSlots()) 1221 return hr; 1222 1223 CMruNode *pNode; 1224 hr = GetNode(FALSE, pidl, &pNode); 1225 if (FAILED(hr)) 1226 return hr; 1227 1228 if (hr == S_OK) 1229 hr = pNode->Clear(this); 1230 else 1231 hr = E_FAIL; 1232 1233 pNode->Release(); 1234 1235 _SaveNodeSlots(); 1236 return hr; 1237 } 1238 1239 void CMruPidlList::EmptyNodeSlot(UINT nNodeSlot) 1240 { 1241 m_pbNodeSlots[nNodeSlot - 1] = 0; // nNodeSlot is 1-base 1242 m_bNeedSave = TRUE; 1243 } 1244 1245 EXTERN_C HRESULT CMruPidlList_CreateInstance(DWORD_PTR dwUnused1, void **ppv, DWORD_PTR dwUnused3) 1246 { 1247 UNREFERENCED_PARAMETER(dwUnused1); 1248 UNREFERENCED_PARAMETER(dwUnused3); 1249 1250 TRACE("%p %p %p\n", dwUnused1, ppv, dwUnused3); 1251 1252 if (!ppv) 1253 return E_POINTER; 1254 1255 *ppv = NULL; 1256 1257 CMruPidlList *pMruList = new CMruPidlList(); 1258 if (pMruList == NULL) 1259 return E_OUTOFMEMORY; 1260 1261 *ppv = static_cast<IMruPidlList*>(pMruList); 1262 TRACE("%p\n", *ppv); 1263 return S_OK; 1264 } 1265 1266 HRESULT CMruNode::Clear(CMruPidlList *pList) 1267 { 1268 UINT uSlot, nNodeSlot; 1269 HRESULT hr; 1270 1271 while (SUCCEEDED(_GetSlot(0, &uSlot))) 1272 { 1273 CMruNode *pNode; 1274 hr = _CreateNode(uSlot, &pNode); 1275 if (SUCCEEDED(hr)) 1276 { 1277 hr = pNode->GetNodeSlot(&nNodeSlot); 1278 if (SUCCEEDED(hr)) 1279 pList->EmptyNodeSlot(nNodeSlot); 1280 1281 pNode->Clear(pList); 1282 pNode->Release(); 1283 } 1284 Delete(0); 1285 } 1286 1287 return S_OK; 1288 } 1289 1290 class CMruClassFactory : public IClassFactory 1291 { 1292 protected: 1293 LONG m_cRefs = 1; 1294 1295 public: 1296 CMruClassFactory() 1297 { 1298 SHDOCVW_LockModule(); 1299 } 1300 virtual ~CMruClassFactory() 1301 { 1302 SHDOCVW_UnlockModule(); 1303 } 1304 1305 // IUnknown methods 1306 STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj) override; 1307 STDMETHODIMP_(ULONG) AddRef() override 1308 { 1309 return ::InterlockedIncrement(&m_cRefs); 1310 } 1311 STDMETHODIMP_(ULONG) Release() 1312 { 1313 if (::InterlockedDecrement(&m_cRefs) == 0) 1314 { 1315 delete this; 1316 return 0; 1317 } 1318 return m_cRefs; 1319 } 1320 1321 // IClassFactory methods 1322 STDMETHODIMP CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObject); 1323 STDMETHODIMP LockServer(BOOL fLock); 1324 1325 static void* operator new(size_t size) 1326 { 1327 return ::LocalAlloc(LPTR, size); 1328 } 1329 static void operator delete(void *ptr) 1330 { 1331 ::LocalFree(ptr); 1332 } 1333 }; 1334 1335 STDMETHODIMP CMruClassFactory::QueryInterface(REFIID riid, void **ppvObj) 1336 { 1337 if (!ppvObj) 1338 return E_POINTER; 1339 if (IsEqualGUID(riid, IID_IClassFactory) || IsEqualGUID(riid, IID_IUnknown)) 1340 { 1341 *ppvObj = static_cast<IClassFactory*>(this); 1342 AddRef(); 1343 return S_OK; 1344 } 1345 ERR("%s: E_NOINTERFACE\n", debugstr_guid(&riid)); 1346 return E_NOINTERFACE; 1347 } 1348 1349 STDMETHODIMP CMruClassFactory::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObject) 1350 { 1351 if (pUnkOuter) 1352 return CLASS_E_NOAGGREGATION; 1353 1354 if (IsEqualGUID(riid, IID_IMruDataList)) 1355 return CMruLongList_CreateInstance(0, ppvObject, 0); 1356 1357 if (IsEqualGUID(riid, IID_IMruPidlList)) 1358 return CMruPidlList_CreateInstance(0, ppvObject, 0); 1359 1360 return E_NOINTERFACE; 1361 } 1362 1363 STDMETHODIMP CMruClassFactory::LockServer(BOOL fLock) 1364 { 1365 if (fLock) 1366 SHDOCVW_LockModule(); 1367 else 1368 SHDOCVW_UnlockModule(); 1369 return S_OK; 1370 } 1371 1372 EXTERN_C HRESULT CMruClassFactory_CreateInstance(REFIID riid, void **ppv) 1373 { 1374 CMruClassFactory *pFactory = new CMruClassFactory(); 1375 if (!pFactory) 1376 return E_OUTOFMEMORY; 1377 1378 HRESULT hr = pFactory->QueryInterface(riid, ppv); 1379 pFactory->Release(); 1380 return hr; 1381 } 1382