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