1 /*
2  * PROJECT:     NT Object Namespace shell extension
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Folder view class header and implementation
5  * COPYRIGHT:   Copyright 2015-2017 David Quintana <gigaherz@gmail.com>
6  */
7 
8 #pragma once
9 
10 extern const GUID CLSID_NtObjectFolder;
11 
12 class CFolderViewCB :
13     public CComObjectRootEx<CComMultiThreadModelNoCS>,
14     public IShellFolderViewCB
15 {
16     IShellView* m_View;
17 
18 public:
19 
20     CFolderViewCB() : m_View(NULL) {}
21     virtual ~CFolderViewCB() {}
22 
23     virtual HRESULT STDMETHODCALLTYPE MessageSFVCB(UINT uMsg, WPARAM wParam, LPARAM lParam)
24     {
25         switch (uMsg)
26         {
27             case SFVM_DEFVIEWMODE:
28             {
29                 FOLDERVIEWMODE* pViewMode = (FOLDERVIEWMODE*)lParam;
30                 *pViewMode = FVM_DETAILS;
31                 return S_OK;
32             }
33 
34             case SFVM_COLUMNCLICK:
35                 return S_FALSE;
36 
37             case SFVM_BACKGROUNDENUM:
38                 return S_OK;
39         }
40 
41         DbgPrint("MessageSFVCB unimplemented %d %08x %08x\n", uMsg, wParam, lParam);
42         return E_NOTIMPL;
43     }
44 
45     virtual HRESULT STDMETHODCALLTYPE Initialize(IShellView* psv)
46     {
47         m_View = psv;
48         return S_OK;
49     }
50 
51     DECLARE_NOT_AGGREGATABLE(CFolderViewCB)
52     DECLARE_PROTECT_FINAL_CONSTRUCT()
53 
54     BEGIN_COM_MAP(CFolderViewCB)
55         COM_INTERFACE_ENTRY_IID(IID_IShellFolderViewCB, IShellFolderViewCB)
56     END_COM_MAP()
57 };
58 
59 template<class TSelf, typename TItemId, class TExtractIcon>
60 class CCommonFolder :
61     public CComObjectRootEx<CComMultiThreadModelNoCS>,
62     public IShellFolder2,
63     public IPersistFolder2
64 {
65 protected:
66     WCHAR m_NtPath[MAX_PATH];
67 
68     LPITEMIDLIST m_shellPidl;
69 
70 public:
71 
72     CCommonFolder() :
73         m_shellPidl(NULL)
74     {
75     }
76 
77     virtual ~CCommonFolder()
78     {
79         if (m_shellPidl)
80             ILFree(m_shellPidl);
81     }
82 
83     // IShellFolder
84     virtual HRESULT STDMETHODCALLTYPE ParseDisplayName(
85         HWND hwndOwner,
86         LPBC pbcReserved,
87         LPOLESTR lpszDisplayName,
88         ULONG *pchEaten,
89         LPITEMIDLIST *ppidl,
90         ULONG *pdwAttributes)
91     {
92         if (!ppidl)
93             return E_POINTER;
94 
95         if (pchEaten)
96             *pchEaten = 0;
97 
98         if (pdwAttributes)
99             *pdwAttributes = 0;
100 
101         TRACE("CCommonFolder::ParseDisplayName name=%S (ntPath=%S)\n", lpszDisplayName, m_NtPath);
102 
103         const TItemId * info;
104         IEnumIDList * it;
105         HRESULT hr = EnumObjects(hwndOwner, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &it);
106         if (FAILED(hr))
107             return hr;
108 
109         PWSTR end = StrChrW(lpszDisplayName, '\\');
110         int length = end ? end - lpszDisplayName : wcslen(lpszDisplayName);
111 
112         while (TRUE)
113         {
114             hr = it->Next(1, ppidl, NULL);
115 
116             if (FAILED(hr))
117                 return hr;
118 
119             if (hr != S_OK)
120                 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
121 
122             hr = GetInfoFromPidl(*ppidl, &info);
123             if (FAILED_UNEXPECTEDLY(hr))
124                 return hr;
125 
126             if (StrCmpNW(info->entryName, lpszDisplayName, length) == 0)
127                 break;
128         }
129 
130         // if has remaining path to parse (and the path didn't just terminate in a backslash)
131         if (end && wcslen(end) > 1)
132         {
133             CComPtr<IShellFolder> psfChild;
134             hr = BindToObject(*ppidl, pbcReserved, IID_PPV_ARG(IShellFolder, &psfChild));
135             if (FAILED_UNEXPECTEDLY(hr))
136                 return hr;
137 
138             LPITEMIDLIST child;
139             hr = psfChild->ParseDisplayName(hwndOwner, pbcReserved, end + 1, pchEaten, &child, pdwAttributes);
140             if (FAILED(hr))
141                 return hr;
142 
143             LPITEMIDLIST old = *ppidl;
144             *ppidl = ILCombine(old, child);
145             ILFree(old);
146 
147             // Count the path separator
148             if (pchEaten)
149                 (*pchEaten) += 1;
150         }
151         else
152         {
153             if (pdwAttributes)
154                 *pdwAttributes = ConvertAttributes(info, pdwAttributes);
155         }
156 
157         if (pchEaten)
158             *pchEaten += wcslen(info->entryName);
159 
160         return S_OK;
161     }
162 
163     virtual HRESULT STDMETHODCALLTYPE EnumObjects(
164         HWND hwndOwner,
165         SHCONTF grfFlags,
166         IEnumIDList **ppenumIDList) PURE;
167 
168     virtual HRESULT STDMETHODCALLTYPE BindToObject(
169         LPCITEMIDLIST pidl,
170         LPBC pbcReserved,
171         REFIID riid,
172         void **ppvOut)
173     {
174         const TItemId * info;
175 
176         if (IsEqualIID(riid, IID_IShellFolder))
177         {
178             HRESULT hr = GetInfoFromPidl(pidl, &info);
179             if (FAILED_UNEXPECTEDLY(hr))
180                 return hr;
181 
182             WCHAR path[MAX_PATH];
183 
184             StringCbCopyW(path, sizeof(path), m_NtPath);
185             PathAppendW(path, info->entryName);
186 
187             LPITEMIDLIST first = ILCloneFirst(pidl);
188             LPCITEMIDLIST rest = ILGetNext(pidl);
189 
190             LPITEMIDLIST fullPidl = ILCombine(m_shellPidl, first);
191 
192             CComPtr<IShellFolder> psfChild;
193             hr = InternalBindToObject(path, info, first, rest, fullPidl, pbcReserved, &psfChild);
194 
195             ILFree(fullPidl);
196             ILFree(first);
197 
198             if (FAILED(hr))
199                 return hr;
200 
201             if (hr == S_FALSE)
202                 return S_OK;
203 
204             if (rest->mkid.cb > 0)
205             {
206                 return psfChild->BindToObject(rest, pbcReserved, riid, ppvOut);
207             }
208 
209             return psfChild->QueryInterface(riid, ppvOut);
210         }
211 
212         return E_NOTIMPL;
213     }
214 
215 protected:
216     virtual HRESULT STDMETHODCALLTYPE InternalBindToObject(
217         PWSTR path,
218         const TItemId * info,
219         LPITEMIDLIST first,
220         LPCITEMIDLIST rest,
221         LPITEMIDLIST fullPidl,
222         LPBC pbcReserved,
223         IShellFolder** ppsfChild) PURE;
224 
225     virtual HRESULT STDMETHODCALLTYPE ResolveSymLink(
226         const TItemId * info,
227         LPITEMIDLIST * fullPidl)
228     {
229         return E_NOTIMPL;
230     }
231 
232 public:
233 
234     virtual HRESULT STDMETHODCALLTYPE BindToStorage(
235         LPCITEMIDLIST pidl,
236         LPBC pbcReserved,
237         REFIID riid,
238         void **ppvObj)
239     {
240         UNIMPLEMENTED;
241         return E_NOTIMPL;
242     }
243 
244     virtual HRESULT STDMETHODCALLTYPE CompareIDs(
245         LPARAM lParam,
246         LPCITEMIDLIST pidl1,
247         LPCITEMIDLIST pidl2)
248     {
249         HRESULT hr;
250 
251         TRACE("CompareIDs %d\n", lParam);
252 
253         const TItemId * id1;
254         hr = GetInfoFromPidl(pidl1, &id1);
255         if (FAILED(hr))
256             return E_INVALIDARG;
257 
258         const TItemId * id2;
259         hr = GetInfoFromPidl(pidl2, &id2);
260         if (FAILED(hr))
261             return E_INVALIDARG;
262 
263         hr = CompareIDs(lParam, id1, id2);
264         if (hr != S_EQUAL)
265             return hr;
266 
267         // The wollowing snipped is basically SHELL32_CompareChildren
268 
269         PUIDLIST_RELATIVE rest1 = ILGetNext(pidl1);
270         PUIDLIST_RELATIVE rest2 = ILGetNext(pidl2);
271 
272         bool isEmpty1 = (rest1->mkid.cb == 0);
273         bool isEmpty2 = (rest2->mkid.cb == 0);
274 
275         if (isEmpty1 || isEmpty2)
276             return MAKE_COMPARE_HRESULT(isEmpty2 - isEmpty1);
277 
278         LPCITEMIDLIST first1 = ILCloneFirst(pidl1);
279         if (!first1)
280             return E_OUTOFMEMORY;
281 
282         CComPtr<IShellFolder> psfNext;
283         hr = BindToObject(first1, NULL, IID_PPV_ARG(IShellFolder, &psfNext));
284         if (FAILED_UNEXPECTEDLY(hr))
285             return hr;
286 
287         return psfNext->CompareIDs(lParam, rest1, rest2);
288     }
289 
290 protected:
291     virtual HRESULT STDMETHODCALLTYPE CompareName(
292         LPARAM lParam,
293         const TItemId * first,
294         const TItemId * second)
295     {
296         bool f1 = IsFolder(first);
297         bool f2 = IsFolder(second);
298 
299         HRESULT hr = MAKE_COMPARE_HRESULT(f2 - f1);
300         if (hr != S_EQUAL)
301             return hr;
302 
303         bool canonical = (lParam & 0xFFFF0000) == SHCIDS_CANONICALONLY;
304         if (canonical)
305         {
306             // Shortcut: avoid comparing contents if not necessary when the results are not for display.
307             hr = MAKE_COMPARE_HRESULT(second->entryNameLength - first->entryNameLength);
308             if (hr != S_EQUAL)
309                 return hr;
310 
311             int minlength = min(first->entryNameLength, second->entryNameLength);
312             if (minlength > 0)
313             {
314                 hr = MAKE_COMPARE_HRESULT(memcmp(first->entryName, second->entryName, minlength));
315                 if (hr != S_EQUAL)
316                     return hr;
317             }
318 
319             return S_EQUAL;
320         }
321 
322         int minlength = min(first->entryNameLength, second->entryNameLength);
323         if (minlength > 0)
324         {
325             hr = MAKE_COMPARE_HRESULT(StrCmpNW(first->entryName, second->entryName, minlength / sizeof(WCHAR)));
326             if (hr != S_EQUAL)
327                 return hr;
328         }
329 
330         return MAKE_COMPARE_HRESULT(second->entryNameLength - first->entryNameLength);
331     }
332 
333 public:
334     virtual HRESULT STDMETHODCALLTYPE CreateViewObject(
335         HWND hwndOwner,
336         REFIID riid,
337         void **ppvOut)
338     {
339         if (!IsEqualIID(riid, IID_IShellView))
340             return E_NOINTERFACE;
341 
342         _CComObject<CFolderViewCB> *pcb;
343 
344         HRESULT hr = _CComObject<CFolderViewCB>::CreateInstance(&pcb);
345         if (FAILED(hr))
346             return hr;
347 
348         pcb->AddRef();
349 
350         SFV_CREATE sfv;
351         sfv.cbSize = sizeof(sfv);
352         sfv.pshf = this;
353         sfv.psvOuter = NULL;
354         sfv.psfvcb = pcb;
355 
356         IShellView* view;
357 
358         hr = SHCreateShellFolderView(&sfv, &view);
359         if (FAILED(hr))
360             return hr;
361 
362         pcb->Initialize(view);
363 
364         pcb->Release();
365 
366         *ppvOut = view;
367 
368         return S_OK;
369     }
370 
371     virtual HRESULT STDMETHODCALLTYPE GetAttributesOf(
372         UINT cidl,
373         PCUITEMID_CHILD_ARRAY apidl,
374         SFGAOF *rgfInOut)
375     {
376         const TItemId * info;
377 
378         TRACE("GetAttributesOf %d\n", cidl);
379 
380         if (cidl == 0)
381         {
382             *rgfInOut &= SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_BROWSABLE;
383             return S_OK;
384         }
385 
386         for (int i = 0; i < (int)cidl; i++)
387         {
388             PCUITEMID_CHILD pidl = apidl[i];
389 
390             HRESULT hr = GetInfoFromPidl(pidl, &info);
391             if (FAILED_UNEXPECTEDLY(hr))
392                 return hr;
393 
394             // Update attributes.
395             *rgfInOut = ConvertAttributes(info, rgfInOut);
396         }
397 
398         return S_OK;
399     }
400 
401     virtual HRESULT STDMETHODCALLTYPE GetUIObjectOf(
402         HWND hwndOwner,
403         UINT cidl,
404         PCUITEMID_CHILD_ARRAY apidl,
405         REFIID riid,
406         UINT *prgfInOut,
407         void **ppvOut)
408     {
409         DWORD res;
410         TRACE("GetUIObjectOf\n");
411 
412         if (IsEqualIID(riid, IID_IContextMenu) ||
413             IsEqualIID(riid, IID_IContextMenu2) ||
414             IsEqualIID(riid, IID_IContextMenu3))
415         {
416             CComPtr<IContextMenu> pcm;
417 
418             HKEY keys[1];
419 
420             LPITEMIDLIST parent = m_shellPidl;
421 
422             CComPtr<IShellFolder> psfParent = this;
423 
424             LPCITEMIDLIST child;
425 
426             int nkeys = _countof(keys);
427             if (cidl == 1 && IsSymLink(apidl[0]))
428             {
429                 const TItemId * info;
430                 HRESULT hr = GetInfoFromPidl(apidl[0], &info);
431                 if (FAILED(hr))
432                     return hr;
433 
434                 LPITEMIDLIST target;
435                 hr = ResolveSymLink(info, &target);
436                 if (FAILED(hr))
437                     return hr;
438 
439                 CComPtr<IShellFolder> psfTarget;
440                 hr = ::SHBindToParent(target, IID_PPV_ARG(IShellFolder, &psfTarget), &child);
441                 if (FAILED(hr))
442                 {
443                     ILFree(target);
444                     return hr;
445                 }
446 
447                 parent = ILClone(target);
448                 ILRemoveLastID(parent);
449                 psfParent = psfTarget;
450 
451                 apidl = &child;
452             }
453 
454             if (cidl == 1 && IsFolder(apidl[0]))
455             {
456                 res = RegOpenKey(HKEY_CLASSES_ROOT, L"Folder", keys + 0);
457                 if (!NT_SUCCESS(res))
458                     return HRESULT_FROM_NT(res);
459             }
460             else
461             {
462                 nkeys = 0;
463             }
464 
465             HRESULT hr = CDefFolderMenu_Create2(parent, hwndOwner, cidl, apidl, psfParent, DefCtxMenuCallback, nkeys, keys, &pcm);
466             if (FAILED_UNEXPECTEDLY(hr))
467                 return hr;
468 
469             return pcm->QueryInterface(riid, ppvOut);
470         }
471 
472         if (IsEqualIID(riid, IID_IExtractIconW))
473         {
474             return ShellObjectCreatorInit<TExtractIcon>(m_NtPath, m_shellPidl, cidl, apidl, riid, ppvOut);
475         }
476 
477         if (IsEqualIID(riid, IID_IDataObject))
478         {
479             return CIDLData_CreateFromIDArray(m_shellPidl, cidl, apidl, (IDataObject**)ppvOut);
480         }
481 
482         if (IsEqualIID(riid, IID_IQueryAssociations))
483         {
484             if (cidl == 1 && IsFolder(apidl[0]))
485             {
486                 CComPtr<IQueryAssociations> pqa;
487                 HRESULT hr = AssocCreate(CLSID_QueryAssociations, IID_PPV_ARG(IQueryAssociations, &pqa));
488                 if (FAILED_UNEXPECTEDLY(hr))
489                     return hr;
490 
491                 hr = pqa->Init(ASSOCF_INIT_DEFAULTTOFOLDER, L"NTObjShEx.NTDirectory", NULL, hwndOwner);
492                 if (FAILED_UNEXPECTEDLY(hr))
493                     return hr;
494 
495                 return pqa->QueryInterface(riid, ppvOut);
496             }
497         }
498 
499         return E_NOTIMPL;
500     }
501 
502     virtual HRESULT STDMETHODCALLTYPE GetDisplayNameOf(
503         LPCITEMIDLIST pidl,
504         SHGDNF uFlags,
505         STRRET *lpName)
506     {
507         const TItemId * info;
508 
509         TRACE("GetDisplayNameOf %p\n", pidl);
510 
511         HRESULT hr = GetInfoFromPidl(pidl, &info);
512         if (FAILED_UNEXPECTEDLY(hr))
513             return hr;
514 
515         if (GET_SHGDN_FOR(uFlags) & SHGDN_FOREDITING)
516         {
517             hr = MakeStrRetFromString(info->entryName, info->entryNameLength, lpName);
518             if (FAILED_UNEXPECTEDLY(hr))
519                 return hr;
520         }
521 
522         WCHAR path[MAX_PATH] = { 0 };
523 
524         if (GET_SHGDN_FOR(uFlags) & SHGDN_FORPARSING)
525         {
526             if (GET_SHGDN_RELATION(uFlags) != SHGDN_INFOLDER)
527             {
528                 hr = GetFullName(m_shellPidl, uFlags, path, _countof(path));
529                 if (FAILED_UNEXPECTEDLY(hr))
530                     return hr;
531             }
532         }
533 
534         PathAppendW(path, info->entryName);
535 
536         LPCITEMIDLIST pidlNext = ILGetNext(pidl);
537         if (pidlNext && pidlNext->mkid.cb > 0)
538         {
539             LPITEMIDLIST pidlFirst = ILCloneFirst(pidl);
540 
541             CComPtr<IShellFolder> psfChild;
542             hr = BindToObject(pidlFirst, NULL, IID_PPV_ARG(IShellFolder, &psfChild));
543             if (FAILED_UNEXPECTEDLY(hr))
544                 return hr;
545 
546             WCHAR temp[MAX_PATH];
547             STRRET childName;
548 
549             hr = psfChild->GetDisplayNameOf(pidlNext, uFlags | SHGDN_INFOLDER, &childName);
550             if (FAILED_UNEXPECTEDLY(hr))
551                 return hr;
552 
553             hr = StrRetToBufW(&childName, pidlNext, temp, _countof(temp));
554             if (FAILED_UNEXPECTEDLY(hr))
555                 return hr;
556 
557             PathAppendW(path, temp);
558 
559             ILFree(pidlFirst);
560         }
561 
562         hr = MakeStrRetFromString(path, lpName);
563         if (FAILED_UNEXPECTEDLY(hr))
564             return hr;
565 
566         return S_OK;
567     }
568 
569     virtual HRESULT STDMETHODCALLTYPE SetNameOf(
570         HWND hwnd,
571         LPCITEMIDLIST pidl,
572         LPCOLESTR lpszName,
573         SHGDNF uFlags,
574         LPITEMIDLIST *ppidlOut)
575     {
576         UNIMPLEMENTED;
577         return E_NOTIMPL;
578     }
579 
580     // IShellFolder2
581     virtual HRESULT STDMETHODCALLTYPE GetDefaultSearchGUID(
582         GUID *lpguid)
583     {
584         UNIMPLEMENTED;
585         return E_NOTIMPL;
586     }
587 
588     virtual HRESULT STDMETHODCALLTYPE EnumSearches(
589         IEnumExtraSearch **ppenum)
590     {
591         UNIMPLEMENTED;
592         return E_NOTIMPL;
593     }
594 
595     virtual HRESULT STDMETHODCALLTYPE GetDefaultColumn(
596         DWORD dwReserved,
597         ULONG *pSort,
598         ULONG *pDisplay)
599     {
600         if (pSort)
601             *pSort = 0;
602         if (pDisplay)
603             *pDisplay = 0;
604         return S_OK;
605     }
606 
607     virtual HRESULT STDMETHODCALLTYPE GetDefaultColumnState(
608         UINT iColumn,
609         SHCOLSTATEF *pcsFlags) PURE;
610 
611     virtual HRESULT STDMETHODCALLTYPE GetDetailsEx(
612         LPCITEMIDLIST pidl,
613         const SHCOLUMNID *pscid,
614         VARIANT *pv) PURE;
615 
616     virtual HRESULT STDMETHODCALLTYPE GetDetailsOf(
617         LPCITEMIDLIST pidl,
618         UINT iColumn,
619         SHELLDETAILS *psd) PURE;
620 
621     virtual HRESULT STDMETHODCALLTYPE MapColumnToSCID(
622         UINT iColumn,
623         SHCOLUMNID *pscid) PURE;
624 
625     // IPersist
626     virtual HRESULT STDMETHODCALLTYPE GetClassID(CLSID *lpClassId)
627     {
628         if (!lpClassId)
629             return E_POINTER;
630 
631         *lpClassId = CLSID_NtObjectFolder;
632         return S_OK;
633     }
634 
635     // IPersistFolder
636     virtual HRESULT STDMETHODCALLTYPE Initialize(PCIDLIST_ABSOLUTE pidl)
637     {
638         m_shellPidl = ILClone(pidl);
639 
640         StringCbCopyW(m_NtPath, sizeof(m_NtPath), L"\\");
641 
642         return S_OK;
643     }
644 
645     // IPersistFolder2
646     virtual HRESULT STDMETHODCALLTYPE GetCurFolder(PIDLIST_ABSOLUTE * pidl)
647     {
648         if (pidl)
649             *pidl = ILClone(m_shellPidl);
650         if (!m_shellPidl)
651             return S_FALSE;
652         return S_OK;
653     }
654 
655     // Internal
656 protected:
657     virtual HRESULT STDMETHODCALLTYPE CompareIDs(
658         LPARAM lParam,
659         const TItemId * first,
660         const TItemId * second) PURE;
661 
662     virtual ULONG STDMETHODCALLTYPE ConvertAttributes(
663         const TItemId * entry,
664         PULONG inMask) PURE;
665 
666     virtual BOOL STDMETHODCALLTYPE IsFolder(LPCITEMIDLIST pcidl)
667     {
668         const TItemId * info;
669 
670         HRESULT hr = GetInfoFromPidl(pcidl, &info);
671         if (FAILED(hr))
672             return hr;
673 
674         return IsFolder(info);
675     }
676 
677     virtual BOOL STDMETHODCALLTYPE IsFolder(const TItemId * info) PURE;
678 
679     virtual BOOL STDMETHODCALLTYPE IsSymLink(LPCITEMIDLIST pcidl)
680     {
681         const TItemId * info;
682 
683         HRESULT hr = GetInfoFromPidl(pcidl, &info);
684         if (FAILED(hr))
685             return hr;
686 
687         return IsSymLink(info);
688     }
689 
690     virtual BOOL STDMETHODCALLTYPE IsSymLink(const TItemId * info)
691     {
692         return FALSE;
693     }
694 
695     virtual HRESULT GetInfoFromPidl(LPCITEMIDLIST pcidl, const TItemId ** pentry) PURE;
696 
697 public:
698     static HRESULT CALLBACK DefCtxMenuCallback(IShellFolder * /*psf*/, HWND /*hwnd*/, IDataObject * /*pdtobj*/, UINT uMsg, WPARAM /*wParam*/, LPARAM /*lParam*/)
699     {
700         switch (uMsg)
701         {
702             case DFM_MERGECONTEXTMENU:
703                 return S_OK;
704 
705             case DFM_INVOKECOMMAND:
706             case DFM_INVOKECOMMANDEX:
707             case DFM_GETDEFSTATICID: // Required for Windows 7 to pick a default
708                 return S_FALSE;
709         }
710         return E_NOTIMPL;
711     }
712 
713     DECLARE_NOT_AGGREGATABLE(TSelf)
714     DECLARE_PROTECT_FINAL_CONSTRUCT()
715 
716     BEGIN_COM_MAP(TSelf)
717         COM_INTERFACE_ENTRY_IID(IID_IShellFolder, IShellFolder)
718         COM_INTERFACE_ENTRY_IID(IID_IShellFolder2, IShellFolder2)
719         COM_INTERFACE_ENTRY_IID(IID_IPersist, IPersist)
720         COM_INTERFACE_ENTRY_IID(IID_IPersistFolder, IPersistFolder)
721         COM_INTERFACE_ENTRY_IID(IID_IPersistFolder2, IPersistFolder2)
722     END_COM_MAP()
723 
724 };
725