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 
25 WINE_DEFAULT_DEBUG_CHANNEL(shell);
26 
27 /*
28 CDesktopFolder should create two file system folders internally, one representing the
29 user's desktop folder, and the other representing the common desktop folder. It should
30 also create a CRegFolder to represent the virtual items that exist only in the registry.
31 The CRegFolder is aggregated by the CDesktopFolder, and queries for the CLSID_IShellFolder,
32 CLSID_IShellFolder2, or CLSID_IShellIconOverlay interfaces prefer the CRegFolder
33 implementation.
34 The CDesktopFolderEnum class should create two enumerators, one for each of the file
35 system folders, and enumerate the contents of each folder. Since the CRegFolder
36 implementation of IShellFolder::EnumObjects enumerates the virtual items, the
37 CDesktopFolderEnum is only responsible for returning the physical items.
38 CDesktopFolderEnum is incorrect where it filters My Computer from the enumeration
39 if the new start menu is used. The CDesktopViewCallback is responsible for filtering
40 it from the view by handling the IncludeObject query to return S_FALSE. The enumerator
41 always shows My Computer.
42 */
43 
44 /* Undocumented functions from shdocvw */
45 extern "C" HRESULT WINAPI IEParseDisplayNameWithBCW(DWORD codepage, LPCWSTR lpszDisplayName, LPBC pbc, LPITEMIDLIST *ppidl);
46 
47 static const WCHAR ClassicStartMenuW[] = L"SOFTWARE\\Microsoft\\Windows\\"
48     L"CurrentVersion\\Explorer\\HideDesktopIcons\\ClassicStartMenu";
49 
50 static INT
51 IsNamespaceExtensionHidden(const WCHAR *iid)
52 {
53     DWORD Result, dwResult;
54     dwResult = sizeof(DWORD);
55 
56     if (RegGetValueW(HKEY_CURRENT_USER, /* FIXME use NewStartPanel when activated */
57                      ClassicStartMenuW,
58                      iid,
59                      RRF_RT_DWORD,
60                      NULL,
61                      &Result,
62                      &dwResult) != ERROR_SUCCESS)
63     {
64         return -1;
65     }
66 
67     return Result;
68 }
69 
70 static INT IsNamespaceExtensionHidden(LPCITEMIDLIST pidl)
71 {
72     GUID const *clsid = _ILGetGUIDPointer (pidl);
73     if (!clsid)
74         return -1;
75 
76     WCHAR pwszGuid[CHARS_IN_GUID];
77     SHELL32_GUIDToStringW(*clsid, pwszGuid);
78     return IsNamespaceExtensionHidden(pwszGuid);
79 }
80 
81 class CDesktopFolderEnum :
82     public CEnumIDListBase
83 {
84     private:
85 //    CComPtr                                fDesktopEnumerator;
86 //    CComPtr                                fCommonDesktopEnumerator;
87     public:
88 
89         void AddItemsFromClassicStartMenuKey(HKEY hKeyRoot)
90         {
91             DWORD dwResult;
92             HKEY hkey;
93             DWORD j = 0, dwVal, Val, dwType, dwIID;
94             LONG r;
95             WCHAR iid[50];
96             LPITEMIDLIST pidl;
97 
98             dwResult = RegOpenKeyExW(hKeyRoot, ClassicStartMenuW, 0, KEY_READ, &hkey);
99             if (dwResult != ERROR_SUCCESS)
100                 return;
101 
102             while(1)
103             {
104                 dwVal = sizeof(Val);
105                 dwIID = sizeof(iid) / sizeof(WCHAR);
106 
107                 r = RegEnumValueW(hkey, j++, iid, &dwIID, NULL, &dwType, (LPBYTE)&Val, &dwVal);
108                 if (r != ERROR_SUCCESS)
109                     break;
110 
111                 if (Val == 0 && dwType == REG_DWORD)
112                 {
113                     pidl = _ILCreateGuidFromStrW(iid);
114                     if (pidl != NULL)
115                     {
116                         if (!HasItemWithCLSID(pidl))
117                             AddToEnumList(pidl);
118                         else
119                             SHFree(pidl);
120                     }
121                 }
122             }
123             RegCloseKey(hkey);
124         }
125 
126         HRESULT WINAPI Initialize(DWORD dwFlags,IEnumIDList * pRegEnumerator, IEnumIDList *pDesktopEnumerator, IEnumIDList *pCommonDesktopEnumerator)
127         {
128             BOOL ret = TRUE;
129             LPITEMIDLIST pidl;
130 
131             static const WCHAR MyDocumentsClassString[] = L"{450D8FBA-AD25-11D0-98A8-0800361B1103}";
132             static const WCHAR InternetClassString[] = L"{871C5380-42A0-1069-A2EA-08002B30309D}";
133 
134             TRACE("(%p)->(flags=0x%08x)\n", this, dwFlags);
135 
136             /* enumerate the root folders */
137             if (dwFlags & SHCONTF_FOLDERS)
138             {
139                 AddToEnumList(_ILCreateMyComputer());
140                 if (IsNamespaceExtensionHidden(MyDocumentsClassString) < 1)
141                     AddToEnumList(_ILCreateMyDocuments());
142                 if (IsNamespaceExtensionHidden(InternetClassString) < 1)
143                     AddToEnumList(_ILCreateIExplore());
144 
145                 DWORD dwFetched;
146                 while((S_OK == pRegEnumerator->Next(1, &pidl, &dwFetched)) && dwFetched)
147                 {
148                     if (IsNamespaceExtensionHidden(pidl) < 1)
149                     {
150                         if (!HasItemWithCLSID(pidl))
151                             AddToEnumList(pidl);
152                         else
153                             SHFree(pidl);
154                     }
155                 }
156                 AddItemsFromClassicStartMenuKey(HKEY_LOCAL_MACHINE);
157                 AddItemsFromClassicStartMenuKey(HKEY_CURRENT_USER);
158             }
159 
160             /* Enumerate the items in the two fs folders */
161             AppendItemsFromEnumerator(pDesktopEnumerator);
162             AppendItemsFromEnumerator(pCommonDesktopEnumerator);
163 
164             return ret ? S_OK : E_FAIL;
165         }
166 
167 
168         BEGIN_COM_MAP(CDesktopFolderEnum)
169         COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList)
170         END_COM_MAP()
171 };
172 
173 int SHELL_ConfirmMsgBox(HWND hWnd, LPWSTR lpszText, LPWSTR lpszCaption, HICON hIcon, BOOL bYesToAll);
174 
175 static const shvheader DesktopSFHeader[] = {
176     {IDS_SHV_COLUMN_NAME, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 15},
177     {IDS_SHV_COLUMN_COMMENTS, SHCOLSTATE_TYPE_STR, LVCFMT_LEFT, 10},
178     {IDS_SHV_COLUMN_TYPE, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 10},
179     {IDS_SHV_COLUMN_SIZE, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
180     {IDS_SHV_COLUMN_MODIFIED, SHCOLSTATE_TYPE_DATE | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 12},
181     {IDS_SHV_COLUMN_ATTRIBUTES, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 10}
182 };
183 
184 #define DESKTOPSHELLVIEWCOLUMNS 6
185 
186 static const DWORD dwDesktopAttributes =
187     SFGAO_HASSUBFOLDER | SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR |
188     SFGAO_STORAGEANCESTOR | SFGAO_HASPROPSHEET | SFGAO_STORAGE;
189 static const DWORD dwMyComputerAttributes =
190     SFGAO_CANRENAME | SFGAO_CANDELETE | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET |
191     SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_CANLINK;
192 static DWORD dwMyNetPlacesAttributes =
193     SFGAO_CANRENAME | SFGAO_CANDELETE | SFGAO_HASPROPSHEET | SFGAO_DROPTARGET |
194     SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_CANLINK;
195 
196 CDesktopFolder::CDesktopFolder() :
197     sPathTarget(NULL),
198     pidlRoot(NULL)
199 {
200 }
201 
202 CDesktopFolder::~CDesktopFolder()
203 {
204 }
205 
206 HRESULT WINAPI CDesktopFolder::FinalConstruct()
207 {
208     WCHAR szMyPath[MAX_PATH];
209     HRESULT hr;
210 
211     /* Create the root pidl */
212     pidlRoot = _ILCreateDesktop();
213     if (!pidlRoot)
214         return E_OUTOFMEMORY;
215 
216     /* Create the inner fs folder */
217     hr = SHELL32_CoCreateInitSF(pidlRoot,
218                                 &CLSID_ShellFSFolder,
219                                 CSIDL_DESKTOPDIRECTORY,
220                                 IID_PPV_ARG(IShellFolder2, &m_DesktopFSFolder));
221     if (FAILED_UNEXPECTEDLY(hr))
222         return hr;
223 
224     /* Create the inner shared fs folder. Dont fail on failure. */
225     hr = SHELL32_CoCreateInitSF(pidlRoot,
226                                 &CLSID_ShellFSFolder,
227                                 CSIDL_COMMON_DESKTOPDIRECTORY,
228                                 IID_PPV_ARG(IShellFolder2, &m_SharedDesktopFSFolder));
229     if (FAILED_UNEXPECTEDLY(hr))
230         return hr;
231 
232     /* Create the inner reg folder */
233     hr = CRegFolder_CreateInstance(&CLSID_ShellDesktop,
234                                    pidlRoot,
235                                    L"",
236                                    L"Desktop",
237                                    IID_PPV_ARG(IShellFolder2, &m_regFolder));
238     if (FAILED_UNEXPECTEDLY(hr))
239         return hr;
240 
241     /* Cache the path to the user desktop directory */
242     if (!SHGetSpecialFolderPathW( 0, szMyPath, CSIDL_DESKTOPDIRECTORY, TRUE ))
243         return E_UNEXPECTED;
244 
245     sPathTarget = (LPWSTR)SHAlloc((wcslen(szMyPath) + 1) * sizeof(WCHAR));
246     if (!sPathTarget)
247         return E_OUTOFMEMORY;
248 
249     wcscpy(sPathTarget, szMyPath);
250     return S_OK;
251 }
252 
253 HRESULT CDesktopFolder::_GetSFFromPidl(LPCITEMIDLIST pidl, IShellFolder2** psf)
254 {
255     WCHAR szFileName[MAX_PATH];
256 
257     if (_ILIsSpecialFolder(pidl))
258         return m_regFolder->QueryInterface(IID_PPV_ARG(IShellFolder2, psf));
259 
260     lstrcpynW(szFileName, sPathTarget, MAX_PATH - 1);
261     PathAddBackslashW(szFileName);
262     int cLen = wcslen(szFileName);
263 
264     if (!_ILSimpleGetTextW(pidl, szFileName + cLen, MAX_PATH - cLen))
265         return E_FAIL;
266 
267     if (GetFileAttributes(szFileName) == INVALID_FILE_ATTRIBUTES)
268         return m_SharedDesktopFSFolder->QueryInterface(IID_PPV_ARG(IShellFolder2, psf));
269     else
270         return m_DesktopFSFolder->QueryInterface(IID_PPV_ARG(IShellFolder2, psf));
271 }
272 
273 /**************************************************************************
274  *    CDesktopFolder::ParseDisplayName
275  *
276  * NOTES
277  *    "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}" and "" binds
278  *    to MyComputer
279  */
280 HRESULT WINAPI CDesktopFolder::ParseDisplayName(
281     HWND hwndOwner,
282     LPBC pbc,
283     LPOLESTR lpszDisplayName,
284     DWORD *pchEaten,
285     PIDLIST_RELATIVE *ppidl,
286     DWORD *pdwAttributes)
287 {
288     LPCWSTR szNext = NULL;
289     LPITEMIDLIST pidlTemp = NULL;
290     PARSEDURLW urldata;
291     HRESULT hr = S_OK;
292 
293     TRACE ("(%p)->(HWND=%p,%p,%p=%s,%p,pidl=%p,%p)\n",
294            this, hwndOwner, pbc, lpszDisplayName, debugstr_w(lpszDisplayName),
295            pchEaten, ppidl, pdwAttributes);
296 
297     if (!ppidl)
298         return E_INVALIDARG;
299 
300     *ppidl = NULL;
301 
302     if (!lpszDisplayName)
303         return E_INVALIDARG;
304 
305     if (pchEaten)
306         *pchEaten = 0;        /* strange but like the original */
307 
308     urldata.cbSize = sizeof(urldata);
309 
310     if (lpszDisplayName[0] == ':' && lpszDisplayName[1] == ':')
311     {
312         return m_regFolder->ParseDisplayName(hwndOwner, pbc, lpszDisplayName, pchEaten, ppidl, pdwAttributes);
313     }
314     else if (PathGetDriveNumberW (lpszDisplayName) >= 0)
315     {
316         /* it's a filesystem path with a drive. Let MyComputer/UnixDosFolder parse it */
317         pidlTemp = _ILCreateMyComputer ();
318         szNext = lpszDisplayName;
319     }
320     else if (PathIsUNCW(lpszDisplayName))
321     {
322         pidlTemp = _ILCreateNetwork();
323         szNext = lpszDisplayName;
324     }
325     else if( (pidlTemp = SHELL32_CreatePidlFromBindCtx(pbc, lpszDisplayName)) )
326     {
327         *ppidl = pidlTemp;
328         return S_OK;
329     }
330     else if (SUCCEEDED(ParseURLW(lpszDisplayName, &urldata)))
331     {
332         if (urldata.nScheme == URL_SCHEME_SHELL) /* handle shell: urls */
333         {
334             TRACE ("-- shell url: %s\n", debugstr_w(urldata.pszSuffix));
335             pidlTemp = _ILCreateGuidFromStrW(urldata.pszSuffix + 2);
336         }
337         else
338             return IEParseDisplayNameWithBCW(CP_ACP, lpszDisplayName, pbc, ppidl);
339     }
340     else
341     {
342         if (*lpszDisplayName)
343         {
344             /* it's a filesystem path on the desktop. Let a FSFolder parse it */
345             hr = m_DesktopFSFolder->ParseDisplayName(hwndOwner, pbc, lpszDisplayName, pchEaten, ppidl, pdwAttributes);
346             if (SUCCEEDED(hr))
347                 return hr;
348 
349             return m_SharedDesktopFSFolder->ParseDisplayName(hwndOwner, pbc, lpszDisplayName, pchEaten, ppidl, pdwAttributes);
350         }
351         else
352             pidlTemp = _ILCreateMyComputer();
353 
354         szNext = NULL;
355     }
356 
357     if (SUCCEEDED(hr) && pidlTemp)
358     {
359         if (szNext && *szNext)
360         {
361             hr = SHELL32_ParseNextElement(this, hwndOwner, pbc,
362                                           &pidlTemp, (LPOLESTR) szNext, pchEaten, pdwAttributes);
363         }
364         else
365         {
366             if (pdwAttributes && *pdwAttributes)
367             {
368                 GetAttributesOf(1, &pidlTemp, pdwAttributes);
369             }
370         }
371     }
372 
373     if (SUCCEEDED(hr))
374         *ppidl = pidlTemp;
375     else
376         *ppidl = NULL;
377 
378     TRACE ("(%p)->(-- ret=0x%08x)\n", this, hr);
379 
380     return hr;
381 }
382 
383 /**************************************************************************
384  *        CDesktopFolder::EnumObjects
385  */
386 HRESULT WINAPI CDesktopFolder::EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList)
387 {
388     CComPtr<IEnumIDList> pRegEnumerator;
389     CComPtr<IEnumIDList> pDesktopEnumerator;
390     CComPtr<IEnumIDList> pCommonDesktopEnumerator;
391     HRESULT hr;
392 
393     hr = m_regFolder->EnumObjects(hwndOwner, dwFlags, &pRegEnumerator);
394     if (FAILED(hr))
395         ERR("EnumObjects for reg folder failed\n");
396 
397     hr = m_DesktopFSFolder->EnumObjects(hwndOwner, dwFlags, &pDesktopEnumerator);
398     if (FAILED(hr))
399         ERR("EnumObjects for desktop fs folder failed\n");
400 
401     hr = m_SharedDesktopFSFolder->EnumObjects(hwndOwner, dwFlags, &pCommonDesktopEnumerator);
402     if (FAILED(hr))
403         ERR("EnumObjects for shared desktop fs folder failed\n");
404 
405     return ShellObjectCreatorInit<CDesktopFolderEnum>(dwFlags,pRegEnumerator, pDesktopEnumerator, pCommonDesktopEnumerator, IID_PPV_ARG(IEnumIDList, ppEnumIDList));
406 }
407 
408 /**************************************************************************
409  *        CDesktopFolder::BindToObject
410  */
411 HRESULT WINAPI CDesktopFolder::BindToObject(
412     PCUIDLIST_RELATIVE pidl,
413     LPBC pbcReserved,
414     REFIID riid,
415     LPVOID *ppvOut)
416 {
417     if (!pidl)
418         return E_INVALIDARG;
419 
420     CComPtr<IShellFolder2> psf;
421     HRESULT hr = _GetSFFromPidl(pidl, &psf);
422     if (FAILED_UNEXPECTEDLY(hr))
423         return hr;
424 
425     return psf->BindToObject(pidl, pbcReserved, riid, ppvOut);
426 }
427 
428 /**************************************************************************
429  *    CDesktopFolder::BindToStorage
430  */
431 HRESULT WINAPI CDesktopFolder::BindToStorage(
432     PCUIDLIST_RELATIVE pidl,
433     LPBC pbcReserved,
434     REFIID riid,
435     LPVOID *ppvOut)
436 {
437     FIXME ("(%p)->(pidl=%p,%p,%s,%p) stub\n",
438            this, pidl, pbcReserved, shdebugstr_guid (&riid), ppvOut);
439 
440     *ppvOut = NULL;
441     return E_NOTIMPL;
442 }
443 
444 /**************************************************************************
445  *     CDesktopFolder::CompareIDs
446  */
447 HRESULT WINAPI CDesktopFolder::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2)
448 {
449     bool bIsDesktopFolder1, bIsDesktopFolder2;
450 
451     if (!pidl1 || !pidl2)
452     {
453         ERR("Got null pidl pointer (%Ix %p %p)!\n", lParam, pidl1, pidl2);
454         return E_INVALIDARG;
455     }
456 
457     bIsDesktopFolder1 = _ILIsDesktop(pidl1);
458     bIsDesktopFolder2 = _ILIsDesktop(pidl2);
459     if (bIsDesktopFolder1 || bIsDesktopFolder2)
460         return MAKE_COMPARE_HRESULT(bIsDesktopFolder1 - bIsDesktopFolder2);
461 
462     if (_ILIsSpecialFolder(pidl1) || _ILIsSpecialFolder(pidl2))
463         return m_regFolder->CompareIDs(lParam, pidl1, pidl2);
464 
465     return m_DesktopFSFolder->CompareIDs(lParam, pidl1, pidl2);
466 }
467 
468 /**************************************************************************
469  *    CDesktopFolder::CreateViewObject
470  */
471 HRESULT WINAPI CDesktopFolder::CreateViewObject(
472     HWND hwndOwner,
473     REFIID riid,
474     LPVOID *ppvOut)
475 {
476     HRESULT hr = E_INVALIDARG;
477 
478     TRACE ("(%p)->(hwnd=%p,%s,%p)\n",
479            this, hwndOwner, shdebugstr_guid (&riid), ppvOut);
480 
481     if (!ppvOut)
482         return hr;
483 
484     *ppvOut = NULL;
485 
486     if (IsEqualIID (riid, IID_IDropTarget))
487     {
488         hr = m_DesktopFSFolder->CreateViewObject(hwndOwner, riid, ppvOut);
489     }
490     else if (IsEqualIID (riid, IID_IContextMenu))
491     {
492             HKEY hKeys[16];
493             UINT cKeys = 0;
494             AddClassKeyToArray(L"Directory\\Background", hKeys, &cKeys);
495 
496             DEFCONTEXTMENU dcm;
497             dcm.hwnd = hwndOwner;
498             dcm.pcmcb = this;
499             dcm.pidlFolder = pidlRoot;
500             dcm.psf = this;
501             dcm.cidl = 0;
502             dcm.apidl = NULL;
503             dcm.cKeys = cKeys;
504             dcm.aKeys = hKeys;
505             dcm.punkAssociationInfo = NULL;
506             hr = SHCreateDefaultContextMenu (&dcm, riid, ppvOut);
507     }
508     else if (IsEqualIID (riid, IID_IShellView))
509     {
510         SFV_CREATE sfvparams = {sizeof(SFV_CREATE), this};
511         hr = SHCreateShellFolderView(&sfvparams, (IShellView**)ppvOut);
512     }
513     TRACE ("-- (%p)->(interface=%p)\n", this, ppvOut);
514     return hr;
515 }
516 
517 /**************************************************************************
518  *  CDesktopFolder::GetAttributesOf
519  */
520 HRESULT WINAPI CDesktopFolder::GetAttributesOf(
521     UINT cidl,
522     PCUITEMID_CHILD_ARRAY apidl,
523     DWORD *rgfInOut)
524 {
525     HRESULT hr = S_OK;
526 
527     TRACE("(%p)->(cidl=%d apidl=%p mask=%p (0x%08x))\n",
528           this, cidl, apidl, rgfInOut, rgfInOut ? *rgfInOut : 0);
529 
530     if (cidl && !apidl)
531         return E_INVALIDARG;
532 
533     if (*rgfInOut == 0)
534         *rgfInOut = ~0;
535 
536     if(cidl == 0)
537         *rgfInOut &= dwDesktopAttributes;
538     else
539     {
540         /* TODO: always add SFGAO_CANLINK */
541         for (UINT i = 0; i < cidl; ++i)
542         {
543             pdump(*apidl);
544             if (_ILIsDesktop(*apidl))
545                 *rgfInOut &= dwDesktopAttributes;
546             else if (_ILIsMyComputer(apidl[i]))
547                 *rgfInOut &= dwMyComputerAttributes;
548             else if (_ILIsNetHood(apidl[i]))
549                 *rgfInOut &= dwMyNetPlacesAttributes;
550             else if (_ILIsFolder(apidl[i]) || _ILIsValue(apidl[i]) || _ILIsSpecialFolder(apidl[i]))
551             {
552                 CComPtr<IShellFolder2> psf;
553                 HRESULT hr = _GetSFFromPidl(apidl[i], &psf);
554                 if (FAILED_UNEXPECTEDLY(hr))
555                     continue;
556 
557                 psf->GetAttributesOf(1, &apidl[i], rgfInOut);
558             }
559             else
560                 ERR("Got an unknown pidl type!!!\n");
561         }
562     }
563     /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */
564     *rgfInOut &= ~SFGAO_VALIDATE;
565 
566     TRACE("-- result=0x%08x\n", *rgfInOut);
567 
568     return hr;
569 }
570 
571 /**************************************************************************
572  *    CDesktopFolder::GetUIObjectOf
573  *
574  * PARAMETERS
575  *  HWND           hwndOwner, //[in ] Parent window for any output
576  *  UINT           cidl,      //[in ] array size
577  *  LPCITEMIDLIST* apidl,     //[in ] simple pidl array
578  *  REFIID         riid,      //[in ] Requested Interface
579  *  UINT*          prgfInOut, //[   ] reserved
580  *  LPVOID*        ppvObject) //[out] Resulting Interface
581  *
582  */
583 HRESULT WINAPI CDesktopFolder::GetUIObjectOf(
584     HWND hwndOwner,
585     UINT cidl,
586     PCUITEMID_CHILD_ARRAY apidl,
587     REFIID riid,
588     UINT *prgfInOut,
589     LPVOID *ppvOut)
590 {
591     LPVOID pObj = NULL;
592     HRESULT hr = E_INVALIDARG;
593 
594     TRACE ("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n",
595            this, hwndOwner, cidl, apidl, shdebugstr_guid (&riid), prgfInOut, ppvOut);
596 
597     if (!ppvOut)
598         return hr;
599 
600     *ppvOut = NULL;
601 
602     if (cidl == 1 && !_ILIsSpecialFolder(apidl[0]))
603     {
604         CComPtr<IShellFolder2> psf;
605         HRESULT hr = _GetSFFromPidl(apidl[0], &psf);
606         if (FAILED_UNEXPECTEDLY(hr))
607             return hr;
608 
609         return psf->GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, ppvOut);
610     }
611 
612     if (IsEqualIID (riid, IID_IContextMenu))
613     {
614         if (cidl > 0 && _ILIsSpecialFolder(apidl[0]))
615         {
616             hr = m_regFolder->GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, &pObj);
617         }
618         else
619         {
620             /* Do not use the context menu of the CFSFolder here. */
621             /* We need to pass a pointer of the CDesktopFolder so as the data object that the context menu gets is rooted to the desktop */
622             /* Otherwise operations like that involve items from both user and shared desktop will not work */
623             HKEY hKeys[16];
624             UINT cKeys = 0;
625             if (cidl > 0)
626             {
627                 AddFSClassKeysToArray(apidl[0], hKeys, &cKeys);
628             }
629 
630             DEFCONTEXTMENU dcm;
631             dcm.hwnd = hwndOwner;
632             dcm.pcmcb = this;
633             dcm.pidlFolder = pidlRoot;
634             dcm.psf = this;
635             dcm.cidl = cidl;
636             dcm.apidl = apidl;
637             dcm.cKeys = cKeys;
638             dcm.aKeys = hKeys;
639             dcm.punkAssociationInfo = NULL;
640             hr = SHCreateDefaultContextMenu (&dcm, riid, &pObj);
641         }
642     }
643     else if (IsEqualIID (riid, IID_IDataObject) && (cidl >= 1))
644     {
645         hr = IDataObject_Constructor( hwndOwner, pidlRoot, apidl, cidl, TRUE, (IDataObject **)&pObj);
646     }
647     else if ((IsEqualIID (riid, IID_IExtractIconA) || IsEqualIID (riid, IID_IExtractIconW)) && (cidl == 1))
648     {
649         hr = m_regFolder->GetUIObjectOf(hwndOwner, cidl, apidl, riid, prgfInOut, &pObj);
650     }
651     else if (IsEqualIID (riid, IID_IDropTarget) && (cidl == 1))
652     {
653         CComPtr<IShellFolder> psfChild;
654         hr = this->BindToObject(apidl[0], NULL, IID_PPV_ARG(IShellFolder, &psfChild));
655         if (FAILED_UNEXPECTEDLY(hr))
656             return hr;
657 
658         return psfChild->CreateViewObject(NULL, riid, ppvOut);
659     }
660     else
661         hr = E_NOINTERFACE;
662 
663     if (SUCCEEDED(hr) && !pObj)
664         hr = E_OUTOFMEMORY;
665 
666     *ppvOut = pObj;
667     TRACE ("(%p)->hr=0x%08x\n", this, hr);
668     return hr;
669 }
670 
671 /**************************************************************************
672  *    CDesktopFolder::GetDisplayNameOf
673  *
674  * NOTES
675  *    special case: pidl = null gives desktop-name back
676  */
677 HRESULT WINAPI CDesktopFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, LPSTRRET strRet)
678 {
679     TRACE ("(%p)->(pidl=%p,0x%08x,%p)\n", this, pidl, dwFlags, strRet);
680     pdump (pidl);
681 
682     if (!strRet)
683         return E_INVALIDARG;
684 
685     if (!_ILIsPidlSimple (pidl))
686     {
687         return SHELL32_GetDisplayNameOfChild(this, pidl, dwFlags, strRet);
688     }
689     else if (_ILIsDesktop(pidl))
690     {
691         if ((GET_SHGDN_RELATION(dwFlags) == SHGDN_NORMAL) && (GET_SHGDN_FOR(dwFlags) & SHGDN_FORPARSING))
692             return SHSetStrRet(strRet, sPathTarget);
693         else
694             return m_regFolder->GetDisplayNameOf(pidl, dwFlags, strRet);
695     }
696 
697     /* file system folder or file rooted at the desktop */
698     CComPtr<IShellFolder2> psf;
699     HRESULT hr = _GetSFFromPidl(pidl, &psf);
700     if (FAILED_UNEXPECTEDLY(hr))
701         return hr;
702 
703     return psf->GetDisplayNameOf(pidl, dwFlags, strRet);
704 }
705 
706 /**************************************************************************
707  *  CDesktopFolder::SetNameOf
708  *  Changes the name of a file object or subfolder, possibly changing its item
709  *  identifier in the process.
710  *
711  * PARAMETERS
712  *  HWND          hwndOwner,  //[in ] Owner window for output
713  *  LPCITEMIDLIST pidl,       //[in ] simple pidl of item to change
714  *  LPCOLESTR     lpszName,   //[in ] the items new display name
715  *  DWORD         dwFlags,    //[in ] SHGNO formatting flags
716  *  LPITEMIDLIST* ppidlOut)   //[out] simple pidl returned
717  */
718 HRESULT WINAPI CDesktopFolder::SetNameOf(
719     HWND hwndOwner,
720     PCUITEMID_CHILD pidl,    /* simple pidl */
721     LPCOLESTR lpName,
722     DWORD dwFlags,
723     PITEMID_CHILD *pPidlOut)
724 {
725     CComPtr<IShellFolder2> psf;
726     HRESULT hr = _GetSFFromPidl(pidl, &psf);
727     if (FAILED_UNEXPECTEDLY(hr))
728         return hr;
729 
730     return psf->SetNameOf(hwndOwner, pidl, lpName, dwFlags, pPidlOut);
731 }
732 
733 HRESULT WINAPI CDesktopFolder::GetDefaultSearchGUID(GUID *pguid)
734 {
735     FIXME ("(%p)\n", this);
736     return E_NOTIMPL;
737 }
738 
739 HRESULT WINAPI CDesktopFolder::EnumSearches(IEnumExtraSearch **ppenum)
740 {
741     FIXME ("(%p)\n", this);
742     return E_NOTIMPL;
743 }
744 
745 HRESULT WINAPI CDesktopFolder::GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay)
746 {
747     TRACE ("(%p)\n", this);
748 
749     if (pSort)
750         *pSort = 0;
751     if (pDisplay)
752         *pDisplay = 0;
753 
754     return S_OK;
755 }
756 
757 HRESULT WINAPI CDesktopFolder::GetDefaultColumnState(UINT iColumn, DWORD *pcsFlags)
758 {
759     TRACE ("(%p)\n", this);
760 
761     if (!pcsFlags || iColumn >= DESKTOPSHELLVIEWCOLUMNS)
762         return E_INVALIDARG;
763 
764     *pcsFlags = DesktopSFHeader[iColumn].pcsFlags;
765 
766     return S_OK;
767 }
768 
769 HRESULT WINAPI CDesktopFolder::GetDetailsEx(
770     PCUITEMID_CHILD pidl,
771     const SHCOLUMNID *pscid,
772     VARIANT *pv)
773 {
774     FIXME ("(%p)\n", this);
775 
776     return E_NOTIMPL;
777 }
778 
779 HRESULT WINAPI CDesktopFolder::GetDetailsOf(
780     PCUITEMID_CHILD pidl,
781     UINT iColumn,
782     SHELLDETAILS *psd)
783 {
784     if (!psd || iColumn >= DESKTOPSHELLVIEWCOLUMNS)
785         return E_INVALIDARG;
786 
787     if (!pidl)
788     {
789         psd->fmt = DesktopSFHeader[iColumn].fmt;
790         psd->cxChar = DesktopSFHeader[iColumn].cxChar;
791         return SHSetStrRet(&psd->str, DesktopSFHeader[iColumn].colnameid);
792     }
793 
794     CComPtr<IShellFolder2> psf;
795     HRESULT hr = _GetSFFromPidl(pidl, &psf);
796     if (FAILED_UNEXPECTEDLY(hr))
797         return hr;
798 
799     hr =  psf->GetDetailsOf(pidl, iColumn, psd);
800     if (FAILED_UNEXPECTEDLY(hr))
801         return hr;
802 
803     return hr;
804 }
805 
806 HRESULT WINAPI CDesktopFolder::MapColumnToSCID(UINT column, SHCOLUMNID *pscid)
807 {
808     FIXME ("(%p)\n", this);
809     return E_NOTIMPL;
810 }
811 
812 HRESULT WINAPI CDesktopFolder::GetClassID(CLSID *lpClassId)
813 {
814     TRACE ("(%p)\n", this);
815 
816     if (!lpClassId)
817         return E_POINTER;
818 
819     *lpClassId = CLSID_ShellDesktop;
820 
821     return S_OK;
822 }
823 
824 HRESULT WINAPI CDesktopFolder::Initialize(PCIDLIST_ABSOLUTE pidl)
825 {
826     TRACE ("(%p)->(%p)\n", this, pidl);
827 
828     if (!pidl)
829         return S_OK;
830 
831     return E_INVALIDARG;
832 }
833 
834 HRESULT WINAPI CDesktopFolder::GetCurFolder(PIDLIST_ABSOLUTE * pidl)
835 {
836     TRACE ("(%p)->(%p)\n", this, pidl);
837 
838     if (!pidl)
839         return E_INVALIDARG; /* xp doesn't have this check and crashes on NULL */
840     *pidl = ILClone (pidlRoot);
841     return S_OK;
842 }
843 
844 HRESULT WINAPI CDesktopFolder::CallBack(IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
845 {
846     if (uMsg != DFM_MERGECONTEXTMENU && uMsg != DFM_INVOKECOMMAND)
847         return S_OK;
848 
849     /* no data object means no selection */
850     if (!pdtobj)
851     {
852         if (uMsg == DFM_INVOKECOMMAND && wParam == 0)
853         {
854             if (32 >= (UINT_PTR)ShellExecuteW(hwndOwner, L"open", L"rundll32.exe",
855                                               L"shell32.dll,Control_RunDLL desk.cpl", NULL, SW_SHOWNORMAL))
856             {
857                 return E_FAIL;
858             }
859             return S_OK;
860         }
861         else if (uMsg == DFM_MERGECONTEXTMENU)
862         {
863             QCMINFO *pqcminfo = (QCMINFO *)lParam;
864             HMENU hpopup = CreatePopupMenu();
865             _InsertMenuItemW(hpopup, 0, TRUE, 0, MFT_STRING, MAKEINTRESOURCEW(IDS_PROPERTIES), MFS_ENABLED);
866             Shell_MergeMenus(pqcminfo->hmenu, hpopup, pqcminfo->indexMenu, pqcminfo->idCmdFirst++, pqcminfo->idCmdLast, MM_ADDSEPARATOR);
867             DestroyMenu(hpopup);
868         }
869 
870         return S_OK;
871     }
872 
873     if (uMsg != DFM_INVOKECOMMAND || wParam != DFM_CMD_PROPERTIES)
874         return S_OK;
875 
876     return Shell_DefaultContextMenuCallBack(this, pdtobj);
877 }
878 
879 /*************************************************************************
880  * SHGetDesktopFolder            [SHELL32.@]
881  */
882 HRESULT WINAPI SHGetDesktopFolder(IShellFolder **psf)
883 {
884     HRESULT    hres = S_OK;
885     TRACE("\n");
886 
887     if(!psf) return E_INVALIDARG;
888     *psf = NULL;
889     hres = CDesktopFolder::_CreatorClass::CreateInstance(NULL, IID_PPV_ARG(IShellFolder, psf));
890 
891     TRACE("-- %p->(%p)\n",psf, *psf);
892     return hres;
893 }
894