1 /*
2  * Shell Menu Site
3  *
4  * Copyright 2014 David Quintana
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 #include "shellmenu.h"
21 #include <atlwin.h>
22 #include <shlwapi_undoc.h>
23 
24 #include "CMergedFolder.h"
25 
26 WINE_DEFAULT_DEBUG_CHANNEL(CMergedFolder);
27 
28 struct LocalPidlInfo
29 {
30     BOOL shared;
31     IShellFolder * parent;
32     LPITEMIDLIST pidl;
33     LPITEMIDLIST pidl2;
34     LPCWSTR parseName;
35 };
36 
37 class CEnumMergedFolder :
38     public CComObjectRootEx<CComMultiThreadModelNoCS>,
39     public IEnumIDList
40 {
41 
42 private:
43     CComPtr<IShellFolder> m_UserLocalFolder;
44     CComPtr<IShellFolder> m_AllUSersFolder;
45 
46     HWND m_HwndOwner;
47     SHCONTF m_Flags;
48 
49     HDSA m_hDsa;
50     UINT m_hDsaIndex;
51     UINT m_hDsaCount;
52 
53 public:
54     CEnumMergedFolder();
55     virtual ~CEnumMergedFolder();
56 
57     DECLARE_NOT_AGGREGATABLE(CEnumMergedFolder)
58     DECLARE_PROTECT_FINAL_CONSTRUCT()
59 
60     BEGIN_COM_MAP(CEnumMergedFolder)
61         COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList)
62     END_COM_MAP()
63 
64     int  DsaDeleteCallback(LocalPidlInfo * info);
65 
66     static int CALLBACK s_DsaDeleteCallback(void *pItem, void *pData);
67 
68     HRESULT SetSources(IShellFolder * userLocal, IShellFolder * allUSers);
69     HRESULT Begin(HWND hwndOwner, SHCONTF flags);
70     HRESULT FindPidlInList(HWND hwndOwner, LPCITEMIDLIST pcidl, LocalPidlInfo * pinfo);
71     HRESULT FindByName(HWND hwndOwner, LPCWSTR strParsingName, LocalPidlInfo * pinfo);
72 
73     virtual HRESULT STDMETHODCALLTYPE Next(
74         ULONG celt,
75         LPITEMIDLIST *rgelt,
76         ULONG *pceltFetched);
77 
78     virtual HRESULT STDMETHODCALLTYPE Skip(ULONG celt);
79     virtual HRESULT STDMETHODCALLTYPE Reset();
80     virtual HRESULT STDMETHODCALLTYPE Clone(IEnumIDList **ppenum);
81 };
82 
83 CEnumMergedFolder::CEnumMergedFolder() :
84     m_UserLocalFolder(NULL),
85     m_AllUSersFolder(NULL),
86     m_HwndOwner(NULL),
87     m_Flags(0),
88     m_hDsa(NULL),
89     m_hDsaIndex(0),
90     m_hDsaCount(0)
91 {
92 }
93 
94 CEnumMergedFolder::~CEnumMergedFolder()
95 {
96     DSA_DestroyCallback(m_hDsa, s_DsaDeleteCallback, this);
97 }
98 
99 int  CEnumMergedFolder::DsaDeleteCallback(LocalPidlInfo * info)
100 {
101     ILFree(info->pidl);
102     if (info->pidl2)
103         ILFree(info->pidl2);
104     CoTaskMemFree((LPVOID)info->parseName);
105     return 0;
106 }
107 
108 int CALLBACK CEnumMergedFolder::s_DsaDeleteCallback(void *pItem, void *pData)
109 {
110     CEnumMergedFolder * mf = (CEnumMergedFolder*) pData;
111     LocalPidlInfo  * item = (LocalPidlInfo*) pItem;
112     return mf->DsaDeleteCallback(item);
113 }
114 
115 HRESULT CEnumMergedFolder::SetSources(IShellFolder * userLocal, IShellFolder * allUSers)
116 {
117     m_UserLocalFolder = userLocal;
118     m_AllUSersFolder = allUSers;
119 
120     TRACE("SetSources %p %p\n", userLocal, allUSers);
121     return S_OK;
122 }
123 
124 HRESULT CEnumMergedFolder::Begin(HWND hwndOwner, SHCONTF flags)
125 {
126     HRESULT hr;
127     LPITEMIDLIST pidl = NULL;
128 
129     if (m_hDsa && m_HwndOwner == hwndOwner && m_Flags == flags)
130     {
131         return Reset();
132     }
133 
134     TRACE("Search conditions changed, recreating list...\n");
135 
136     CComPtr<IEnumIDList> userLocal;
137     CComPtr<IEnumIDList> allUsers;
138 
139     hr = m_UserLocalFolder->EnumObjects(hwndOwner, flags, &userLocal);
140     if (FAILED_UNEXPECTEDLY(hr))
141         return hr;
142     hr = m_AllUSersFolder->EnumObjects(hwndOwner, flags, &allUsers);
143     if (FAILED_UNEXPECTEDLY(hr))
144         return hr;
145 
146     if (!m_hDsa)
147     {
148         m_hDsa = DSA_Create(sizeof(LocalPidlInfo), 10);
149     }
150 
151     DSA_EnumCallback(m_hDsa, s_DsaDeleteCallback, this);
152     DSA_DeleteAllItems(m_hDsa);
153     m_hDsaCount = 0;
154 
155     // The sources are not ordered so load all of the items for the user folder first
156     TRACE("Loading Local entries...\n");
157     for (;;)
158     {
159         hr = userLocal->Next(1, &pidl, NULL);
160         if (FAILED_UNEXPECTEDLY(hr))
161             return hr;
162 
163         if (hr == S_FALSE)
164             break;
165 
166         LPWSTR name;
167         STRRET str = { STRRET_WSTR };
168         hr = m_UserLocalFolder->GetDisplayNameOf(pidl, SHGDN_FORPARSING | SHGDN_INFOLDER, &str);
169         if (FAILED(hr))
170             return hr;
171         StrRetToStrW(&str, pidl, &name);
172 
173         LocalPidlInfo info = {
174             FALSE,
175             m_UserLocalFolder,
176             ILClone(pidl),
177             NULL,
178             name
179         };
180 
181         ILFree(pidl);
182 
183         TRACE("Inserting item %d with name %S\n", m_hDsaCount, name);
184         int idx = DSA_InsertItem(m_hDsa, DSA_APPEND, &info);
185         TRACE("New index: %d\n", idx);
186 
187         m_hDsaCount++;
188     }
189 
190     // Then load the items for the common folder
191     TRACE("Loading Common entries...\n");
192     for (;;)
193     {
194         hr = allUsers->Next(1, &pidl, NULL);
195         if (FAILED_UNEXPECTEDLY(hr))
196             return hr;
197 
198         if (hr == S_FALSE)
199             break;
200 
201         LPWSTR name;
202         STRRET str = { STRRET_WSTR };
203         hr = m_AllUSersFolder->GetDisplayNameOf(pidl, SHGDN_FORPARSING | SHGDN_INFOLDER, &str);
204         if (FAILED(hr))
205             return hr;
206         StrRetToStrW(&str, pidl, &name);
207 
208         LocalPidlInfo info = {
209             FALSE,
210             m_AllUSersFolder,
211             ILClone(pidl),
212             NULL,
213             name
214         };
215 
216         ILFree(pidl);
217 
218         // Try to find an existing entry with the same name, and makr it as shared.
219         // FIXME: This is sub-optimal, a hash table would be a lot more efficient.
220         BOOL bShared = FALSE;
221         for (int i = 0; i < (int)m_hDsaCount; i++)
222         {
223             LocalPidlInfo *pInfo = (LocalPidlInfo *) DSA_GetItemPtr(m_hDsa, i);
224 
225             int order = CompareStringW(GetThreadLocale(), NORM_IGNORECASE,
226                                    pInfo->parseName, lstrlenW(pInfo->parseName),
227                                    info.parseName, lstrlenW(info.parseName));
228 
229             if (order == CSTR_EQUAL)
230             {
231                 TRACE("Item name already exists! Marking '%S' as shared ...\n", name);
232                 bShared = TRUE;
233                 pInfo->shared = TRUE;
234                 pInfo->pidl2 = info.pidl;
235                 CoTaskMemFree(name);
236                 break;
237             }
238         }
239 
240         // If an entry was not found, add a new one for this item
241         if (!bShared)
242         {
243             TRACE("Inserting item %d with name %S\n", m_hDsaCount, name);
244             int idx = DSA_InsertItem(m_hDsa, DSA_APPEND, &info);
245             TRACE("New index: %d\n", idx);
246 
247             m_hDsaCount++;
248         }
249     }
250 
251     m_HwndOwner = hwndOwner;
252     m_Flags = flags;
253 
254     return Reset();
255 }
256 
257 HRESULT CEnumMergedFolder::FindPidlInList(HWND hwndOwner, LPCITEMIDLIST pcidl, LocalPidlInfo * pinfo)
258 {
259     HRESULT hr;
260 
261     if (!m_hDsa)
262     {
263         Begin(hwndOwner, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS);
264     }
265 
266     TRACE("Searching for pidl { cb=%d } in a list of %d items\n", pcidl->mkid.cb, m_hDsaCount);
267 
268     for (int i = 0; i < (int)m_hDsaCount; i++)
269     {
270         LocalPidlInfo * pInfo = (LocalPidlInfo *) DSA_GetItemPtr(m_hDsa, i);
271         if (!pInfo)
272             return E_FAIL;
273 
274         TRACE("Comparing with item at %d with parent %p and pidl { cb=%d }\n", i, pInfo->parent, pInfo->pidl->mkid.cb);
275 
276         hr = pInfo->parent->CompareIDs(0, pInfo->pidl, pcidl);
277         if (FAILED_UNEXPECTEDLY(hr))
278             return hr;
279 
280         if (hr == S_OK)
281         {
282             *pinfo = *pInfo;
283             return S_OK;
284         }
285         else
286         {
287             TRACE("Comparison returned %d\n", (int) (short) (hr & 0xFFFF));
288         }
289     }
290 
291     TRACE("Pidl not found\n");
292     return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
293 }
294 
295 HRESULT CEnumMergedFolder::FindByName(HWND hwndOwner, LPCWSTR strParsingName, LocalPidlInfo * pinfo)
296 {
297     if (!m_hDsa)
298     {
299         Begin(hwndOwner, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS);
300     }
301 
302     TRACE("Searching for '%S' in a list of %d items\n", strParsingName, m_hDsaCount);
303 
304     for (int i = 0; i < (int) m_hDsaCount; i++)
305     {
306         LocalPidlInfo * pInfo = (LocalPidlInfo *) DSA_GetItemPtr(m_hDsa, i);
307         if (!pInfo)
308             return E_FAIL;
309 
310         int order = CompareStringW(GetThreadLocale(), NORM_IGNORECASE,
311                                    pInfo->parseName, lstrlenW(pInfo->parseName),
312                                    strParsingName, lstrlenW(strParsingName));
313         switch (order)
314         {
315         case CSTR_EQUAL:
316             *pinfo = *pInfo;
317             return S_OK;
318         default:
319             continue;
320         }
321     }
322 
323     TRACE("Pidl not found\n");
324     return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
325 }
326 
327 HRESULT STDMETHODCALLTYPE CEnumMergedFolder::Next(
328     ULONG celt,
329     LPITEMIDLIST *rgelt,
330     ULONG *pceltFetched)
331 {
332     if (pceltFetched)
333         *pceltFetched = 0;
334 
335     if (m_hDsaIndex == m_hDsaCount)
336         return S_FALSE;
337 
338     for (int i = 0; i < (int)celt;)
339     {
340         LocalPidlInfo * tinfo = (LocalPidlInfo *) DSA_GetItemPtr(m_hDsa, m_hDsaIndex);
341         if (!tinfo)
342             return E_FAIL;
343 
344         LocalPidlInfo info = *tinfo;
345 
346         TRACE("Returning next item at %d with parent %p and pidl { cb=%d }\n", m_hDsaIndex, info.parent, info.pidl->mkid.cb);
347 
348         // FIXME: ILClone shouldn't be needed here! This should be causing leaks
349         if (rgelt) rgelt[i] = ILClone(info.pidl);
350         i++;
351 
352         m_hDsaIndex++;
353         if (m_hDsaIndex == m_hDsaCount)
354         {
355             if (pceltFetched)
356                 *pceltFetched = i;
357             return (i == (int)celt) ? S_OK : S_FALSE;
358         }
359     }
360 
361     if (pceltFetched) *pceltFetched = celt;
362     return S_OK;
363 }
364 
365 HRESULT STDMETHODCALLTYPE CEnumMergedFolder::Skip(ULONG celt)
366 {
367     return Next(celt, NULL, NULL);
368 }
369 
370 HRESULT STDMETHODCALLTYPE CEnumMergedFolder::Reset()
371 {
372     m_hDsaIndex = 0;
373     return S_OK;
374 }
375 
376 HRESULT STDMETHODCALLTYPE CEnumMergedFolder::Clone(
377     IEnumIDList **ppenum)
378 {
379     UNIMPLEMENTED;
380     return E_NOTIMPL;
381 }
382 
383 //-----------------------------------------------------------------------------
384 // CMergedFolder
385 
386 CMergedFolder::CMergedFolder() :
387     m_UserLocal(NULL),
388     m_AllUsers(NULL),
389     m_EnumSource(NULL),
390     m_UserLocalPidl(NULL),
391     m_AllUsersPidl(NULL),
392     m_shellPidl(NULL)
393 {
394 }
395 
396 CMergedFolder::~CMergedFolder()
397 {
398     if (m_UserLocalPidl) ILFree(m_UserLocalPidl);
399     if (m_AllUsersPidl)  ILFree(m_AllUsersPidl);
400 }
401 
402 // IAugmentedShellFolder2
403 HRESULT STDMETHODCALLTYPE CMergedFolder::AddNameSpace(LPGUID lpGuid, IShellFolder * psf, LPCITEMIDLIST pcidl, ULONG dwUnknown)
404 {
405     if (lpGuid)
406     {
407         TRACE("FIXME: No idea how to handle the GUID\n");
408         return E_NOTIMPL;
409     }
410 
411     TRACE("AddNameSpace %p %p\n", m_UserLocal.p, m_AllUsers.p);
412 
413     // FIXME: Use a DSA to store the list of merged namespaces, together with their related info (psf, pidl, ...)
414     // For now, assume only 2 will ever be used, and ignore all the other data.
415     if (!m_UserLocal)
416     {
417         m_UserLocal = psf;
418         m_UserLocalPidl = ILClone(pcidl);
419         return S_OK;
420     }
421 
422     if (m_AllUsers)
423         return E_FAIL;
424 
425     m_AllUsers = psf;
426     m_AllUsersPidl = ILClone(pcidl);
427 
428     m_EnumSource = new CComObject<CEnumMergedFolder>();
429     return m_EnumSource->SetSources(m_UserLocal, m_AllUsers);
430 }
431 
432 HRESULT STDMETHODCALLTYPE CMergedFolder::GetNameSpaceID(LPCITEMIDLIST pcidl, LPGUID lpGuid)
433 {
434     UNIMPLEMENTED;
435     return E_NOTIMPL;
436 }
437 
438 HRESULT STDMETHODCALLTYPE CMergedFolder::QueryNameSpace(ULONG dwUnknown, LPGUID lpGuid, IShellFolder ** ppsf)
439 {
440     UNIMPLEMENTED;
441     return E_NOTIMPL;
442 }
443 
444 HRESULT STDMETHODCALLTYPE CMergedFolder::EnumNameSpace(ULONG dwUnknown, PULONG lpUnknown)
445 {
446     UNIMPLEMENTED;
447     return E_NOTIMPL;
448 }
449 
450 HRESULT STDMETHODCALLTYPE CMergedFolder::UnWrapIDList(LPCITEMIDLIST pcidl, LONG lUnknown, IShellFolder ** ppsf, LPITEMIDLIST * ppidl1, LPITEMIDLIST *ppidl2, LONG * lpUnknown)
451 {
452     UNIMPLEMENTED;
453     return E_NOTIMPL;
454 }
455 
456 // IShellFolder
457 HRESULT STDMETHODCALLTYPE CMergedFolder::ParseDisplayName(
458     HWND hwndOwner,
459     LPBC pbcReserved,
460     LPOLESTR lpszDisplayName,
461     ULONG *pchEaten,
462     LPITEMIDLIST *ppidl,
463     ULONG *pdwAttributes)
464 {
465     HRESULT hr;
466     LocalPidlInfo info;
467 
468     if (!ppidl)
469         return E_FAIL;
470 
471     if (pchEaten)
472         *pchEaten = 0;
473 
474     if (pdwAttributes)
475         *pdwAttributes = 0;
476 
477     TRACE("ParseDisplayName name=%S\n", lpszDisplayName);
478 
479     hr = m_EnumSource->FindByName(hwndOwner, lpszDisplayName, &info);
480     if (FAILED(hr))
481     {
482         return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
483     }
484 
485     *ppidl = ILClone(info.pidl);
486 
487     if (pchEaten)
488         *pchEaten = lstrlenW(info.parseName);
489 
490     if (pdwAttributes)
491         *pdwAttributes = info.parent->GetAttributesOf(1, (LPCITEMIDLIST*)ppidl, pdwAttributes);
492 
493     return S_OK;
494 }
495 
496 HRESULT STDMETHODCALLTYPE CMergedFolder::EnumObjects(
497     HWND hwndOwner,
498     SHCONTF grfFlags,
499     IEnumIDList **ppenumIDList)
500 {
501     TRACE("EnumObjects\n");
502     HRESULT hr = m_EnumSource->QueryInterface(IID_PPV_ARG(IEnumIDList, ppenumIDList));
503     if (FAILED_UNEXPECTEDLY(hr))
504         return hr;
505     return m_EnumSource->Begin(hwndOwner, grfFlags);
506 }
507 
508 HRESULT STDMETHODCALLTYPE CMergedFolder::BindToObject(
509     LPCITEMIDLIST pidl,
510     LPBC pbcReserved,
511     REFIID riid,
512     void **ppvOut)
513 {
514     LocalPidlInfo info;
515     HRESULT hr;
516 
517     hr = m_EnumSource->FindPidlInList(NULL, pidl, &info);
518     if (FAILED_UNEXPECTEDLY(hr))
519         return hr;
520 
521     TRACE("BindToObject shared = %d\n", info.shared);
522 
523     if (!info.shared)
524         return info.parent->BindToObject(info.pidl, pbcReserved, riid, ppvOut);
525 
526     if (riid != IID_IShellFolder)
527         return E_FAIL;
528 
529     // Construct a child MergedFolder and return it
530     CComPtr<IShellFolder> fld1;
531     CComPtr<IShellFolder> fld2;
532 
533     // In shared folders, the user one takes precedence over the common one, so it will always be on pidl1
534     hr = m_UserLocal->BindToObject(info.pidl, pbcReserved, IID_PPV_ARG(IShellFolder, &fld1));
535     if (FAILED_UNEXPECTEDLY(hr))
536         return hr;
537 
538     hr = m_AllUsers->BindToObject(info.pidl2, pbcReserved, IID_PPV_ARG(IShellFolder, &fld2));
539     if (FAILED_UNEXPECTEDLY(hr))
540         return hr;
541 
542     CComPtr<IAugmentedShellFolder> pasf;
543     hr = CMergedFolder_CreateInstance(IID_PPV_ARG(IAugmentedShellFolder, &pasf));
544     if (FAILED_UNEXPECTEDLY(hr))
545         return hr;
546 
547     hr = pasf->QueryInterface(riid, ppvOut);
548     if (FAILED_UNEXPECTEDLY(hr))
549         return hr;
550 
551     hr = pasf->AddNameSpace(NULL, fld1, info.pidl, 0xFF00);
552     if (FAILED_UNEXPECTEDLY(hr))
553         return hr;
554 
555     hr = pasf->AddNameSpace(NULL, fld2, info.pidl2, 0x0000);
556     if (FAILED_UNEXPECTEDLY(hr))
557         return hr;
558 
559     return hr;
560 }
561 
562 HRESULT STDMETHODCALLTYPE CMergedFolder::BindToStorage(
563     LPCITEMIDLIST pidl,
564     LPBC pbcReserved,
565     REFIID riid,
566     void **ppvObj)
567 {
568     UNIMPLEMENTED;
569     return E_NOTIMPL;
570 }
571 
572 HRESULT STDMETHODCALLTYPE CMergedFolder::CompareIDs(
573     LPARAM lParam,
574     LPCITEMIDLIST pidl1,
575     LPCITEMIDLIST pidl2)
576 {
577     TRACE("CompareIDs\n");
578     return m_UserLocal->CompareIDs(lParam, pidl1, pidl2);
579 }
580 
581 HRESULT STDMETHODCALLTYPE CMergedFolder::CreateViewObject(
582     HWND hwndOwner,
583     REFIID riid,
584     void **ppvOut)
585 {
586     UNIMPLEMENTED;
587     return E_NOTIMPL;
588 }
589 
590 HRESULT STDMETHODCALLTYPE CMergedFolder::GetAttributesOf(
591     UINT cidl,
592     PCUITEMID_CHILD_ARRAY apidl,
593     SFGAOF *rgfInOut)
594 {
595     LocalPidlInfo info;
596     HRESULT hr;
597 
598     TRACE("GetAttributesOf\n");
599 
600     for (int i = 0; i < (int)cidl; i++)
601     {
602         LPCITEMIDLIST pidl = apidl[i];
603 
604         hr = m_EnumSource->FindPidlInList(NULL, pidl, &info);
605         if (FAILED_UNEXPECTEDLY(hr))
606             return hr;
607 
608         pidl = info.pidl;
609 
610         SFGAOF * pinOut1 = rgfInOut ? rgfInOut + i : NULL;
611 
612         hr = info.parent->GetAttributesOf(1, &pidl, pinOut1);
613 
614         if (FAILED_UNEXPECTEDLY(hr))
615             return hr;
616     }
617 
618     return S_OK;
619 }
620 
621 HRESULT STDMETHODCALLTYPE CMergedFolder::GetUIObjectOf(
622     HWND hwndOwner,
623     UINT cidl,
624     PCUITEMID_CHILD_ARRAY apidl,
625     REFIID riid,
626     UINT *prgfInOut,
627     void **ppvOut)
628 {
629     LocalPidlInfo info;
630     HRESULT hr;
631 
632     TRACE("GetUIObjectOf\n");
633 
634     for (int i = 0; i < (int)cidl; i++)
635     {
636         LPCITEMIDLIST pidl = apidl[i];
637 
638         TRACE("Processing GetUIObjectOf item %d of %u...\n", i, cidl);
639 
640         hr = m_EnumSource->FindPidlInList(hwndOwner, pidl, &info);
641         if (FAILED_UNEXPECTEDLY(hr))
642             return hr;
643 
644         pidl = info.pidl;
645 
646         TRACE("FindPidlInList succeeded with parent %p and pidl { db=%d }\n", info.parent, info.pidl->mkid.cb);
647 
648         UINT * pinOut1 = prgfInOut ? prgfInOut+i : NULL;
649         void** ppvOut1 = ppvOut ? ppvOut + i : NULL;
650 
651         hr = info.parent->GetUIObjectOf(hwndOwner, 1, &pidl, riid, pinOut1, ppvOut1);
652 
653         if (FAILED_UNEXPECTEDLY(hr))
654             return hr;
655     }
656 
657     return S_OK;
658 }
659 
660 HRESULT STDMETHODCALLTYPE CMergedFolder::GetDisplayNameOf(
661     LPCITEMIDLIST pidl,
662     SHGDNF uFlags,
663     STRRET *lpName)
664 {
665     LocalPidlInfo info;
666     HRESULT hr;
667 
668     TRACE("GetDisplayNameOf\n");
669 
670     hr = m_EnumSource->FindPidlInList(NULL, pidl, &info);
671     if (FAILED_UNEXPECTEDLY(hr))
672         return hr;
673 
674     hr = info.parent->GetDisplayNameOf(info.pidl, uFlags, lpName);
675 
676     if (FAILED_UNEXPECTEDLY(hr))
677         return hr;
678     return S_OK;
679 }
680 
681 HRESULT STDMETHODCALLTYPE CMergedFolder::SetNameOf(
682     HWND hwnd,
683     LPCITEMIDLIST pidl,
684     LPCOLESTR lpszName,
685     SHGDNF uFlags,
686     LPITEMIDLIST *ppidlOut)
687 {
688     UNIMPLEMENTED;
689     return E_NOTIMPL;
690 }
691 
692 // IPersist
693 HRESULT STDMETHODCALLTYPE CMergedFolder::GetClassID(CLSID *lpClassId)
694 {
695     UNIMPLEMENTED;
696     return E_NOTIMPL;
697 }
698 
699 // IPersistFolder
700 HRESULT STDMETHODCALLTYPE CMergedFolder::Initialize(PCIDLIST_ABSOLUTE pidl)
701 {
702     m_shellPidl = ILClone(pidl);
703     return S_OK;
704 }
705 
706 // IPersistFolder2
707 HRESULT STDMETHODCALLTYPE CMergedFolder::GetCurFolder(PIDLIST_ABSOLUTE * pidl)
708 {
709     if (pidl)
710         *pidl = m_shellPidl;
711     return S_OK;
712 }
713 
714 // IShellFolder2
715 HRESULT STDMETHODCALLTYPE CMergedFolder::GetDefaultSearchGUID(
716     GUID *lpguid)
717 {
718     UNIMPLEMENTED;
719     return E_NOTIMPL;
720 }
721 
722 HRESULT STDMETHODCALLTYPE CMergedFolder::EnumSearches(
723     IEnumExtraSearch **ppenum)
724 {
725     UNIMPLEMENTED;
726     return E_NOTIMPL;
727 }
728 
729 HRESULT STDMETHODCALLTYPE CMergedFolder::GetDefaultColumn(
730     DWORD dwReserved,
731     ULONG *pSort,
732     ULONG *pDisplay)
733 {
734     UNIMPLEMENTED;
735     return E_NOTIMPL;
736 }
737 
738 HRESULT STDMETHODCALLTYPE CMergedFolder::GetDefaultColumnState(
739     UINT iColumn,
740     SHCOLSTATEF *pcsFlags)
741 {
742     UNIMPLEMENTED;
743     return E_NOTIMPL;
744 }
745 
746 HRESULT STDMETHODCALLTYPE CMergedFolder::GetDetailsEx(
747     LPCITEMIDLIST pidl,
748     const SHCOLUMNID *pscid,
749     VARIANT *pv)
750 {
751     LocalPidlInfo info;
752     HRESULT hr;
753 
754     TRACE("GetDetailsEx\n");
755 
756     hr = m_EnumSource->FindPidlInList(NULL, pidl, &info);
757     if (FAILED_UNEXPECTEDLY(hr))
758         return hr;
759 
760     CComPtr<IShellFolder2> parent2;
761     hr = info.parent->QueryInterface(IID_PPV_ARG(IShellFolder2, &parent2));
762     if (FAILED_UNEXPECTEDLY(hr))
763         return hr;
764 
765     hr = parent2->GetDetailsEx(info.pidl, pscid, pv);
766     if (FAILED_UNEXPECTEDLY(hr))
767         return hr;
768     return S_OK;
769 }
770 
771 HRESULT STDMETHODCALLTYPE CMergedFolder::GetDetailsOf(
772     LPCITEMIDLIST pidl,
773     UINT iColumn,
774     SHELLDETAILS *psd)
775 {
776     LocalPidlInfo info;
777     HRESULT hr;
778 
779     TRACE("GetDetailsOf\n");
780 
781     hr = m_EnumSource->FindPidlInList(NULL, pidl, &info);
782     if (FAILED_UNEXPECTEDLY(hr))
783         return hr;
784 
785     CComPtr<IShellFolder2> parent2;
786     hr = info.parent->QueryInterface(IID_PPV_ARG(IShellFolder2, &parent2));
787     if (FAILED_UNEXPECTEDLY(hr))
788         return hr;
789 
790     hr = parent2->GetDetailsOf(info.pidl, iColumn, psd);
791 
792     if (FAILED_UNEXPECTEDLY(hr))
793         return hr;
794     return S_OK;
795 }
796 
797 HRESULT STDMETHODCALLTYPE CMergedFolder::MapColumnToSCID(
798     UINT iColumn,
799     SHCOLUMNID *pscid)
800 {
801     UNIMPLEMENTED;
802     return E_NOTIMPL;
803 }
804 
805 // IAugmentedShellFolder3
806 HRESULT STDMETHODCALLTYPE CMergedFolder::QueryNameSpace2(ULONG, QUERYNAMESPACEINFO *)
807 {
808     UNIMPLEMENTED;
809     return E_NOTIMPL;
810 }
811 
812 extern "C"
813 HRESULT WINAPI RSHELL_CMergedFolder_CreateInstance(REFIID riid, LPVOID *ppv)
814 {
815     return ShellObjectCreator<CMergedFolder>(riid, ppv);
816 }
817