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