xref: /reactos/dll/win32/shdocvw/mrulist.cpp (revision 960a305e)
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:
CSafeMutex()42     CSafeMutex() : m_hMutex(NULL)
43     {
44     }
~CSafeMutex()45     ~CSafeMutex()
46     {
47         if (m_hMutex)
48         {
49             ::ReleaseMutex(m_hMutex);
50             m_hMutex = NULL;
51         }
52     }
53 
Enter(HANDLE hMutex)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;
AddRef()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 
operator new(size_t size)120     static void* operator new(size_t size)
121     {
122         return ::LocalAlloc(LPTR, size);
123     }
operator delete(void * ptr)124     static void operator delete(void *ptr)
125     {
126         ::LocalFree(ptr);
127     }
128 };
129 
CMruBase()130 CMruBase::CMruBase()
131 {
132     SHDOCVW_LockModule();
133 }
134 
~CMruBase()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 
QueryInterface(REFIID riid,void ** ppvObj)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 
STDMETHODIMP_(ULONG)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 
_LoadItem(UINT iSlot)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 
_GetSlotItem(UINT iSlot,SLOTITEMDATA ** ppItem)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 
_GetItem(UINT iSlot,SLOTITEMDATA ** ppItem)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 
_DeleteItem(UINT iSlot)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 
_CheckUsedSlots()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 
_AddItem(UINT iSlot,LPCVOID pvData,DWORD cbData)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
InitData(UINT cCapacity,UINT flags,HKEY hKey,LPCWSTR pszSubKey OPTIONAL,SLOTCOMPARE fnCompare OPTIONAL)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 
AddData(LPCVOID pvData,DWORD cbData,UINT * piSlot)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 
FindData(LPCVOID pvData,DWORD cbData,UINT * piSlot)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 
GetData(UINT iSlot,LPVOID pvData,DWORD cbData)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 
QueryInfo(UINT iSlot,UINT * piGotSlot,DWORD * pcbData)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 
Delete(UINT iSlot)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 
_IsEqual(const SLOTITEMDATA * pItem,LPCVOID pvData,UINT cbData) const386 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 
_DeleteValue(LPCWSTR pszValue)413 DWORD CMruBase::_DeleteValue(LPCWSTR pszValue)
414 {
415     return SHDeleteValueW(m_hKey, NULL, pszValue);
416 }
417 
_UseEmptySlot(UINT * piSlot)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:
CMruShortList()455     CMruShortList()
456     {
457     }
458 
~CMruShortList()459     ~CMruShortList() override
460     {
461         m_pszSlotData = (LPWSTR)::LocalFree(m_pszSlotData);
462     }
463 };
464 
_InitSlots()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 
_SaveSlots()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.
_UpdateSlots(UINT iSlot)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 
_SlotString(UINT iSlot,LPWSTR psz,DWORD cch)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 
_GetSlot(UINT iSlot,UINT * puSlot)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 
_RemoveSlot(UINT iSlot,UINT * puSlot)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:
CMruLongList()575     CMruLongList()
576     {
577     }
578 
~CMruLongList()579     ~CMruLongList() override
580     {
581         m_puSlotData = (UINT*)::LocalFree(m_puSlotData);
582     }
583 };
584 
_InitSlots()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 
_SaveSlots()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 
_UpdateSlots(UINT iSlot)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 
_SlotString(UINT iSlot,LPWSTR psz,DWORD cch)643 void CMruLongList::_SlotString(UINT iSlot, LPWSTR psz, DWORD cch)
644 {
645     StringCchPrintfW(psz, cch, L"%d", iSlot);
646 }
647 
_GetSlot(UINT iSlot,UINT * puSlot)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 
_RemoveSlot(UINT iSlot,UINT * puSlot)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 
_ImportShortList()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
CMruLongList_CreateInstance(DWORD_PTR dwUnused1,void ** ppv,DWORD_PTR dwUnused3)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:
CMruNode()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 
CMruNode(CMruNode * pParent,UINT iSlot)761 CMruNode::CMruNode(CMruNode *pParent, UINT iSlot)
762 {
763     m_iSlot = iSlot;
764     m_pParent = pParent;
765     pParent->AddRef();
766 }
767 
~CMruNode()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 
GetParent()783 CMruNode *CMruNode::GetParent()
784 {
785     if (m_pParent)
786         m_pParent->AddRef();
787     return m_pParent;
788 }
789 
_CreateNode(UINT iSlot,CMruNode ** ppNewNode)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 
GetNode(BOOL bAdd,LPCITEMIDLIST pidl,CMruNode ** ppNewNode)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 
BindToSlot(UINT iSlot,IShellFolder ** ppSF)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 
_InitLate()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 
_IsEqual(SLOTITEMDATA * pItem,LPCVOID pvData,UINT cbData)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 
GetNodeSlot(UINT * pnNodeSlot)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 
SetNodeSlot(UINT nNodeSlot)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 
_AddPidl(UINT iSlot,LPCITEMIDLIST pidl)897 HRESULT CMruNode::_AddPidl(UINT iSlot, LPCITEMIDLIST pidl)
898 {
899     return CMruBase::_AddItem(iSlot, pidl, sizeof(WORD) + pidl->mkid.cb);
900 }
901 
_DeleteValue(LPCWSTR pszValue)902 DWORD CMruNode::_DeleteValue(LPCWSTR pszValue)
903 {
904     CMruBase::_DeleteValue(pszValue);
905     return SHDeleteKeyW(m_hKey, pszValue);
906 }
907 
_FindPidl(LPCITEMIDLIST pidl,UINT * piSlot)908 HRESULT CMruNode::_FindPidl(LPCITEMIDLIST pidl, UINT *piSlot)
909 {
910     return FindData(pidl, sizeof(WORD) + pidl->mkid.cb, piSlot);
911 }
912 
_GetPidlSlot(LPCITEMIDLIST pidl,BOOL bAdd,UINT * piSlot)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 
RemoveLeast(UINT * pnNodeSlot)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:
CMruPidlList()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;
AddRef()989     STDMETHODIMP_(ULONG) AddRef() override
990     {
991         return CMruBase::AddRef();
992     }
Release()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 
~CMruPidlList()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 
QueryInterface(REFIID riid,void ** ppvObj)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 
_LoadNodeSlots()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 
_SaveNodeSlots()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 
_InitNodeSlots()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 
GetEmptySlot(UINT * pnNodeSlot)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 
InitList(UINT cMRUSize,HKEY hKey,LPCWSTR pszSubKey)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 
UsePidl(LPCITEMIDLIST pidl,UINT * pnNodeSlot)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 
QueryPidl(LPCITEMIDLIST pidl,UINT cSlots,UINT * pnNodeSlots,UINT * pcNodeSlots)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 
PruneKids(LPCITEMIDLIST pidl)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 
EmptyNodeSlot(UINT nNodeSlot)1239 void CMruPidlList::EmptyNodeSlot(UINT nNodeSlot)
1240 {
1241     m_pbNodeSlots[nNodeSlot - 1] = 0; // nNodeSlot is 1-base
1242     m_bNeedSave = TRUE;
1243 }
1244 
CMruPidlList_CreateInstance(DWORD_PTR dwUnused1,void ** ppv,DWORD_PTR dwUnused3)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 
Clear(CMruPidlList * pList)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:
CMruClassFactory()1296     CMruClassFactory()
1297     {
1298         SHDOCVW_LockModule();
1299     }
~CMruClassFactory()1300     virtual ~CMruClassFactory()
1301     {
1302         SHDOCVW_UnlockModule();
1303     }
1304 
1305     // IUnknown methods
1306     STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj) override;
AddRef()1307     STDMETHODIMP_(ULONG) AddRef() override
1308     {
1309         return ::InterlockedIncrement(&m_cRefs);
1310     }
Release()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 
operator new(size_t size)1325     static void* operator new(size_t size)
1326     {
1327         return ::LocalAlloc(LPTR, size);
1328     }
operator delete(void * ptr)1329     static void operator delete(void *ptr)
1330     {
1331         ::LocalFree(ptr);
1332     }
1333 };
1334 
QueryInterface(REFIID riid,void ** ppvObj)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 
CreateInstance(IUnknown * pUnkOuter,REFIID riid,void ** ppvObject)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 
LockServer(BOOL fLock)1363 STDMETHODIMP CMruClassFactory::LockServer(BOOL fLock)
1364 {
1365     if (fLock)
1366         SHDOCVW_LockModule();
1367     else
1368         SHDOCVW_UnlockModule();
1369     return S_OK;
1370 }
1371 
CMruClassFactory_CreateInstance(REFIID riid,void ** ppv)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