xref: /reactos/dll/win32/shdocvw/mrulist.cpp (revision d41dec2e)
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