1 /*
2  *    Virtual Desktop Folder
3  *
4  *    Copyright 1997                Marcus Meissner
5  *    Copyright 1998, 1999, 2002    Juergen Schmied
6  *    Copyright 2009                Andrew Hill
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  */
22 
23 #include <precomp.h>
24 #include "CFSFolder.h" // Only for CFSFolder::*FSColumn* helpers!
25 
26 WINE_DEFAULT_DEBUG_CHANNEL(shell);
27 
28 STDMETHODIMP
29 CDesktopFolder::ShellUrlParseDisplayName(
30     HWND hwndOwner,
31     LPBC pbc,
32     LPOLESTR lpszDisplayName,
33     DWORD *pchEaten,
34     PIDLIST_RELATIVE *ppidl,
35     DWORD *pdwAttributes)
36 {
37     LPWSTR pch;
38     INT cch, csidl;
39     HRESULT hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
40     PARSEDURLW ParsedURL = { sizeof(ParsedURL) };
41 
42     ::ParseURLW(lpszDisplayName, &ParsedURL);
43 
44     DWORD attrs = (pdwAttributes ? *pdwAttributes : 0) | SFGAO_STREAM;
45     if (ParsedURL.pszSuffix[0] == L':' && ParsedURL.pszSuffix[1] == L':') // It begins from "::"
46     {
47         CComPtr<IShellFolder> psfDesktop;
48         hr = SHGetDesktopFolder(&psfDesktop);
49         if (SUCCEEDED(hr))
50         {
51             CComPtr<IBindCtx> pBindCtx;
52             hr = ::CreateBindCtx(0, &pBindCtx);
53             if (SUCCEEDED(hr))
54             {
55                 BIND_OPTS BindOps = { sizeof(BindOps) };
56                 BindOps.grfMode = STGM_CREATE;
57                 pBindCtx->SetBindOptions(&BindOps);
58                 hr = psfDesktop->ParseDisplayName(hwndOwner, pBindCtx,
59                                                   (LPWSTR)ParsedURL.pszSuffix,
60                                                   pchEaten, ppidl, &attrs);
61             }
62         }
63     }
64     else
65     {
66         csidl = Shell_ParseSpecialFolder(ParsedURL.pszSuffix, &pch, &cch);
67         if (csidl == -1)
68         {
69             ERR("\n");
70             return hr;
71         }
72 
73         CComHeapPtr<ITEMIDLIST> pidlLocation;
74         hr = SHGetFolderLocation(hwndOwner, (csidl | CSIDL_FLAG_CREATE), NULL, 0, &pidlLocation);
75         if (FAILED_UNEXPECTEDLY(hr))
76             return hr;
77 
78         if (pch && *pch)
79         {
80             CComPtr<IShellFolder> psfFolder;
81             hr = SHBindToObject(NULL, pidlLocation, IID_PPV_ARG(IShellFolder, &psfFolder));
82             if (SUCCEEDED(hr))
83             {
84                 CComHeapPtr<ITEMIDLIST> pidlNew;
85                 hr = psfFolder->ParseDisplayName(hwndOwner, pbc, pch, pchEaten, &pidlNew, &attrs);
86                 if (SUCCEEDED(hr))
87                 {
88                     hr = SHILCombine(pidlLocation, pidlNew, ppidl);
89                     if (pchEaten)
90                         *pchEaten += cch;
91                 }
92             }
93         }
94         else
95         {
96             if (attrs)
97                 hr = SHGetNameAndFlagsW(pidlLocation, 0, NULL, 0, &attrs);
98 
99             if (SUCCEEDED(hr))
100             {
101                 if (pchEaten)
102                     *pchEaten = cch;
103                 *ppidl = pidlLocation.Detach();
104             }
105         }
106     }
107 
108     // FIXME: SHWindowsPolicy
109     if (SUCCEEDED(hr) && (attrs & SFGAO_STREAM) &&
110         !BindCtx_ContainsObject(pbc, STR_PARSE_SHELL_PROTOCOL_TO_FILE_OBJECTS))
111     {
112         ILFree(*ppidl);
113         *ppidl = NULL;
114         hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
115     }
116 
117     if (pdwAttributes)
118         *pdwAttributes = attrs;
119 
120     // FIXME: SHWindowsPolicy
121     if (FAILED(hr) && !Shell_FailForceReturn(hr))
122         hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
123 
124     return hr;
125 }
126 
127 STDMETHODIMP
128 CDesktopFolder::HttpUrlParseDisplayName(
129     HWND hwndOwner,
130     LPBC pbc,
131     LPOLESTR lpszDisplayName,
132     DWORD *pchEaten,
133     PIDLIST_RELATIVE *ppidl,
134     DWORD *pdwAttributes)
135 {
136     FIXME("\n");
137     return E_NOTIMPL; // FIXME
138 }
139 
140 /*
141 CDesktopFolder should create two file system folders internally, one representing the
142 user's desktop folder, and the other representing the common desktop folder. It should
143 also create a CRegFolder to represent the virtual items that exist only in the registry.
144 The CRegFolder is aggregated by the CDesktopFolder, and queries for the CLSID_IShellFolder,
145 CLSID_IShellFolder2, or CLSID_IShellIconOverlay interfaces prefer the CRegFolder
146 implementation.
147 The CDesktopFolderEnum class should create two enumerators, one for each of the file
148 system folders, and enumerate the contents of each folder. Since the CRegFolder
149 implementation of IShellFolder::EnumObjects enumerates the virtual items, the
150 CDesktopFolderEnum is only responsible for returning the physical items.
151 CDesktopFolderEnum is incorrect where it filters My Computer from the enumeration
152 if the new start menu is used. The CDesktopViewCallback is responsible for filtering
153 it from the view by handling the IncludeObject query to return S_FALSE. The enumerator
154 always shows My Computer.
155 */
156 
157 /* Undocumented functions from shdocvw */
158 extern "C" HRESULT WINAPI IEParseDisplayNameWithBCW(DWORD codepage, LPCWSTR lpszDisplayName, LPBC pbc, LPITEMIDLIST *ppidl);
159 
160 static const WCHAR ClassicStartMenuW[] = L"SOFTWARE\\Microsoft\\Windows\\"
161     L"CurrentVersion\\Explorer\\HideDesktopIcons\\ClassicStartMenu";
162 
163 static INT
164 IsNamespaceExtensionHidden(const WCHAR *iid)
165 {
166     DWORD Result, dwResult;
167     dwResult = sizeof(DWORD);
168 
169     if (RegGetValueW(HKEY_CURRENT_USER, /* FIXME use NewStartPanel when activated */
170                      ClassicStartMenuW,
171                      iid,
172                      RRF_RT_DWORD,
173                      NULL,
174                      &Result,
175                      &dwResult) != ERROR_SUCCESS)
176     {
177         return -1;
178     }
179 
180     return Result;
181 }
182 
183 static INT IsNamespaceExtensionHidden(LPCITEMIDLIST pidl)
184 {
185     GUID const *clsid = _ILGetGUIDPointer (pidl);
186     if (!clsid)
187         return -1;
188 
189     WCHAR pwszGuid[CHARS_IN_GUID];
190     SHELL32_GUIDToStringW(*clsid, pwszGuid);
191     return IsNamespaceExtensionHidden(pwszGuid);
192 }
193 
194 class CDesktopFolderEnum :
195     public CEnumIDListBase
196 {
197     private:
198 //    CComPtr                                fDesktopEnumerator;
199 //    CComPtr                                fCommonDesktopEnumerator;
200     public:
201 
202         void AddItemsFromClassicStartMenuKey(HKEY hKeyRoot)
203         {
204             DWORD dwResult;
205             HKEY hkey;
206             DWORD j = 0, dwVal, Val, dwType, dwIID;
207             LONG r;
208             WCHAR iid[50];
209             LPITEMIDLIST pidl;
210 
211             dwResult = RegOpenKeyExW(hKeyRoot, ClassicStartMenuW, 0, KEY_READ, &hkey);
212             if (dwResult != ERROR_SUCCESS)
213                 return;
214 
215             while(1)
216             {
217                 dwVal = sizeof(Val);
218                 dwIID = sizeof(iid) / sizeof(WCHAR);
219 
220                 r = RegEnumValueW(hkey, j++, iid, &dwIID, NULL, &dwType, (LPBYTE)&Val, &dwVal);
221                 if (r != ERROR_SUCCESS)
222                     break;
223 
224                 if (Val == 0 && dwType == REG_DWORD)
225                 {
226                     pidl = _ILCreateGuidFromStrW(iid);
227                     if (pidl != NULL)
228                     {
229                         if (!HasItemWithCLSID(pidl))
230                             AddToEnumList(pidl);
231                         else
232                             SHFree(pidl);
233                     }
234                 }
235             }
236             RegCloseKey(hkey);
237         }
238 
239         HRESULT WINAPI Initialize(DWORD dwFlags,IEnumIDList * pRegEnumerator, IEnumIDList *pDesktopEnumerator, IEnumIDList *pCommonDesktopEnumerator)
240         {
241             BOOL ret = TRUE;
242             LPITEMIDLIST pidl;
243 
244             static const WCHAR MyDocumentsClassString[] = L"{450D8FBA-AD25-11D0-98A8-0800361B1103}";
245             static const WCHAR InternetClassString[] = L"{871C5380-42A0-1069-A2EA-08002B30309D}";
246 
247             TRACE("(%p)->(flags=0x%08x)\n", this, dwFlags);
248 
249             /* enumerate the root folders */
250             if (dwFlags & SHCONTF_FOLDERS)
251             {
252                 AddToEnumList(_ILCreateMyComputer());
253                 if (IsNamespaceExtensionHidden(MyDocumentsClassString) < 1)
254                     AddToEnumList(_ILCreateMyDocuments());
255                 if (IsNamespaceExtensionHidden(InternetClassString) < 1)
256                     AddToEnumList(_ILCreateIExplore());
257 
258                 DWORD dwFetched;
259                 while((S_OK == pRegEnumerator->Next(1, &pidl, &dwFetched)) && dwFetched)
260                 {
261                     if (IsNamespaceExtensionHidden(pidl) < 1)
262                     {
263                         if (!HasItemWithCLSID(pidl))
264                             AddToEnumList(pidl);
265                         else
266                             SHFree(pidl);
267                     }
268                 }
269                 AddItemsFromClassicStartMenuKey(HKEY_LOCAL_MACHINE);
270                 AddItemsFromClassicStartMenuKey(HKEY_CURRENT_USER);
271             }
272 
273             /* Enumerate the items in the two fs folders */
274             AppendItemsFromEnumerator(pDesktopEnumerator);
275             AppendItemsFromEnumerator(pCommonDesktopEnumerator);
276 
277             return ret ? S_OK : E_FAIL;
278         }
279 
280 
281         BEGIN_COM_MAP(CDesktopFolderEnum)
282         COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList)
283         END_COM_MAP()
284 };
285 
286 int SHELL_ConfirmMsgBox(HWND hWnd, LPWSTR lpszText, LPWSTR lpszCaption, HICON hIcon, BOOL bYesToAll);
287 
288 static const DWORD dwDesktopAttributes =
289     SFGAO_HASSUBFOLDER | SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR |
290     SFGAO_STORAGEANCESTOR | SFGAO_HASPROPSHEET | SFGAO_STORAGE;
291 static const DWORD dwMyComputerAttributes =
292     SFGAO_CANRENAME | SFGAO_CANDELETE | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET |
293     SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_CANLINK;
294 static DWORD dwMyNetPlacesAttributes =
295     SFGAO_CANRENAME | SFGAO_CANDELETE | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET |
296     SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_CANLINK;
297 
298 CDesktopFolder::CDesktopFolder() :
299     sPathTarget(NULL),
300     pidlRoot(NULL)
301 {
302 }
303 
304 CDesktopFolder::~CDesktopFolder()
305 {
306 }
307 
308 HRESULT WINAPI CDesktopFolder::FinalConstruct()
309 {
310     WCHAR szMyPath[MAX_PATH];
311     HRESULT hr;
312 
313     /* Create the root pidl */
314     pidlRoot = _ILCreateDesktop();
315     if (!pidlRoot)
316         return E_OUTOFMEMORY;
317 
318     /* Create the inner fs folder */
319     hr = SHELL32_CoCreateInitSF(pidlRoot,
320                                 &CLSID_ShellFSFolder,
321                                 CSIDL_DESKTOPDIRECTORY,
322                                 IID_PPV_ARG(IShellFolder2, &m_DesktopFSFolder));
323     if (FAILED_UNEXPECTEDLY(hr))
324         return hr;
325 
326     /* Create the inner shared fs folder. Dont fail on failure. */
327     hr = SHELL32_CoCreateInitSF(pidlRoot,
328                                 &CLSID_ShellFSFolder,
329                                 CSIDL_COMMON_DESKTOPDIRECTORY,
330                                 IID_PPV_ARG(IShellFolder2, &m_SharedDesktopFSFolder));
331     if (FAILED_UNEXPECTEDLY(hr))
332         return hr;
333 
334     /* Create the inner reg folder */
335     hr = CRegFolder_CreateInstance(&CLSID_ShellDesktop,
336                                    pidlRoot,
337                                    L"",
338                                    L"Desktop",
339                                    IID_PPV_ARG(IShellFolder2, &m_regFolder));
340     if (FAILED_UNEXPECTEDLY(hr))
341         return hr;
342 
343     /* Cache the path to the user desktop directory */
344     if (!SHGetSpecialFolderPathW( 0, szMyPath, CSIDL_DESKTOPDIRECTORY, TRUE ))
345         return E_UNEXPECTED;
346 
347     sPathTarget = (LPWSTR)SHAlloc((wcslen(szMyPath) + 1) * sizeof(WCHAR));
348     if (!sPathTarget)
349         return E_OUTOFMEMORY;
350 
351     wcscpy(sPathTarget, szMyPath);
352     return S_OK;
353 }
354 
355 HRESULT CDesktopFolder::_GetSFFromPidl(LPCITEMIDLIST pidl, IShellFolder2** psf)
356 {
357     WCHAR szFileName[MAX_PATH];
358 
359     if (_ILIsSpecialFolder(pidl))
360         return m_regFolder->QueryInterface(IID_PPV_ARG(IShellFolder2, psf));
361 
362     lstrcpynW(szFileName, sPathTarget, MAX_PATH - 1);
363     PathAddBackslashW(szFileName);
364     int cLen = wcslen(szFileName);
365 
366     if (!_ILSimpleGetTextW(pidl, szFileName + cLen, MAX_PATH - cLen))
367         return E_FAIL;
368 
369     if (GetFileAttributes(szFileName) == INVALID_FILE_ATTRIBUTES)
370         return m_SharedDesktopFSFolder->QueryInterface(IID_PPV_ARG(IShellFolder2, psf));
371     else
372         return m_DesktopFSFolder->QueryInterface(IID_PPV_ARG(IShellFolder2, psf));
373 }
374 
375 HRESULT CDesktopFolder::_ParseDisplayNameByParent(
376     HWND hwndOwner,
377     LPBC pbc,
378     LPOLESTR lpszDisplayName,
379     DWORD *pchEaten,
380     PIDLIST_RELATIVE *ppidl,
381     DWORD *pdwAttributes)
382 {
383     if (pchEaten)
384         *pchEaten = 0;
385 
386     CComHeapPtr<ITEMIDLIST> pidlParent;
387     BOOL bPath = FALSE;
388     WCHAR wch = *lpszDisplayName;
389     if (((L'A' <= wch && wch <= L'Z') || (L'a' <= wch && wch <= L'z')) &&
390         (lpszDisplayName[1] == L':'))
391     {
392         // "C:..."
393         bPath = TRUE;
394         pidlParent.Attach(_ILCreateMyComputer());
395     }
396     else if (PathIsUNCW(lpszDisplayName)) // "\\\\..."
397     {
398         bPath = TRUE;
399         pidlParent.Attach(_ILCreateNetwork());
400     }
401 
402     if (bPath)
403     {
404         if (!pidlParent)
405             return E_OUTOFMEMORY;
406 
407         CComPtr<IShellFolder> pParentFolder;
408         SHBindToObject(NULL, pidlParent, IID_PPV_ARG(IShellFolder, &pParentFolder));
409 
410         CComHeapPtr<ITEMIDLIST> pidlChild;
411         HRESULT hr = pParentFolder->ParseDisplayName(hwndOwner, pbc, lpszDisplayName,
412                                                      pchEaten, &pidlChild, pdwAttributes);
413         if (FAILED(hr))
414             return hr;
415 
416         *ppidl = ILCombine(pidlParent, pidlChild);
417         return (*ppidl ? S_OK : E_OUTOFMEMORY);
418     }
419 
420     if (!UrlIsW(lpszDisplayName, URLIS_URL) || SHSkipJunctionBinding(pbc, NULL))
421         return E_INVALIDARG;
422 
423     // Now lpszDisplayName is a URL
424     PARSEDURLW ParsedURL = { sizeof(ParsedURL) };
425     ::ParseURLW(lpszDisplayName, &ParsedURL);
426 
427     switch (ParsedURL.nScheme)
428     {
429         case URL_SCHEME_FILE: // "file:..."
430         {
431             // Convert "file://..." to a normal path
432             WCHAR szPath[MAX_PATH];
433             DWORD cchPath = _countof(szPath);
434             HRESULT hr = PathCreateFromUrlW(lpszDisplayName, szPath, &cchPath, 0);
435             if (FAILED_UNEXPECTEDLY(hr))
436                 return hr;
437 
438             CComPtr<IShellFolder> psfDesktop;
439             hr = SHGetDesktopFolder(&psfDesktop);
440             if (FAILED_UNEXPECTEDLY(hr))
441                 return hr;
442 
443             // Parse by desktop folder
444             return psfDesktop->ParseDisplayName(hwndOwner, pbc, szPath, pchEaten, ppidl,
445                                                 pdwAttributes);
446         }
447         case URL_SCHEME_HTTP:  // "http:..."
448         case URL_SCHEME_HTTPS: // "https:..."
449         {
450             if (!BindCtx_ContainsObject(pbc, STR_PARSE_PREFER_FOLDER_BROWSING))
451                 return E_INVALIDARG;
452 
453             return HttpUrlParseDisplayName(hwndOwner,
454                                            pbc,
455                                            lpszDisplayName,
456                                            pchEaten,
457                                            ppidl,
458                                            pdwAttributes);
459         }
460         case URL_SCHEME_SHELL: // "shell:..."
461         {
462             return ShellUrlParseDisplayName(hwndOwner,
463                                             pbc,
464                                             lpszDisplayName,
465                                             pchEaten,
466                                             ppidl,
467                                             pdwAttributes);
468         }
469         case URL_SCHEME_MSSHELLROOTED:
470         case URL_SCHEME_MSSHELLIDLIST:
471         {
472             WARN("We don't support 'ms-shell-rooted:' and 'ms-shell-idlist:' schemes\n");
473             break;
474         }
475         default:
476         {
477             TRACE("Scheme: %u\n", ParsedURL.nScheme);
478             break;
479         }
480     }
481 
482     return E_INVALIDARG;
483 }
484 
485 /**************************************************************************
486  *    CDesktopFolder::ParseDisplayName
487  *
488  * NOTES
489  *    "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}" and "" binds
490  *    to MyComputer
491  */
492 HRESULT WINAPI CDesktopFolder::ParseDisplayName(
493     HWND hwndOwner,
494     LPBC pbc,
495     LPOLESTR lpszDisplayName,
496     DWORD *pchEaten,
497     PIDLIST_RELATIVE *ppidl,
498     DWORD *pdwAttributes)
499 {
500     TRACE ("(%p)->(HWND=%p,%p,%p=%s,%p,pidl=%p,%p)\n",
501            this, hwndOwner, pbc, lpszDisplayName, debugstr_w(lpszDisplayName),
502            pchEaten, ppidl, pdwAttributes);
503 
504     if (!ppidl)
505         return E_INVALIDARG;
506 
507     *ppidl = NULL;
508 
509     if (!lpszDisplayName)
510         return E_INVALIDARG;
511 
512     if (!*lpszDisplayName)
513     {
514         *ppidl = _ILCreateMyComputer();
515         return (*ppidl ? S_OK : E_OUTOFMEMORY);
516     }
517 
518     if (lpszDisplayName[0] == ':' && lpszDisplayName[1] == ':')
519     {
520         return m_regFolder->ParseDisplayName(hwndOwner, pbc, lpszDisplayName, pchEaten, ppidl,
521                                              pdwAttributes);
522     }
523 
524     HRESULT hr = _ParseDisplayNameByParent(hwndOwner, pbc, lpszDisplayName, pchEaten, ppidl,
525                                            pdwAttributes);
526     if (SUCCEEDED(hr))
527     {
528         if (BindCtx_ContainsObject(pbc, STR_PARSE_TRANSLATE_ALIASES))
529         {
530             CComHeapPtr<ITEMIDLIST> pidlAlias;
531             if (SUCCEEDED(Shell_TranslateIDListAlias(*ppidl, NULL, &pidlAlias, 0xFFFF)))
532             {
533                 ILFree(*ppidl);
534                 *ppidl = pidlAlias.Detach();
535             }
536         }
537 
538         TRACE ("(%p)->(-- ret=0x%08x)\n", this, hr);
539         return hr;
540     }
541 
542     if (Shell_FailForceReturn(hr))
543         return hr;
544 
545     if (BindCtx_ContainsObject(pbc, STR_DONT_PARSE_RELATIVE))
546         return E_INVALIDARG;
547 
548     if (SHIsFileSysBindCtx(pbc, NULL) == S_OK)
549         return hr;
550 
551     BIND_OPTS BindOps = { sizeof(BindOps) };
552     BOOL bCreate = FALSE;
553     if (pbc && SUCCEEDED(pbc->GetBindOptions(&BindOps)) && (BindOps.grfMode & STGM_CREATE))
554     {
555         BindOps.grfMode &= ~STGM_CREATE;
556         bCreate = TRUE;
557         pbc->SetBindOptions(&BindOps);
558     }
559 
560     if (m_DesktopFSFolder)
561     {
562         hr = m_DesktopFSFolder->ParseDisplayName(hwndOwner,
563                                                  pbc,
564                                                  lpszDisplayName,
565                                                  pchEaten,
566                                                  ppidl,
567                                                  pdwAttributes);
568     }
569 
570     if (FAILED(hr) && m_SharedDesktopFSFolder)
571     {
572         hr = m_SharedDesktopFSFolder->ParseDisplayName(hwndOwner,
573                                                        pbc,
574                                                        lpszDisplayName,
575                                                        pchEaten,
576                                                        ppidl,
577                                                        pdwAttributes);
578     }
579 
580     if (FAILED(hr) && bCreate && m_DesktopFSFolder)
581     {
582         BindOps.grfMode |= STGM_CREATE;
583         pbc->SetBindOptions(&BindOps);
584         hr = m_DesktopFSFolder->ParseDisplayName(hwndOwner,
585                                                  pbc,
586                                                  lpszDisplayName,
587                                                  pchEaten,
588                                                  ppidl,
589                                                  pdwAttributes);
590     }
591 
592     TRACE ("(%p)->(-- ret=0x%08x)\n", this, hr);
593 
594     return hr;
595 }
596 
597 /**************************************************************************
598  *        CDesktopFolder::EnumObjects
599  */
600 HRESULT WINAPI CDesktopFolder::EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList)
601 {
602     CComPtr<IEnumIDList> pRegEnumerator;
603     CComPtr<IEnumIDList> pDesktopEnumerator;
604     CComPtr<IEnumIDList> pCommonDesktopEnumerator;
605     HRESULT hr;
606 
607     hr = m_regFolder->EnumObjects(hwndOwner, dwFlags, &pRegEnumerator);
608     if (FAILED(hr))
609         ERR("EnumObjects for reg folder failed\n");
610 
611     hr = m_DesktopFSFolder->EnumObjects(hwndOwner, dwFlags, &pDesktopEnumerator);
612     if (FAILED(hr))
613         ERR("EnumObjects for desktop fs folder failed\n");
614 
615     hr = m_SharedDesktopFSFolder->EnumObjects(hwndOwner, dwFlags, &pCommonDesktopEnumerator);
616     if (FAILED(hr))
617         ERR("EnumObjects for shared desktop fs folder failed\n");
618 
619     return ShellObjectCreatorInit<CDesktopFolderEnum>(dwFlags,pRegEnumerator, pDesktopEnumerator, pCommonDesktopEnumerator, IID_PPV_ARG(IEnumIDList, ppEnumIDList));
620 }
621 
622 /**************************************************************************
623  *        CDesktopFolder::BindToObject
624  */
625 HRESULT WINAPI CDesktopFolder::BindToObject(
626     PCUIDLIST_RELATIVE pidl,
627     LPBC pbcReserved,
628     REFIID riid,
629     LPVOID *ppvOut)
630 {
631     if (!pidl)
632         return E_INVALIDARG;
633 
634     CComPtr<IShellFolder2> psf;
635     HRESULT hr = _GetSFFromPidl(pidl, &psf);
636     if (FAILED_UNEXPECTEDLY(hr))
637         return hr;
638 
639     return psf->BindToObject(pidl, pbcReserved, riid, ppvOut);
640 }
641 
642 /**************************************************************************
643  *    CDesktopFolder::BindToStorage
644  */
645 HRESULT WINAPI CDesktopFolder::BindToStorage(
646     PCUIDLIST_RELATIVE pidl,
647     LPBC pbcReserved,
648     REFIID riid,
649     LPVOID *ppvOut)
650 {
651     FIXME ("(%p)->(pidl=%p,%p,%s,%p) stub\n",
652            this, pidl, pbcReserved, shdebugstr_guid (&riid), ppvOut);
653 
654     *ppvOut = NULL;
655     return E_NOTIMPL;
656 }
657 
658 /**************************************************************************
659  *     CDesktopFolder::CompareIDs
660  */
661 HRESULT WINAPI CDesktopFolder::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2)
662 {
663     bool bIsDesktopFolder1, bIsDesktopFolder2;
664 
665     if (!pidl1 || !pidl2)
666     {
667         ERR("Got null pidl pointer (%Ix %p %p)!\n", lParam, pidl1, pidl2);
668         return E_INVALIDARG;
669     }
670 
671     bIsDesktopFolder1 = _ILIsDesktop(pidl1);
672     bIsDesktopFolder2 = _ILIsDesktop(pidl2);
673     if (bIsDesktopFolder1 || bIsDesktopFolder2)
674         return MAKE_COMPARE_HRESULT(bIsDesktopFolder1 - bIsDesktopFolder2);
675 
676     if (_ILIsSpecialFolder(pidl1) || _ILIsSpecialFolder(pidl2))
677         return m_regFolder->CompareIDs(lParam, pidl1, pidl2);
678 
679     return m_DesktopFSFolder->CompareIDs(lParam, pidl1, pidl2);
680 }
681 
682 /**************************************************************************
683  *    CDesktopFolder::CreateViewObject
684  */
685 HRESULT WINAPI CDesktopFolder::CreateViewObject(
686     HWND hwndOwner,
687     REFIID riid,
688     LPVOID *ppvOut)
689 {
690     HRESULT hr = E_INVALIDARG;
691 
692     TRACE ("(%p)->(hwnd=%p,%s,%p)\n",
693            this, hwndOwner, shdebugstr_guid (&riid), ppvOut);
694 
695     if (!ppvOut)
696         return hr;
697 
698     *ppvOut = NULL;
699 
700     if (IsEqualIID (riid, IID_IDropTarget))
701     {
702         hr = m_DesktopFSFolder->CreateViewObject(hwndOwner, riid, ppvOut);
703     }
704     else if (IsEqualIID (riid, IID_IContextMenu))
705     {
706             HKEY hKeys[16];
707             UINT cKeys = 0;
708             AddClassKeyToArray(L"Directory\\Background", hKeys, &cKeys);
709 
710             DEFCONTEXTMENU dcm;
711             dcm.hwnd = hwndOwner;
712             dcm.pcmcb = this;
713             dcm.pidlFolder = pidlRoot;
714             dcm.psf = this;
715             dcm.cidl = 0;
716             dcm.apidl = NULL;
717             dcm.cKeys = cKeys;
718             dcm.aKeys = hKeys;
719             dcm.punkAssociationInfo = NULL;
720             hr = SHCreateDefaultContextMenu (&dcm, riid, ppvOut);
721     }
722     else if (IsEqualIID (riid, IID_IShellView))
723     {
724         SFV_CREATE sfvparams = {sizeof(SFV_CREATE), this};
725         hr = SHCreateShellFolderView(&sfvparams, (IShellView**)ppvOut);
726     }
727     TRACE ("-- (%p)->(interface=%p)\n", this, ppvOut);
728     return hr;
729 }
730 
731 /**************************************************************************
732  *  CDesktopFolder::GetAttributesOf
733  */
734 HRESULT WINAPI CDesktopFolder::GetAttributesOf(
735     UINT cidl,
736     PCUITEMID_CHILD_ARRAY apidl,
737     DWORD *rgfInOut)
738 {
739     HRESULT hr = S_OK;
740 
741     TRACE("(%p)->(cidl=%d apidl=%p mask=%p (0x%08x))\n",
742           this, cidl, apidl, rgfInOut, rgfInOut ? *rgfInOut : 0);
743 
744     if (cidl && !apidl)
745         return E_INVALIDARG;
746 
747     if (*rgfInOut == 0)
748         *rgfInOut = ~0;
749 
750     if(cidl == 0)
751         *rgfInOut &= dwDesktopAttributes;
752     else
753     {
754         /* TODO: always add SFGAO_CANLINK */
755         for (UINT i = 0; i < cidl; ++i)
756         {
757             pdump(*apidl);
758             if (_ILIsDesktop(*apidl))
759                 *rgfInOut &= dwDesktopAttributes;
760             else if (_ILIsMyComputer(apidl[i]))
761                 *rgfInOut &= dwMyComputerAttributes;
762             else if (_ILIsNetHood(apidl[i]))
763                 *rgfInOut &= dwMyNetPlacesAttributes;
764             else if (_ILIsFolder(apidl[i]) || _ILIsValue(apidl[i]) || _ILIsSpecialFolder(apidl[i]))
765             {
766                 CComPtr<IShellFolder2> psf;
767                 HRESULT hr = _GetSFFromPidl(apidl[i], &psf);
768                 if (FAILED_UNEXPECTEDLY(hr))
769                     continue;
770 
771                 psf->GetAttributesOf(1, &apidl[i], rgfInOut);
772             }
773             else
774                 ERR("Got an unknown pidl type!!!\n");
775         }
776     }
777     /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */
778     *rgfInOut &= ~SFGAO_VALIDATE;
779 
780     TRACE("-- result=0x%08x\n", *rgfInOut);
781 
782     return hr;
783 }
784 
785 /**************************************************************************
786  *    CDesktopFolder::GetUIObjectOf
787  *
788  * PARAMETERS
789  *  HWND           hwndOwner, //[in ] Parent window for any output
790  *  UINT           cidl,      //[in ] array size
791  *  LPCITEMIDLIST* apidl,     //[in ] simple pidl array
792  *  REFIID         riid,      //[in ] Requested Interface
793  *  UINT*          prgfInOut, //[   ] reserved
794  *  LPVOID*        ppvObject) //[out] Resulting Interface
795  *
796  */
797 HRESULT WINAPI CDesktopFolder::GetUIObjectOf(
798     HWND hwndOwner,
799     UINT cidl,
800     PCUITEMID_CHILD_ARRAY apidl,
801     REFIID riid,
802     UINT *prgfInOut,
803     LPVOID *ppvOut)
804 {
805     LPVOID pObj = NULL;
806     HRESULT hr = E_INVALIDARG;
807 
808     TRACE ("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n",
809            this, hwndOwner, cidl, apidl, shdebugstr_guid (&riid), prgfInOut, ppvOut);
810 
811     if (!ppvOut)
812         return hr;
813 
814     *ppvOut = NULL;
815 
816     if (cidl == 1 && !_ILIsSpecialFolder(apidl[0]))
817     {
818         CComPtr<IShellFolder2> psf;
819         HRESULT hr = _GetSFFromPidl(apidl[0], &psf);
820         if (FAILED_UNEXPECTEDLY(hr))
821             return hr;
822 
823         return psf->GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, ppvOut);
824     }
825 
826     if (IsEqualIID (riid, IID_IContextMenu))
827     {
828         if (cidl > 0 && _ILIsSpecialFolder(apidl[0]))
829         {
830             hr = m_regFolder->GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, &pObj);
831         }
832         else
833         {
834             /* Do not use the context menu of the CFSFolder here. */
835             /* We need to pass a pointer of the CDesktopFolder so as the data object that the context menu gets is rooted to the desktop */
836             /* Otherwise operations like that involve items from both user and shared desktop will not work */
837             HKEY hKeys[16];
838             UINT cKeys = 0;
839             if (cidl > 0)
840             {
841                 AddFSClassKeysToArray(cidl, apidl, hKeys, &cKeys);
842             }
843 
844             DEFCONTEXTMENU dcm;
845             dcm.hwnd = hwndOwner;
846             dcm.pcmcb = this;
847             dcm.pidlFolder = pidlRoot;
848             dcm.psf = this;
849             dcm.cidl = cidl;
850             dcm.apidl = apidl;
851             dcm.cKeys = cKeys;
852             dcm.aKeys = hKeys;
853             dcm.punkAssociationInfo = NULL;
854             hr = SHCreateDefaultContextMenu (&dcm, riid, &pObj);
855         }
856     }
857     else if (IsEqualIID (riid, IID_IDataObject) && (cidl >= 1))
858     {
859         hr = IDataObject_Constructor( hwndOwner, pidlRoot, apidl, cidl, TRUE, (IDataObject **)&pObj);
860     }
861     else if ((IsEqualIID (riid, IID_IExtractIconA) || IsEqualIID (riid, IID_IExtractIconW)) && (cidl == 1))
862     {
863         hr = m_regFolder->GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, &pObj);
864     }
865     else if (IsEqualIID (riid, IID_IDropTarget) && (cidl == 1))
866     {
867         CComPtr<IShellFolder> psfChild;
868         hr = this->BindToObject(apidl[0], NULL, IID_PPV_ARG(IShellFolder, &psfChild));
869         if (FAILED_UNEXPECTEDLY(hr))
870             return hr;
871 
872         return psfChild->CreateViewObject(NULL, riid, ppvOut);
873     }
874     else
875         hr = E_NOINTERFACE;
876 
877     if (SUCCEEDED(hr) && !pObj)
878         hr = E_OUTOFMEMORY;
879 
880     *ppvOut = pObj;
881     TRACE ("(%p)->hr=0x%08x\n", this, hr);
882     return hr;
883 }
884 
885 /**************************************************************************
886  *    CDesktopFolder::GetDisplayNameOf
887  *
888  * NOTES
889  *    special case: pidl = null gives desktop-name back
890  */
891 HRESULT WINAPI CDesktopFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, LPSTRRET strRet)
892 {
893     TRACE ("(%p)->(pidl=%p,0x%08x,%p)\n", this, pidl, dwFlags, strRet);
894     pdump (pidl);
895 
896     if (!strRet)
897         return E_INVALIDARG;
898 
899     if (!_ILIsPidlSimple (pidl))
900     {
901         return SHELL32_GetDisplayNameOfChild(this, pidl, dwFlags, strRet);
902     }
903     else if (_ILIsDesktop(pidl))
904     {
905         if ((GET_SHGDN_RELATION(dwFlags) == SHGDN_NORMAL) && (GET_SHGDN_FOR(dwFlags) & SHGDN_FORPARSING))
906             return SHSetStrRet(strRet, sPathTarget);
907         else
908             return m_regFolder->GetDisplayNameOf(pidl, dwFlags, strRet);
909     }
910 
911     /* file system folder or file rooted at the desktop */
912     CComPtr<IShellFolder2> psf;
913     HRESULT hr = _GetSFFromPidl(pidl, &psf);
914     if (FAILED_UNEXPECTEDLY(hr))
915         return hr;
916 
917     return psf->GetDisplayNameOf(pidl, dwFlags, strRet);
918 }
919 
920 /**************************************************************************
921  *  CDesktopFolder::SetNameOf
922  *  Changes the name of a file object or subfolder, possibly changing its item
923  *  identifier in the process.
924  *
925  * PARAMETERS
926  *  HWND          hwndOwner,  //[in ] Owner window for output
927  *  LPCITEMIDLIST pidl,       //[in ] simple pidl of item to change
928  *  LPCOLESTR     lpszName,   //[in ] the items new display name
929  *  DWORD         dwFlags,    //[in ] SHGNO formatting flags
930  *  LPITEMIDLIST* ppidlOut)   //[out] simple pidl returned
931  */
932 HRESULT WINAPI CDesktopFolder::SetNameOf(
933     HWND hwndOwner,
934     PCUITEMID_CHILD pidl,    /* simple pidl */
935     LPCOLESTR lpName,
936     DWORD dwFlags,
937     PITEMID_CHILD *pPidlOut)
938 {
939     CComPtr<IShellFolder2> psf;
940     HRESULT hr = _GetSFFromPidl(pidl, &psf);
941     if (FAILED_UNEXPECTEDLY(hr))
942         return hr;
943 
944     return psf->SetNameOf(hwndOwner, pidl, lpName, dwFlags, pPidlOut);
945 }
946 
947 HRESULT WINAPI CDesktopFolder::GetDefaultSearchGUID(GUID *pguid)
948 {
949     FIXME ("(%p)\n", this);
950     return E_NOTIMPL;
951 }
952 
953 HRESULT WINAPI CDesktopFolder::EnumSearches(IEnumExtraSearch **ppenum)
954 {
955     FIXME ("(%p)\n", this);
956     return E_NOTIMPL;
957 }
958 
959 HRESULT WINAPI CDesktopFolder::GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay)
960 {
961     TRACE ("(%p)\n", this);
962 
963     if (pSort)
964         *pSort = 0;
965     if (pDisplay)
966         *pDisplay = 0;
967 
968     return S_OK;
969 }
970 
971 HRESULT WINAPI CDesktopFolder::GetDefaultColumnState(UINT iColumn, SHCOLSTATEF *pcsFlags)
972 {
973     HRESULT hr;
974     TRACE ("(%p)\n", this);
975 
976     if (!pcsFlags)
977         return E_INVALIDARG;
978 
979     hr = CFSFolder::GetDefaultFSColumnState(iColumn, *pcsFlags);
980     /*
981     // CDesktopFolder may override the flags if desired (future)
982     switch(iColumn)
983     {
984     case SHFSF_COL_FATTS:
985         *pcsFlags &= ~SHCOLSTATE_ONBYDEFAULT;
986         break;
987     }
988     */
989     return hr;
990 }
991 
992 HRESULT WINAPI CDesktopFolder::GetDetailsEx(
993     PCUITEMID_CHILD pidl,
994     const SHCOLUMNID *pscid,
995     VARIANT *pv)
996 {
997     FIXME ("(%p)\n", this);
998 
999     return E_NOTIMPL;
1000 }
1001 
1002 /*************************************************************************
1003  * Column info functions.
1004  * CFSFolder.h provides defaults for us.
1005  */
1006 HRESULT CDesktopFolder::GetColumnDetails(UINT iColumn, SHELLDETAILS &sd)
1007 {
1008     /* CDesktopFolder may override the flags and/or name if desired */
1009     return CFSFolder::GetFSColumnDetails(iColumn, sd);
1010 }
1011 
1012 HRESULT WINAPI CDesktopFolder::GetDetailsOf(
1013     PCUITEMID_CHILD pidl,
1014     UINT iColumn,
1015     SHELLDETAILS *psd)
1016 {
1017     if (!psd)
1018         return E_INVALIDARG;
1019 
1020     if (!pidl)
1021     {
1022         return GetColumnDetails(iColumn, *psd);
1023     }
1024 
1025     CComPtr<IShellFolder2> psf;
1026     HRESULT hr = _GetSFFromPidl(pidl, &psf);
1027     if (FAILED_UNEXPECTEDLY(hr))
1028         return hr;
1029 
1030     hr =  psf->GetDetailsOf(pidl, iColumn, psd);
1031     if (FAILED_UNEXPECTEDLY(hr))
1032         return hr;
1033 
1034     return hr;
1035 }
1036 
1037 HRESULT WINAPI CDesktopFolder::MapColumnToSCID(UINT column, SHCOLUMNID *pscid)
1038 {
1039     FIXME ("(%p)\n", this);
1040     return E_NOTIMPL;
1041 }
1042 
1043 HRESULT WINAPI CDesktopFolder::GetClassID(CLSID *lpClassId)
1044 {
1045     TRACE ("(%p)\n", this);
1046 
1047     if (!lpClassId)
1048         return E_POINTER;
1049 
1050     *lpClassId = CLSID_ShellDesktop;
1051 
1052     return S_OK;
1053 }
1054 
1055 HRESULT WINAPI CDesktopFolder::Initialize(PCIDLIST_ABSOLUTE pidl)
1056 {
1057     TRACE ("(%p)->(%p)\n", this, pidl);
1058 
1059     if (!pidl)
1060         return S_OK;
1061 
1062     return E_INVALIDARG;
1063 }
1064 
1065 HRESULT WINAPI CDesktopFolder::GetCurFolder(PIDLIST_ABSOLUTE * pidl)
1066 {
1067     TRACE ("(%p)->(%p)\n", this, pidl);
1068 
1069     if (!pidl)
1070         return E_INVALIDARG; /* xp doesn't have this check and crashes on NULL */
1071     *pidl = ILClone (pidlRoot);
1072     return S_OK;
1073 }
1074 
1075 HRESULT WINAPI CDesktopFolder::CallBack(IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
1076 {
1077     if (uMsg != DFM_MERGECONTEXTMENU && uMsg != DFM_INVOKECOMMAND)
1078         return S_OK;
1079 
1080     /* no data object means no selection */
1081     if (!pdtobj)
1082     {
1083         if (uMsg == DFM_INVOKECOMMAND && wParam == 0)
1084         {
1085             if (32 >= (UINT_PTR)ShellExecuteW(hwndOwner, L"open", L"rundll32.exe",
1086                                               L"shell32.dll,Control_RunDLL desk.cpl", NULL, SW_SHOWNORMAL))
1087             {
1088                 return E_FAIL;
1089             }
1090             return S_OK;
1091         }
1092         else if (uMsg == DFM_MERGECONTEXTMENU)
1093         {
1094             QCMINFO *pqcminfo = (QCMINFO *)lParam;
1095             HMENU hpopup = CreatePopupMenu();
1096             _InsertMenuItemW(hpopup, 0, TRUE, 0, MFT_STRING, MAKEINTRESOURCEW(IDS_PROPERTIES), MFS_ENABLED);
1097             Shell_MergeMenus(pqcminfo->hmenu, hpopup, pqcminfo->indexMenu, pqcminfo->idCmdFirst++, pqcminfo->idCmdLast, MM_ADDSEPARATOR);
1098             DestroyMenu(hpopup);
1099         }
1100 
1101         return S_OK;
1102     }
1103 
1104     if (uMsg != DFM_INVOKECOMMAND || wParam != DFM_CMD_PROPERTIES)
1105         return S_OK;
1106 
1107     return Shell_DefaultContextMenuCallBack(this, pdtobj);
1108 }
1109 
1110 /*************************************************************************
1111  * SHGetDesktopFolder            [SHELL32.@]
1112  */
1113 HRESULT WINAPI SHGetDesktopFolder(IShellFolder **psf)
1114 {
1115     HRESULT    hres = S_OK;
1116     TRACE("\n");
1117 
1118     if(!psf) return E_INVALIDARG;
1119     *psf = NULL;
1120     hres = CDesktopFolder::_CreatorClass::CreateInstance(NULL, IID_PPV_ARG(IShellFolder, psf));
1121 
1122     TRACE("-- %p->(%p)\n",psf, *psf);
1123     return hres;
1124 }
1125