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 
CFolderViewCB()20     CFolderViewCB() : m_View(NULL) {}
~CFolderViewCB()21     virtual ~CFolderViewCB() {}
22 
MessageSFVCB(UINT uMsg,WPARAM wParam,LPARAM lParam)23     STDMETHODIMP MessageSFVCB(UINT uMsg, WPARAM wParam, LPARAM lParam) override
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 
STDMETHOD(Initialize)45     STDMETHOD(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 
CCommonFolder()72     CCommonFolder() :
73         m_shellPidl(NULL)
74     {
75     }
76 
~CCommonFolder()77     virtual ~CCommonFolder()
78     {
79         if (m_shellPidl)
80             ILFree(m_shellPidl);
81     }
82 
83     // IShellFolder
ParseDisplayName(HWND hwndOwner,LPBC pbcReserved,LPOLESTR lpszDisplayName,ULONG * pchEaten,LPITEMIDLIST * ppidl,ULONG * pdwAttributes)84     STDMETHODIMP ParseDisplayName(
85         HWND hwndOwner,
86         LPBC pbcReserved,
87         LPOLESTR lpszDisplayName,
88         ULONG *pchEaten,
89         LPITEMIDLIST *ppidl,
90         ULONG *pdwAttributes) override
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     STDMETHOD(EnumObjects)(
164         HWND hwndOwner,
165         SHCONTF grfFlags,
166         IEnumIDList **ppenumIDList) PURE;
167 
BindToObject(LPCITEMIDLIST pidl,LPBC pbcReserved,REFIID riid,void ** ppvOut)168     STDMETHODIMP BindToObject(
169         LPCITEMIDLIST pidl,
170         LPBC pbcReserved,
171         REFIID riid,
172         void **ppvOut) override
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     STDMETHOD(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 
STDMETHOD(ResolveSymLink)225     STDMETHOD(ResolveSymLink)(
226         const TItemId * info,
227         LPITEMIDLIST * fullPidl)
228     {
229         return E_NOTIMPL;
230     }
231 
232 public:
BindToStorage(LPCITEMIDLIST pidl,LPBC pbcReserved,REFIID riid,void ** ppvObj)233     STDMETHODIMP BindToStorage(
234         LPCITEMIDLIST pidl,
235         LPBC pbcReserved,
236         REFIID riid,
237         void **ppvObj) override
238     {
239         UNIMPLEMENTED;
240         return E_NOTIMPL;
241     }
242 
CompareIDs(LPARAM lParam,LPCITEMIDLIST pidl1,LPCITEMIDLIST pidl2)243     STDMETHODIMP CompareIDs(
244         LPARAM lParam,
245         LPCITEMIDLIST pidl1,
246         LPCITEMIDLIST pidl2) override
247     {
248         HRESULT hr;
249 
250         TRACE("CompareIDs %d\n", lParam);
251 
252         const TItemId * id1;
253         hr = GetInfoFromPidl(pidl1, &id1);
254         if (FAILED(hr))
255             return E_INVALIDARG;
256 
257         const TItemId * id2;
258         hr = GetInfoFromPidl(pidl2, &id2);
259         if (FAILED(hr))
260             return E_INVALIDARG;
261 
262         hr = CompareIDs(lParam, id1, id2);
263         if (hr != S_EQUAL)
264             return hr;
265 
266         // The wollowing snipped is basically SHELL32_CompareChildren
267 
268         PUIDLIST_RELATIVE rest1 = ILGetNext(pidl1);
269         PUIDLIST_RELATIVE rest2 = ILGetNext(pidl2);
270 
271         bool isEmpty1 = (rest1->mkid.cb == 0);
272         bool isEmpty2 = (rest2->mkid.cb == 0);
273 
274         if (isEmpty1 || isEmpty2)
275             return MAKE_COMPARE_HRESULT(isEmpty2 - isEmpty1);
276 
277         LPCITEMIDLIST first1 = ILCloneFirst(pidl1);
278         if (!first1)
279             return E_OUTOFMEMORY;
280 
281         CComPtr<IShellFolder> psfNext;
282         hr = BindToObject(first1, NULL, IID_PPV_ARG(IShellFolder, &psfNext));
283         if (FAILED_UNEXPECTEDLY(hr))
284             return hr;
285 
286         return psfNext->CompareIDs(lParam, rest1, rest2);
287     }
288 
289 protected:
STDMETHOD(CompareName)290     STDMETHOD(CompareName)(
291         LPARAM lParam,
292         const TItemId * first,
293         const TItemId * second)
294     {
295         bool f1 = IsFolder(first);
296         bool f2 = IsFolder(second);
297 
298         HRESULT hr = MAKE_COMPARE_HRESULT(f2 - f1);
299         if (hr != S_EQUAL)
300             return hr;
301 
302         bool canonical = (lParam & 0xFFFF0000) == SHCIDS_CANONICALONLY;
303         if (canonical)
304         {
305             // Shortcut: avoid comparing contents if not necessary when the results are not for display.
306             hr = MAKE_COMPARE_HRESULT(second->entryNameLength - first->entryNameLength);
307             if (hr != S_EQUAL)
308                 return hr;
309 
310             int minlength = min(first->entryNameLength, second->entryNameLength);
311             if (minlength > 0)
312             {
313                 hr = MAKE_COMPARE_HRESULT(memcmp(first->entryName, second->entryName, minlength));
314                 if (hr != S_EQUAL)
315                     return hr;
316             }
317 
318             return S_EQUAL;
319         }
320 
321         int minlength = min(first->entryNameLength, second->entryNameLength);
322         if (minlength > 0)
323         {
324             hr = MAKE_COMPARE_HRESULT(StrCmpNW(first->entryName, second->entryName, minlength / sizeof(WCHAR)));
325             if (hr != S_EQUAL)
326                 return hr;
327         }
328 
329         return MAKE_COMPARE_HRESULT(second->entryNameLength - first->entryNameLength);
330     }
331 
332 public:
CreateViewObject(HWND hwndOwner,REFIID riid,void ** ppvOut)333     STDMETHODIMP CreateViewObject(
334         HWND hwndOwner,
335         REFIID riid,
336         void **ppvOut) override
337     {
338         if (!IsEqualIID(riid, IID_IShellView))
339             return E_NOINTERFACE;
340 
341         _CComObject<CFolderViewCB> *pcb;
342 
343         HRESULT hr = _CComObject<CFolderViewCB>::CreateInstance(&pcb);
344         if (FAILED(hr))
345             return hr;
346 
347         pcb->AddRef();
348 
349         SFV_CREATE sfv;
350         sfv.cbSize = sizeof(sfv);
351         sfv.pshf = this;
352         sfv.psvOuter = NULL;
353         sfv.psfvcb = pcb;
354 
355         IShellView* view;
356 
357         hr = SHCreateShellFolderView(&sfv, &view);
358         if (FAILED(hr))
359             return hr;
360 
361         pcb->Initialize(view);
362 
363         pcb->Release();
364 
365         *ppvOut = view;
366 
367         return S_OK;
368     }
369 
GetAttributesOf(UINT cidl,PCUITEMID_CHILD_ARRAY apidl,SFGAOF * rgfInOut)370     STDMETHODIMP GetAttributesOf(
371         UINT cidl,
372         PCUITEMID_CHILD_ARRAY apidl,
373         SFGAOF *rgfInOut) override
374     {
375         const TItemId * info;
376 
377         TRACE("GetAttributesOf %d\n", cidl);
378 
379         if (cidl == 0)
380         {
381             *rgfInOut &= SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_BROWSABLE;
382             return S_OK;
383         }
384 
385         for (int i = 0; i < (int)cidl; i++)
386         {
387             PCUITEMID_CHILD pidl = apidl[i];
388 
389             HRESULT hr = GetInfoFromPidl(pidl, &info);
390             if (FAILED_UNEXPECTEDLY(hr))
391                 return hr;
392 
393             // Update attributes.
394             *rgfInOut = ConvertAttributes(info, rgfInOut);
395         }
396 
397         return S_OK;
398     }
399 
GetUIObjectOf(HWND hwndOwner,UINT cidl,PCUITEMID_CHILD_ARRAY apidl,REFIID riid,UINT * prgfInOut,void ** ppvOut)400     STDMETHODIMP GetUIObjectOf(
401         HWND hwndOwner,
402         UINT cidl,
403         PCUITEMID_CHILD_ARRAY apidl,
404         REFIID riid,
405         UINT *prgfInOut,
406         void **ppvOut) override
407     {
408         DWORD res;
409         TRACE("GetUIObjectOf\n");
410 
411         if (IsEqualIID(riid, IID_IContextMenu) ||
412             IsEqualIID(riid, IID_IContextMenu2) ||
413             IsEqualIID(riid, IID_IContextMenu3))
414         {
415             CComPtr<IContextMenu> pcm;
416 
417             HKEY keys[1];
418 
419             LPITEMIDLIST parent = m_shellPidl;
420 
421             CComPtr<IShellFolder> psfParent = this;
422 
423             LPCITEMIDLIST child;
424 
425             int nkeys = _countof(keys);
426             if (cidl == 1 && IsSymLink(apidl[0]))
427             {
428                 const TItemId * info;
429                 HRESULT hr = GetInfoFromPidl(apidl[0], &info);
430                 if (FAILED(hr))
431                     return hr;
432 
433                 LPITEMIDLIST target;
434                 hr = ResolveSymLink(info, &target);
435                 if (FAILED(hr))
436                     return hr;
437 
438                 CComPtr<IShellFolder> psfTarget;
439                 hr = ::SHBindToParent(target, IID_PPV_ARG(IShellFolder, &psfTarget), &child);
440                 if (FAILED(hr))
441                 {
442                     ILFree(target);
443                     return hr;
444                 }
445 
446                 parent = ILClone(target);
447                 ILRemoveLastID(parent);
448                 psfParent = psfTarget;
449 
450                 apidl = &child;
451             }
452 
453             if (cidl == 1 && IsFolder(apidl[0]))
454             {
455                 res = RegOpenKey(HKEY_CLASSES_ROOT, L"Folder", keys + 0);
456                 if (!NT_SUCCESS(res))
457                     return HRESULT_FROM_NT(res);
458             }
459             else
460             {
461                 nkeys = 0;
462             }
463 
464             HRESULT hr = CDefFolderMenu_Create2(parent, hwndOwner, cidl, apidl, psfParent, DefCtxMenuCallback, nkeys, keys, &pcm);
465             if (FAILED_UNEXPECTEDLY(hr))
466                 return hr;
467 
468             return pcm->QueryInterface(riid, ppvOut);
469         }
470 
471         if (IsEqualIID(riid, IID_IExtractIconW))
472         {
473             return ShellObjectCreatorInit<TExtractIcon>(m_NtPath, m_shellPidl, cidl, apidl, riid, ppvOut);
474         }
475 
476         if (IsEqualIID(riid, IID_IDataObject))
477         {
478             return CIDLData_CreateFromIDArray(m_shellPidl, cidl, apidl, (IDataObject**)ppvOut);
479         }
480 
481         if (IsEqualIID(riid, IID_IQueryAssociations))
482         {
483             if (cidl == 1 && IsFolder(apidl[0]))
484             {
485                 CComPtr<IQueryAssociations> pqa;
486                 HRESULT hr = AssocCreate(CLSID_QueryAssociations, IID_PPV_ARG(IQueryAssociations, &pqa));
487                 if (FAILED_UNEXPECTEDLY(hr))
488                     return hr;
489 
490                 hr = pqa->Init(ASSOCF_INIT_DEFAULTTOFOLDER, L"NTObjShEx.NTDirectory", NULL, hwndOwner);
491                 if (FAILED_UNEXPECTEDLY(hr))
492                     return hr;
493 
494                 return pqa->QueryInterface(riid, ppvOut);
495             }
496         }
497 
498         return E_NOTIMPL;
499     }
500 
GetDisplayNameOf(LPCITEMIDLIST pidl,SHGDNF uFlags,STRRET * lpName)501     STDMETHODIMP GetDisplayNameOf(
502         LPCITEMIDLIST pidl,
503         SHGDNF uFlags,
504         STRRET *lpName) override
505     {
506         const TItemId * info;
507 
508         TRACE("GetDisplayNameOf %p\n", pidl);
509 
510         HRESULT hr = GetInfoFromPidl(pidl, &info);
511         if (FAILED_UNEXPECTEDLY(hr))
512             return hr;
513 
514         if (GET_SHGDN_FOR(uFlags) & SHGDN_FOREDITING)
515         {
516             hr = MakeStrRetFromString(info->entryName, info->entryNameLength, lpName);
517             if (FAILED_UNEXPECTEDLY(hr))
518                 return hr;
519         }
520 
521         WCHAR path[MAX_PATH] = { 0 };
522 
523         if (GET_SHGDN_FOR(uFlags) & SHGDN_FORPARSING)
524         {
525             if (GET_SHGDN_RELATION(uFlags) != SHGDN_INFOLDER)
526             {
527                 hr = GetFullName(m_shellPidl, uFlags, path, _countof(path));
528                 if (FAILED_UNEXPECTEDLY(hr))
529                     return hr;
530             }
531         }
532 
533         PathAppendW(path, info->entryName);
534 
535         LPCITEMIDLIST pidlNext = ILGetNext(pidl);
536         if (pidlNext && pidlNext->mkid.cb > 0)
537         {
538             LPITEMIDLIST pidlFirst = ILCloneFirst(pidl);
539 
540             CComPtr<IShellFolder> psfChild;
541             hr = BindToObject(pidlFirst, NULL, IID_PPV_ARG(IShellFolder, &psfChild));
542             if (FAILED_UNEXPECTEDLY(hr))
543                 return hr;
544 
545             WCHAR temp[MAX_PATH];
546             STRRET childName;
547 
548             hr = psfChild->GetDisplayNameOf(pidlNext, uFlags | SHGDN_INFOLDER, &childName);
549             if (FAILED_UNEXPECTEDLY(hr))
550                 return hr;
551 
552             hr = StrRetToBufW(&childName, pidlNext, temp, _countof(temp));
553             if (FAILED_UNEXPECTEDLY(hr))
554                 return hr;
555 
556             PathAppendW(path, temp);
557 
558             ILFree(pidlFirst);
559         }
560 
561         hr = MakeStrRetFromString(path, lpName);
562         if (FAILED_UNEXPECTEDLY(hr))
563             return hr;
564 
565         return S_OK;
566     }
567 
SetNameOf(HWND hwnd,LPCITEMIDLIST pidl,LPCOLESTR lpszName,SHGDNF uFlags,LPITEMIDLIST * ppidlOut)568     STDMETHODIMP SetNameOf(
569         HWND hwnd,
570         LPCITEMIDLIST pidl,
571         LPCOLESTR lpszName,
572         SHGDNF uFlags,
573         LPITEMIDLIST *ppidlOut) override
574     {
575         UNIMPLEMENTED;
576         return E_NOTIMPL;
577     }
578 
579     // IShellFolder2
GetDefaultSearchGUID(GUID * lpguid)580     STDMETHODIMP GetDefaultSearchGUID(GUID *lpguid) override
581     {
582         UNIMPLEMENTED;
583         return E_NOTIMPL;
584     }
585 
EnumSearches(IEnumExtraSearch ** ppenum)586     STDMETHODIMP EnumSearches(IEnumExtraSearch **ppenum) override
587     {
588         UNIMPLEMENTED;
589         return E_NOTIMPL;
590     }
591 
GetDefaultColumn(DWORD dwReserved,ULONG * pSort,ULONG * pDisplay)592     STDMETHODIMP GetDefaultColumn(
593         DWORD dwReserved,
594         ULONG *pSort,
595         ULONG *pDisplay) override
596     {
597         if (pSort)
598             *pSort = 0;
599         if (pDisplay)
600             *pDisplay = 0;
601         return S_OK;
602     }
603 
604     STDMETHOD(GetDefaultColumnState)(
605         UINT iColumn,
606         SHCOLSTATEF *pcsFlags) PURE;
607 
608     STDMETHOD(GetDetailsEx)(
609         LPCITEMIDLIST pidl,
610         const SHCOLUMNID *pscid,
611         VARIANT *pv) PURE;
612 
613     STDMETHOD(GetDetailsOf)(
614         LPCITEMIDLIST pidl,
615         UINT iColumn,
616         SHELLDETAILS *psd) PURE;
617 
618     STDMETHOD(MapColumnToSCID)(
619         UINT iColumn,
620         SHCOLUMNID *pscid) PURE;
621 
622     // IPersist
GetClassID(CLSID * lpClassId)623     STDMETHODIMP GetClassID(CLSID *lpClassId) override
624     {
625         if (!lpClassId)
626             return E_POINTER;
627 
628         *lpClassId = CLSID_NtObjectFolder;
629         return S_OK;
630     }
631 
632     // IPersistFolder
Initialize(PCIDLIST_ABSOLUTE pidl)633     STDMETHODIMP Initialize(PCIDLIST_ABSOLUTE pidl) override
634     {
635         m_shellPidl = ILClone(pidl);
636 
637         StringCbCopyW(m_NtPath, sizeof(m_NtPath), L"\\");
638 
639         return S_OK;
640     }
641 
642     // IPersistFolder2
GetCurFolder(PIDLIST_ABSOLUTE * pidl)643     STDMETHODIMP GetCurFolder(PIDLIST_ABSOLUTE * pidl) override
644     {
645         if (pidl)
646             *pidl = ILClone(m_shellPidl);
647         if (!m_shellPidl)
648             return S_FALSE;
649         return S_OK;
650     }
651 
652     // Internal
653 protected:
654     STDMETHOD(CompareIDs)(
655         LPARAM lParam,
656         const TItemId * first,
657         const TItemId * second) PURE;
658 
659     STDMETHOD_(ULONG, ConvertAttributes)(
660         const TItemId * entry,
661         PULONG inMask) PURE;
662 
STDMETHOD_(BOOL,IsFolder)663     STDMETHOD_(BOOL, IsFolder)(LPCITEMIDLIST pcidl)
664     {
665         const TItemId * info;
666 
667         HRESULT hr = GetInfoFromPidl(pcidl, &info);
668         if (FAILED(hr))
669             return hr;
670 
671         return IsFolder(info);
672     }
673 
674     STDMETHOD_(BOOL, IsFolder)(const TItemId * info) PURE;
675 
STDMETHOD_(BOOL,IsSymLink)676     STDMETHOD_(BOOL, IsSymLink)(LPCITEMIDLIST pcidl)
677     {
678         const TItemId * info;
679 
680         HRESULT hr = GetInfoFromPidl(pcidl, &info);
681         if (FAILED(hr))
682             return hr;
683 
684         return IsSymLink(info);
685     }
686 
STDMETHOD_(BOOL,IsSymLink)687     STDMETHOD_(BOOL, IsSymLink)(const TItemId * info)
688     {
689         return FALSE;
690     }
691 
692     virtual HRESULT GetInfoFromPidl(LPCITEMIDLIST pcidl, const TItemId ** pentry) PURE;
693 
694 public:
DefCtxMenuCallback(IShellFolder *,HWND,IDataObject *,UINT uMsg,WPARAM,LPARAM)695     static HRESULT CALLBACK DefCtxMenuCallback(IShellFolder * /*psf*/, HWND /*hwnd*/, IDataObject * /*pdtobj*/, UINT uMsg, WPARAM /*wParam*/, LPARAM /*lParam*/)
696     {
697         switch (uMsg)
698         {
699             case DFM_MERGECONTEXTMENU:
700                 return S_OK;
701 
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 };
722