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