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