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