Reactos
1/*
2 * Shell Folder stuff
3 *
4 * Copyright 1997 Marcus Meissner
5 * Copyright 1998, 1999, 2002 Juergen Schmied
6 * Copyright 2018 Katayama Hirofumi MZ
7 *
8 * IShellFolder2 and related interfaces
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25#include "precomp.h"
26
27WINE_DEFAULT_DEBUG_CHANNEL(shell);
28
29SHCONTF SHELL_GetDefaultFolderEnumSHCONTF()
30{
31 SHCONTF Flags = SHCONTF_FOLDERS | SHCONTF_NONFOLDERS;
32 SHELLSTATE ss;
33 SHGetSetSettings(&ss, SSF_SHOWALLOBJECTS | SSF_SHOWSUPERHIDDEN, FALSE);
34 if (ss.fShowAllObjects)
35 Flags |= SHCONTF_INCLUDEHIDDEN;
36 if (ss.fShowSuperHidden)
37 Flags |= SHCONTF_INCLUDESUPERHIDDEN;
38 return Flags;
39}
40
41BOOL SHELL_IncludeItemInFolderEnum(IShellFolder *pSF, PCUITEMID_CHILD pidl, SFGAOF Query, SHCONTF Flags)
42{
43 if (SUCCEEDED(pSF->GetAttributesOf(1, &pidl, &Query)))
44 {
45 if (Query & SFGAO_NONENUMERATED)
46 return FALSE;
47 if ((Query & SFGAO_HIDDEN) && !(Flags & SHCONTF_INCLUDEHIDDEN))
48 return FALSE;
49 if ((Query & (SFGAO_HIDDEN | SFGAO_SYSTEM)) == (SFGAO_HIDDEN | SFGAO_SYSTEM) && !(Flags & SHCONTF_INCLUDESUPERHIDDEN))
50 return FALSE;
51 if ((Flags & (SHCONTF_FOLDERS | SHCONTF_NONFOLDERS)) != (SHCONTF_FOLDERS | SHCONTF_NONFOLDERS))
52 return (Flags & SHCONTF_FOLDERS) ? (Query & SFGAO_FOLDER) : !(Query & SFGAO_FOLDER);
53 }
54 return TRUE;
55}
56
57static HRESULT
58MapSCIDToShell32FsColumn(const SHCOLUMNID *pscid, VARTYPE &vt)
59{
60 if (pscid->fmtid == FMTID_Storage)
61 {
62 switch (pscid->pid)
63 {
64 case PID_STG_NAME: vt = VT_BSTR; return SHFSF_COL_NAME;
65 case PID_STG_SIZE: vt = VT_UI8; return SHFSF_COL_SIZE;
66 case PID_STG_STORAGETYPE: vt = VT_BSTR; return SHFSF_COL_TYPE;
67 case PID_STG_ATTRIBUTES: vt = VT_BSTR; return SHFSF_COL_FATTS;
68 case PID_STG_WRITETIME: vt = VT_DATE; return SHFSF_COL_MDATE;
69 }
70 }
71 if (pscid->fmtid == FMTID_SummaryInformation && pscid->pid == PIDSI_COMMENTS)
72 {
73 vt = VT_BSTR;
74 return SHFSF_COL_COMMENT;
75 }
76 return E_INVALIDARG;
77}
78
79HRESULT
80SHELL_MapSCIDToColumn(IShellFolder2 *pSF, const SHCOLUMNID *pscid)
81{
82 for (UINT i = 0; i <= SHCIDS_COLUMNMASK; ++i)
83 {
84 SHCOLUMNID scid;
85 HRESULT hr = pSF->MapColumnToSCID(i, &scid);
86 if (FAILED(hr))
87 break;
88 if (IsEqualPropertyKey(scid, *pscid))
89 return i;
90 }
91 return E_FAIL;
92}
93
94HRESULT
95SHELL_GetDetailsOfAsStringVariant(IShellFolder2 *pSF, PCUITEMID_CHILD pidl, UINT Column, VARIANT *pVar)
96{
97 V_VT(pVar) = VT_EMPTY;
98 SHELLDETAILS sd;
99 sd.str.uType = STRRET_CSTR;
100 HRESULT hr = pSF->GetDetailsOf(pidl, Column, &sd);
101 if (FAILED(hr))
102 return hr;
103 if (FAILED(hr = StrRetToBSTR(&sd.str, pidl, &V_BSTR(pVar))))
104 return hr;
105 V_VT(pVar) = VT_BSTR;
106 return hr;
107}
108
109HRESULT
110SHELL_GetDetailsOfColumnAsVariant(IShellFolder2 *pSF, PCUITEMID_CHILD pidl, UINT Column, VARTYPE vt, VARIANT *pVar)
111{
112 HRESULT hr = SHELL_GetDetailsOfAsStringVariant(pSF, pidl, Column, pVar);
113 if (FAILED(hr))
114 return hr;
115 if (vt == VT_EMPTY)
116 {
117 SHCOLSTATEF state;
118 if (FAILED(pSF->GetDefaultColumnState(Column, &state)))
119 state = SHCOLSTATE_TYPE_STR;
120 if ((state & SHCOLSTATE_TYPEMASK) == SHCOLSTATE_TYPE_INT)
121 vt = VT_I8;
122 else if ((state & SHCOLSTATE_TYPEMASK) == SHCOLSTATE_TYPE_DATE)
123 vt = VT_DATE;
124 else
125 vt = VT_BSTR;
126 }
127 if (vt != VT_BSTR)
128 VariantChangeType(pVar, pVar, 0, vt);
129 return hr;
130}
131
132HRESULT
133SH32_GetDetailsOfPKeyAsVariant(IShellFolder2 *pSF, PCUITEMID_CHILD pidl, const SHCOLUMNID *pscid, VARIANT *pVar, BOOL UseFsColMap)
134{
135 // CFSFolder and CRegFolder uses the SHFSF_COL columns and can use the faster and better version,
136 // everyone else must ask the folder to map the SCID to a column.
137 VARTYPE vt = VT_EMPTY;
138 HRESULT hr = UseFsColMap ? MapSCIDToShell32FsColumn(pscid, vt) : SHELL_MapSCIDToColumn(pSF, pscid);
139 return SUCCEEDED(hr) ? SHELL_GetDetailsOfColumnAsVariant(pSF, pidl, hr, vt, pVar) : hr;
140}
141
142HRESULT SHELL_CreateAbsolutePidl(IShellFolder *pSF, PCUIDLIST_RELATIVE pidlChild, PIDLIST_ABSOLUTE *ppPidl)
143{
144 PIDLIST_ABSOLUTE pidlFolder;
145 HRESULT hr = SHGetIDListFromObject(pSF, &pidlFolder);
146 if (SUCCEEDED(hr))
147 {
148 hr = SHILCombine(pidlFolder, pidlChild, ppPidl);
149 ILFree(pidlFolder);
150 }
151 return hr;
152}
153
154HRESULT
155Shell_NextElement(
156 _Inout_ LPWSTR *ppch,
157 _Out_ LPWSTR pszOut,
158 _In_ INT cchOut,
159 _In_ BOOL bValidate)
160{
161 *pszOut = UNICODE_NULL;
162
163 if (!*ppch)
164 return S_FALSE;
165
166 HRESULT hr;
167 LPWSTR pchNext = wcschr(*ppch, L'\\');
168 if (pchNext)
169 {
170 if (*ppch < pchNext)
171 {
172 /* Get an element */
173 StringCchCopyNW(pszOut, cchOut, *ppch, pchNext - *ppch);
174 ++pchNext;
175
176 if (!*pchNext)
177 pchNext = NULL; /* No next */
178
179 hr = S_OK;
180 }
181 else /* Double backslashes found? */
182 {
183 pchNext = NULL;
184 hr = E_INVALIDARG;
185 }
186 }
187 else /* No more next */
188 {
189 StringCchCopyW(pszOut, cchOut, *ppch);
190 hr = S_OK;
191 }
192
193 *ppch = pchNext; /* Go next */
194
195 if (hr == S_OK && bValidate && !PathIsValidElement(pszOut))
196 {
197 *pszOut = UNICODE_NULL;
198 hr = E_INVALIDARG;
199 }
200
201 return hr;
202}
203
204HRESULT SHELL32_ParseNextElement (IShellFolder2 * psf, HWND hwndOwner, LPBC pbc,
205 LPITEMIDLIST * pidlInOut, LPOLESTR szNext, DWORD * pEaten, DWORD * pdwAttributes)
206{
207 HRESULT hr = E_INVALIDARG;
208 LPITEMIDLIST pidlIn = pidlInOut ? *pidlInOut : NULL;
209 LPITEMIDLIST pidlOut = NULL;
210 LPITEMIDLIST pidlTemp = NULL;
211 CComPtr<IShellFolder> psfChild;
212
213 TRACE ("(%p, %p, %p, %s)\n", psf, pbc, pidlIn, debugstr_w (szNext));
214
215 /* get the shellfolder for the child pidl and let it analyse further */
216 hr = psf->BindToObject(pidlIn, pbc, IID_PPV_ARG(IShellFolder, &psfChild));
217 if (FAILED(hr))
218 return hr;
219
220 hr = psfChild->ParseDisplayName(hwndOwner, pbc, szNext, pEaten, &pidlOut, pdwAttributes);
221 if (FAILED(hr))
222 return hr;
223
224 pidlTemp = ILCombine (pidlIn, pidlOut);
225 if (!pidlTemp)
226 {
227 hr = E_OUTOFMEMORY;
228 if (pidlOut)
229 ILFree(pidlOut);
230 return hr;
231 }
232
233 if (pidlOut)
234 ILFree (pidlOut);
235
236 if (pidlIn)
237 ILFree (pidlIn);
238
239 *pidlInOut = pidlTemp;
240
241 TRACE ("-- pidl=%p ret=0x%08x\n", pidlInOut ? *pidlInOut : NULL, hr);
242 return S_OK;
243}
244
245/***********************************************************************
246 * SHELL32_CoCreateInitSF
247 *
248 * Creates a shell folder and initializes it with a pidl and a root folder
249 * via IPersistFolder3 or IPersistFolder.
250 *
251 * NOTES
252 * pathRoot can be NULL for Folders being a drive.
253 * In this case the absolute path is built from pidlChild (eg. C:)
254 */
255HRESULT SHELL32_CoCreateInitSF (LPCITEMIDLIST pidlRoot, PERSIST_FOLDER_TARGET_INFO* ppfti,
256 LPCITEMIDLIST pidlChild, const GUID* clsid, REFIID riid, LPVOID *ppvOut)
257{
258 HRESULT hr;
259 CComPtr<IShellFolder> pShellFolder;
260
261 hr = SHCoCreateInstance(NULL, clsid, NULL, IID_PPV_ARG(IShellFolder, &pShellFolder));
262 if (FAILED(hr))
263 return hr;
264
265 LPITEMIDLIST pidlAbsolute = ILCombine (pidlRoot, pidlChild);
266 CComPtr<IPersistFolder> ppf;
267 CComPtr<IPersistFolder3> ppf3;
268
269 if (ppfti && SUCCEEDED(pShellFolder->QueryInterface(IID_PPV_ARG(IPersistFolder3, &ppf3))))
270 {
271 ppf3->InitializeEx(NULL, pidlAbsolute, ppfti);
272 }
273 else if (SUCCEEDED(pShellFolder->QueryInterface(IID_PPV_ARG(IPersistFolder, &ppf))))
274 {
275 ppf->Initialize(pidlAbsolute);
276 }
277 ILFree (pidlAbsolute);
278
279 return pShellFolder->QueryInterface(riid, ppvOut);
280}
281
282HRESULT SHELL32_CoCreateInitSF (LPCITEMIDLIST pidlRoot, const GUID* clsid,
283 int csidl, REFIID riid, LPVOID *ppvOut)
284{
285 /* fill the PERSIST_FOLDER_TARGET_INFO */
286 PERSIST_FOLDER_TARGET_INFO pfti = {0};
287 pfti.dwAttributes = -1;
288 pfti.csidl = csidl;
289
290 return SHELL32_CoCreateInitSF(pidlRoot, &pfti, NULL, clsid, riid, ppvOut);
291}
292
293HRESULT SHELL32_BindToSF (LPCITEMIDLIST pidlRoot, PERSIST_FOLDER_TARGET_INFO* ppfti,
294 LPCITEMIDLIST pidl, const GUID* clsid, REFIID riid, LPVOID *ppvOut)
295{
296 PITEMID_CHILD pidlChild = ILCloneFirst (pidl);
297 if (!pidlChild)
298 return E_FAIL;
299
300 CComPtr<IShellFolder> psf;
301 HRESULT hr = SHELL32_CoCreateInitSF(pidlRoot,
302 ppfti,
303 pidlChild,
304 clsid,
305 IID_PPV_ARG(IShellFolder, &psf));
306 ILFree(pidlChild);
307
308 if (FAILED_UNEXPECTEDLY(hr))
309 return hr;
310
311 if (_ILIsPidlSimple (pidl))
312 return psf->QueryInterface(riid, ppvOut);
313 else
314 return psf->BindToObject(ILGetNext (pidl), NULL, riid, ppvOut);
315}
316
317/***********************************************************************
318 * SHELL32_GetDisplayNameOfChild
319 *
320 * Retrieves the display name of a child object of a shellfolder.
321 *
322 * For a pidl eg. [subpidl1][subpidl2][subpidl3]:
323 * - it binds to the child shellfolder [subpidl1]
324 * - asks it for the displayname of [subpidl2][subpidl3]
325 *
326 * Is possible the pidl is a simple pidl. In this case it asks the
327 * subfolder for the displayname of an empty pidl. The subfolder
328 * returns the own displayname eg. "::{guid}". This is used for
329 * virtual folders with the registry key WantsFORPARSING set.
330 */
331HRESULT SHELL32_GetDisplayNameOfChild (IShellFolder2 * psf,
332 LPCITEMIDLIST pidl, DWORD dwFlags, LPSTRRET strRet)
333{
334 LPITEMIDLIST pidlFirst = ILCloneFirst(pidl);
335 if (!pidlFirst)
336 return E_OUTOFMEMORY;
337
338 CComPtr<IShellFolder> psfChild;
339 HRESULT hr = psf->BindToObject(pidlFirst, NULL, IID_PPV_ARG(IShellFolder, &psfChild));
340 if (SUCCEEDED (hr))
341 {
342 hr = psfChild->GetDisplayNameOf(ILGetNext (pidl), dwFlags, strRet);
343 }
344 ILFree (pidlFirst);
345
346 return hr;
347}
348
349HRESULT SHELL32_CompareChildren(IShellFolder2* psf, LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
350{
351 PUIDLIST_RELATIVE nextpidl1 = ILGetNext (pidl1);
352 PUIDLIST_RELATIVE nextpidl2 = ILGetNext (pidl2);
353
354 bool isEmpty1 = _ILIsDesktop(nextpidl1);
355 bool isEmpty2 = _ILIsDesktop(nextpidl2);
356 if (isEmpty1 || isEmpty2)
357 return MAKE_COMPARE_HRESULT(isEmpty2 - isEmpty1);
358
359 PITEMID_CHILD firstpidl = ILCloneFirst (pidl1);
360 if (!firstpidl)
361 return E_OUTOFMEMORY;
362
363 CComPtr<IShellFolder> psf2;
364 HRESULT hr = psf->BindToObject(firstpidl, 0, IID_PPV_ARG(IShellFolder, &psf2));
365 ILFree(firstpidl);
366 if (FAILED(hr))
367 return MAKE_COMPARE_HRESULT(0);
368
369 return psf2->CompareIDs(lParam, nextpidl1, nextpidl2);
370}
371
372HRESULT SHELL32_CompareDetails(IShellFolder2* isf, LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
373{
374 SHELLDETAILS sd;
375 WCHAR wszItem1[MAX_PATH], wszItem2[MAX_PATH];
376 HRESULT hres;
377 UINT col = LOWORD(lParam); // Column index without SHCIDS_* flags
378
379 hres = isf->GetDetailsOf(pidl1, col, &sd);
380 if (FAILED(hres))
381 return MAKE_COMPARE_HRESULT(1);
382
383 hres = StrRetToBufW(&sd.str, pidl1, wszItem1, MAX_PATH);
384 if (FAILED(hres))
385 return MAKE_COMPARE_HRESULT(1);
386
387 hres = isf->GetDetailsOf(pidl2, col, &sd);
388 if (FAILED(hres))
389 return MAKE_COMPARE_HRESULT(1);
390
391 hres = StrRetToBufW(&sd.str, pidl2, wszItem2, MAX_PATH);
392 if (FAILED(hres))
393 return MAKE_COMPARE_HRESULT(1);
394
395 int ret = CFSFolder::CompareUiStrings(wszItem1, wszItem2, lParam);
396 if (ret == 0)
397 return SHELL32_CompareChildren(isf, lParam, pidl1, pidl2);
398
399 return MAKE_COMPARE_HRESULT(ret);
400}
401
402void CloseRegKeyArray(HKEY* array, UINT cKeys)
403{
404 for (UINT i = 0; i < cKeys; ++i)
405 RegCloseKey(array[i]);
406}
407
408LSTATUS AddClassKeyToArray(const WCHAR* szClass, HKEY* array, UINT* cKeys)
409{
410 if (*cKeys >= 16)
411 return ERROR_MORE_DATA;
412
413 HKEY hkey;
414 LSTATUS result = RegOpenKeyExW(HKEY_CLASSES_ROOT, szClass, 0, KEY_READ | KEY_QUERY_VALUE, &hkey);
415 if (result == ERROR_SUCCESS)
416 {
417 array[*cKeys] = hkey;
418 *cKeys += 1;
419 }
420 return result;
421}
422
423LSTATUS AddClsidKeyToArray(REFCLSID clsid, HKEY* array, UINT* cKeys)
424{
425 WCHAR path[6 + 38 + 1] = L"CLSID\\";
426 StringFromGUID2(clsid, path + 6, 38 + 1);
427 return AddClassKeyToArray(path, array, cKeys);
428}
429
430void AddFSClassKeysToArray(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, HKEY* array, UINT* cKeys)
431{
432 // This function opens the association array keys in canonical order for filesystem items.
433 // The order is documented: learn.microsoft.com/en-us/windows/win32/shell/fa-associationarray
434
435 ASSERT(cidl >= 1 && apidl);
436 PCUITEMID_CHILD pidl = apidl[0];
437 if (_ILIsValue(pidl))
438 {
439 WCHAR buf[MAX_PATH];
440 PWSTR name;
441 FileStructW* pFileData = _ILGetFileStructW(pidl);
442 if (pFileData)
443 {
444 name = pFileData->wszName;
445 }
446 else
447 {
448 _ILSimpleGetTextW(pidl, buf, _countof(buf));
449 name = buf;
450 }
451 LPCWSTR extension = PathFindExtension(name);
452
453 if (extension)
454 {
455 WCHAR wszClass[MAX_PATH], wszSFA[23 + _countof(wszClass)];
456 DWORD dwSize = sizeof(wszClass);
457 if (RegGetValueW(HKEY_CLASSES_ROOT, extension, NULL, RRF_RT_REG_SZ, NULL, wszClass, &dwSize) != ERROR_SUCCESS ||
458 !*wszClass || AddClassKeyToArray(wszClass, array, cKeys) != ERROR_SUCCESS)
459 {
460 // Only add the extension key if the ProgId is not valid
461 AddClassKeyToArray(extension, array, cKeys);
462
463 // "Open With" becomes the default when there are no verbs in the above keys
464 if (cidl == 1)
465 AddClassKeyToArray(L"Unknown", array, cKeys);
466 }
467
468 swprintf(wszSFA, L"SystemFileAssociations\\%s", extension);
469 AddClassKeyToArray(wszSFA, array, cKeys);
470
471 dwSize = sizeof(wszClass);
472 if (RegGetValueW(HKEY_CLASSES_ROOT, extension, L"PerceivedType", RRF_RT_REG_SZ, NULL, wszClass, &dwSize) == ERROR_SUCCESS)
473 {
474 swprintf(wszSFA, L"SystemFileAssociations\\%s", wszClass);
475 AddClassKeyToArray(wszSFA, array, cKeys);
476 }
477 }
478
479 AddClassKeyToArray(L"*", array, cKeys);
480 AddClassKeyToArray(L"AllFilesystemObjects", array, cKeys);
481 }
482 else if (_ILIsFolder(pidl))
483 {
484 // FIXME: Directory > Folder > AFO is the correct order and it's
485 // the order Windows reports in its undocumented association array
486 // but it is somehow not the order Windows adds the items to its menu!
487 // Until the correct algorithm in CDefaultContextMenu can be determined,
488 // we add the folder keys in "menu order".
489 AddClassKeyToArray(L"Folder", array, cKeys);
490 AddClassKeyToArray(L"AllFilesystemObjects", array, cKeys);
491 AddClassKeyToArray(L"Directory", array, cKeys);
492 }
493 else if (_ILIsDrive(pidl))
494 {
495 AddClassKeyToArray(L"Drive", array, cKeys);
496 AddClassKeyToArray(L"Folder", array, cKeys);
497 }
498 else
499 {
500 ERR("Got non FS pidl\n");
501 }
502}
503
504void AddPidlClassKeysToArray(LPCITEMIDLIST pidl, HKEY* array, UINT* cKeys)
505{
506 if ((pidl = ILFindLastID(pidl)) != NULL)
507 AddFSClassKeysToArray(1, &pidl, array, cKeys);
508}
509
510HRESULT SH_GetApidlFromDataObject(IDataObject *pDataObject, PIDLIST_ABSOLUTE* ppidlfolder, PUITEMID_CHILD **apidlItems, UINT *pcidl)
511{
512 CDataObjectHIDA cida(pDataObject);
513
514 if (FAILED_UNEXPECTEDLY(cida.hr()))
515 return cida.hr();
516
517 /* convert the data into pidl */
518 LPITEMIDLIST pidl;
519 LPITEMIDLIST *apidl = _ILCopyCidaToaPidl(&pidl, cida);
520 if (!apidl)
521 {
522 return E_OUTOFMEMORY;
523 }
524
525 *ppidlfolder = pidl;
526 *apidlItems = apidl;
527 *pcidl = cida->cidl;
528
529 return S_OK;
530}
531
532/***********************************************************************
533 * SHCreateLinks
534 *
535 * Undocumented.
536 */
537HRESULT WINAPI SHCreateLinks( HWND hWnd, LPCSTR lpszDir, IDataObject * lpDataObject,
538 UINT uFlags, LPITEMIDLIST *lppidlLinks)
539{
540 FIXME("%p %s %p %08x %p\n", hWnd, lpszDir, lpDataObject, uFlags, lppidlLinks);
541 return E_NOTIMPL;
542}
543
544/***********************************************************************
545 * SHOpenFolderAndSelectItems
546 *
547 * Unimplemented.
548 */
549EXTERN_C HRESULT
550WINAPI
551SHOpenFolderAndSelectItems(PCIDLIST_ABSOLUTE pidlFolder,
552 UINT cidl,
553 PCUITEMID_CHILD_ARRAY apidl,
554 DWORD dwFlags)
555{
556 ERR("SHOpenFolderAndSelectItems() is hackplemented\n");
557 CComHeapPtr<ITEMIDLIST> freeItem;
558 PCIDLIST_ABSOLUTE pidlItem;
559 if (cidl)
560 {
561 /* Firefox sends a full pidl here dispite the fact it is a PCUITEMID_CHILD_ARRAY -_- */
562 if (!ILIsSingle(apidl[0]))
563 {
564 pidlItem = apidl[0];
565 }
566 else
567 {
568 HRESULT hr = SHILCombine(pidlFolder, apidl[0], &pidlItem);
569 if (FAILED_UNEXPECTEDLY(hr))
570 return hr;
571 freeItem.Attach(const_cast<PIDLIST_ABSOLUTE>(pidlItem));
572 }
573 }
574 else
575 {
576 pidlItem = pidlFolder;
577 }
578
579 CComPtr<IShellFolder> psfDesktop;
580
581 HRESULT hr = SHGetDesktopFolder(&psfDesktop);
582 if (FAILED_UNEXPECTEDLY(hr))
583 return hr;
584
585 STRRET strret;
586 hr = psfDesktop->GetDisplayNameOf(pidlItem, SHGDN_FORPARSING, &strret);
587 if (FAILED_UNEXPECTEDLY(hr))
588 return hr;
589
590 WCHAR wszBuf[MAX_PATH];
591 hr = StrRetToBufW(&strret, pidlItem, wszBuf, _countof(wszBuf));
592 if (FAILED_UNEXPECTEDLY(hr))
593 return hr;
594
595 WCHAR wszParams[MAX_PATH];
596 wcscpy(wszParams, L"/select,");
597 wcscat(wszParams, wszBuf);
598
599 SHELLEXECUTEINFOW sei;
600 memset(&sei, 0, sizeof sei);
601 sei.cbSize = sizeof sei;
602 sei.fMask = SEE_MASK_WAITFORINPUTIDLE;
603 sei.lpFile = L"explorer.exe";
604 sei.lpParameters = wszParams;
605
606 if (ShellExecuteExW(&sei))
607 return S_OK;
608 else
609 return E_FAIL;
610}
611
612/*
613 * for internal use
614 */
615HRESULT
616SHELL32_ShowPropertiesDialog(IDataObject *pdtobj)
617{
618 if (!pdtobj)
619 return E_INVALIDARG;
620 return SHELL32_ShowFilesystemItemsPropertiesDialogAsync(NULL, pdtobj);
621}
622
623HRESULT
624SHELL32_DefaultContextMenuCallBack(IShellFolder *psf, IDataObject *pdo, UINT msg)
625{
626 switch (msg)
627 {
628 case DFM_MERGECONTEXTMENU:
629 return S_OK; // Yes, I want verbs
630 case DFM_INVOKECOMMAND:
631 return S_FALSE; // Do it for me please
632 case DFM_GETDEFSTATICID:
633 return S_FALSE; // Supposedly "required for Windows 7 to pick a default"
634 }
635 return E_NOTIMPL;
636}