xref: /reactos/dll/win32/shell32/shlfolder.cpp (revision 2aadf2eb)
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 
27 WINE_DEFAULT_DEBUG_CHANNEL(shell);
28 
SHELL_GetDefaultFolderEnumSHCONTF()29 SHCONTF 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 
SHELL_IncludeItemInFolderEnum(IShellFolder * pSF,PCUITEMID_CHILD pidl,SFGAOF Query,SHCONTF Flags)41 BOOL 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 
57 HRESULT
Shell_NextElement(_Inout_ LPWSTR * ppch,_Out_ LPWSTR pszOut,_In_ INT cchOut,_In_ BOOL bValidate)58 Shell_NextElement(
59     _Inout_ LPWSTR *ppch,
60     _Out_ LPWSTR pszOut,
61     _In_ INT cchOut,
62     _In_ BOOL bValidate)
63 {
64     *pszOut = UNICODE_NULL;
65 
66     if (!*ppch)
67         return S_FALSE;
68 
69     HRESULT hr;
70     LPWSTR pchNext = wcschr(*ppch, L'\\');
71     if (pchNext)
72     {
73         if (*ppch < pchNext)
74         {
75             /* Get an element */
76             StringCchCopyNW(pszOut, cchOut, *ppch, pchNext - *ppch);
77             ++pchNext;
78 
79             if (!*pchNext)
80                 pchNext = NULL; /* No next */
81 
82             hr = S_OK;
83         }
84         else /* Double backslashes found? */
85         {
86             pchNext = NULL;
87             hr = E_INVALIDARG;
88         }
89     }
90     else /* No more next */
91     {
92         StringCchCopyW(pszOut, cchOut, *ppch);
93         hr = S_OK;
94     }
95 
96     *ppch = pchNext; /* Go next */
97 
98     if (hr == S_OK && bValidate && !PathIsValidElement(pszOut))
99     {
100         *pszOut = UNICODE_NULL;
101         hr = E_INVALIDARG;
102     }
103 
104     return hr;
105 }
106 
SHELL32_ParseNextElement(IShellFolder2 * psf,HWND hwndOwner,LPBC pbc,LPITEMIDLIST * pidlInOut,LPOLESTR szNext,DWORD * pEaten,DWORD * pdwAttributes)107 HRESULT SHELL32_ParseNextElement (IShellFolder2 * psf, HWND hwndOwner, LPBC pbc,
108                   LPITEMIDLIST * pidlInOut, LPOLESTR szNext, DWORD * pEaten, DWORD * pdwAttributes)
109 {
110     HRESULT hr = E_INVALIDARG;
111     LPITEMIDLIST pidlIn = pidlInOut ? *pidlInOut : NULL;
112     LPITEMIDLIST pidlOut = NULL;
113     LPITEMIDLIST pidlTemp = NULL;
114     CComPtr<IShellFolder> psfChild;
115 
116     TRACE ("(%p, %p, %p, %s)\n", psf, pbc, pidlIn, debugstr_w (szNext));
117 
118     /* get the shellfolder for the child pidl and let it analyse further */
119     hr = psf->BindToObject(pidlIn, pbc, IID_PPV_ARG(IShellFolder, &psfChild));
120     if (FAILED(hr))
121         return hr;
122 
123     hr = psfChild->ParseDisplayName(hwndOwner, pbc, szNext, pEaten, &pidlOut, pdwAttributes);
124     if (FAILED(hr))
125         return hr;
126 
127     pidlTemp = ILCombine (pidlIn, pidlOut);
128     if (!pidlTemp)
129     {
130         hr = E_OUTOFMEMORY;
131         if (pidlOut)
132             ILFree(pidlOut);
133         return hr;
134     }
135 
136     if (pidlOut)
137         ILFree (pidlOut);
138 
139     if (pidlIn)
140         ILFree (pidlIn);
141 
142     *pidlInOut = pidlTemp;
143 
144     TRACE ("-- pidl=%p ret=0x%08x\n", pidlInOut ? *pidlInOut : NULL, hr);
145     return S_OK;
146 }
147 
148 /***********************************************************************
149  *    SHELL32_CoCreateInitSF
150  *
151  * Creates a shell folder and initializes it with a pidl and a root folder
152  * via IPersistFolder3 or IPersistFolder.
153  *
154  * NOTES
155  *   pathRoot can be NULL for Folders being a drive.
156  *   In this case the absolute path is built from pidlChild (eg. C:)
157  */
SHELL32_CoCreateInitSF(LPCITEMIDLIST pidlRoot,PERSIST_FOLDER_TARGET_INFO * ppfti,LPCITEMIDLIST pidlChild,const GUID * clsid,REFIID riid,LPVOID * ppvOut)158 HRESULT SHELL32_CoCreateInitSF (LPCITEMIDLIST pidlRoot, PERSIST_FOLDER_TARGET_INFO* ppfti,
159                 LPCITEMIDLIST pidlChild, const GUID* clsid, REFIID riid, LPVOID *ppvOut)
160 {
161     HRESULT hr;
162     CComPtr<IShellFolder> pShellFolder;
163 
164     hr = SHCoCreateInstance(NULL, clsid, NULL, IID_PPV_ARG(IShellFolder, &pShellFolder));
165     if (FAILED(hr))
166         return hr;
167 
168     LPITEMIDLIST pidlAbsolute = ILCombine (pidlRoot, pidlChild);
169     CComPtr<IPersistFolder> ppf;
170     CComPtr<IPersistFolder3> ppf3;
171 
172     if (ppfti && SUCCEEDED(pShellFolder->QueryInterface(IID_PPV_ARG(IPersistFolder3, &ppf3))))
173     {
174         ppf3->InitializeEx(NULL, pidlAbsolute, ppfti);
175     }
176     else if (SUCCEEDED(pShellFolder->QueryInterface(IID_PPV_ARG(IPersistFolder, &ppf))))
177     {
178         ppf->Initialize(pidlAbsolute);
179     }
180     ILFree (pidlAbsolute);
181 
182     return pShellFolder->QueryInterface(riid, ppvOut);
183 }
184 
SHELL32_CoCreateInitSF(LPCITEMIDLIST pidlRoot,const GUID * clsid,int csidl,REFIID riid,LPVOID * ppvOut)185 HRESULT SHELL32_CoCreateInitSF (LPCITEMIDLIST pidlRoot, const GUID* clsid,
186                                 int csidl, REFIID riid, LPVOID *ppvOut)
187 {
188     /* fill the PERSIST_FOLDER_TARGET_INFO */
189     PERSIST_FOLDER_TARGET_INFO pfti = {0};
190     pfti.dwAttributes = -1;
191     pfti.csidl = csidl;
192 
193     return SHELL32_CoCreateInitSF(pidlRoot, &pfti, NULL, clsid, riid, ppvOut);
194 }
195 
SHELL32_BindToSF(LPCITEMIDLIST pidlRoot,PERSIST_FOLDER_TARGET_INFO * ppfti,LPCITEMIDLIST pidl,const GUID * clsid,REFIID riid,LPVOID * ppvOut)196 HRESULT SHELL32_BindToSF (LPCITEMIDLIST pidlRoot, PERSIST_FOLDER_TARGET_INFO* ppfti,
197                 LPCITEMIDLIST pidl, const GUID* clsid, REFIID riid, LPVOID *ppvOut)
198 {
199     PITEMID_CHILD pidlChild = ILCloneFirst (pidl);
200     if (!pidlChild)
201         return E_FAIL;
202 
203     CComPtr<IShellFolder> psf;
204     HRESULT hr = SHELL32_CoCreateInitSF(pidlRoot,
205                                         ppfti,
206                                         pidlChild,
207                                         clsid,
208                                         IID_PPV_ARG(IShellFolder, &psf));
209     ILFree(pidlChild);
210 
211     if (FAILED_UNEXPECTEDLY(hr))
212         return hr;
213 
214     if (_ILIsPidlSimple (pidl))
215         return psf->QueryInterface(riid, ppvOut);
216     else
217         return psf->BindToObject(ILGetNext (pidl), NULL, riid, ppvOut);
218 }
219 
220 /***********************************************************************
221  *    SHELL32_GetDisplayNameOfChild
222  *
223  * Retrieves the display name of a child object of a shellfolder.
224  *
225  * For a pidl eg. [subpidl1][subpidl2][subpidl3]:
226  * - it binds to the child shellfolder [subpidl1]
227  * - asks it for the displayname of [subpidl2][subpidl3]
228  *
229  * Is possible the pidl is a simple pidl. In this case it asks the
230  * subfolder for the displayname of an empty pidl. The subfolder
231  * returns the own displayname eg. "::{guid}". This is used for
232  * virtual folders with the registry key WantsFORPARSING set.
233  */
SHELL32_GetDisplayNameOfChild(IShellFolder2 * psf,LPCITEMIDLIST pidl,DWORD dwFlags,LPSTRRET strRet)234 HRESULT SHELL32_GetDisplayNameOfChild (IShellFolder2 * psf,
235                        LPCITEMIDLIST pidl, DWORD dwFlags, LPSTRRET strRet)
236 {
237     LPITEMIDLIST pidlFirst = ILCloneFirst(pidl);
238     if (!pidlFirst)
239         return E_OUTOFMEMORY;
240 
241     CComPtr<IShellFolder> psfChild;
242     HRESULT hr = psf->BindToObject(pidlFirst, NULL, IID_PPV_ARG(IShellFolder, &psfChild));
243     if (SUCCEEDED (hr))
244     {
245         hr = psfChild->GetDisplayNameOf(ILGetNext (pidl), dwFlags, strRet);
246     }
247     ILFree (pidlFirst);
248 
249     return hr;
250 }
251 
SHELL32_CompareChildren(IShellFolder2 * psf,LPARAM lParam,LPCITEMIDLIST pidl1,LPCITEMIDLIST pidl2)252 HRESULT SHELL32_CompareChildren(IShellFolder2* psf, LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
253 {
254     PUIDLIST_RELATIVE nextpidl1 = ILGetNext (pidl1);
255     PUIDLIST_RELATIVE nextpidl2 = ILGetNext (pidl2);
256 
257     bool isEmpty1 = _ILIsDesktop(nextpidl1);
258     bool isEmpty2 = _ILIsDesktop(nextpidl2);
259     if (isEmpty1 || isEmpty2)
260         return MAKE_COMPARE_HRESULT(isEmpty2 - isEmpty1);
261 
262     PITEMID_CHILD firstpidl = ILCloneFirst (pidl1);
263     if (!firstpidl)
264         return E_OUTOFMEMORY;
265 
266     CComPtr<IShellFolder> psf2;
267     HRESULT hr = psf->BindToObject(firstpidl, 0, IID_PPV_ARG(IShellFolder, &psf2));
268     ILFree(firstpidl);
269     if (FAILED(hr))
270         return MAKE_COMPARE_HRESULT(0);
271 
272     return psf2->CompareIDs(lParam, nextpidl1, nextpidl2);
273 }
274 
SHELL32_CompareDetails(IShellFolder2 * isf,LPARAM lParam,LPCITEMIDLIST pidl1,LPCITEMIDLIST pidl2)275 HRESULT SHELL32_CompareDetails(IShellFolder2* isf, LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
276 {
277     SHELLDETAILS sd;
278     WCHAR wszItem1[MAX_PATH], wszItem2[MAX_PATH];
279     HRESULT hres;
280     UINT col = LOWORD(lParam); // Column index without SHCIDS_* flags
281 
282     hres = isf->GetDetailsOf(pidl1, col, &sd);
283     if (FAILED(hres))
284         return MAKE_COMPARE_HRESULT(1);
285 
286     hres = StrRetToBufW(&sd.str, pidl1, wszItem1, MAX_PATH);
287     if (FAILED(hres))
288         return MAKE_COMPARE_HRESULT(1);
289 
290     hres = isf->GetDetailsOf(pidl2, col, &sd);
291     if (FAILED(hres))
292         return MAKE_COMPARE_HRESULT(1);
293 
294     hres = StrRetToBufW(&sd.str, pidl2, wszItem2, MAX_PATH);
295     if (FAILED(hres))
296         return MAKE_COMPARE_HRESULT(1);
297 
298     int ret = _wcsicmp(wszItem1, wszItem2);
299     if (ret == 0)
300         return SHELL32_CompareChildren(isf, lParam, pidl1, pidl2);
301 
302     return MAKE_COMPARE_HRESULT(ret);
303 }
304 
CloseRegKeyArray(HKEY * array,UINT cKeys)305 void CloseRegKeyArray(HKEY* array, UINT cKeys)
306 {
307     for (UINT i = 0; i < cKeys; ++i)
308         RegCloseKey(array[i]);
309 }
310 
AddClassKeyToArray(const WCHAR * szClass,HKEY * array,UINT * cKeys)311 LSTATUS AddClassKeyToArray(const WCHAR* szClass, HKEY* array, UINT* cKeys)
312 {
313     if (*cKeys >= 16)
314         return ERROR_MORE_DATA;
315 
316     HKEY hkey;
317     LSTATUS result = RegOpenKeyExW(HKEY_CLASSES_ROOT, szClass, 0, KEY_READ | KEY_QUERY_VALUE, &hkey);
318     if (result == ERROR_SUCCESS)
319     {
320         array[*cKeys] = hkey;
321         *cKeys += 1;
322     }
323     return result;
324 }
325 
AddClsidKeyToArray(REFCLSID clsid,HKEY * array,UINT * cKeys)326 LSTATUS AddClsidKeyToArray(REFCLSID clsid, HKEY* array, UINT* cKeys)
327 {
328     WCHAR path[6 + 38 + 1] = L"CLSID\\";
329     StringFromGUID2(clsid, path + 6, 38 + 1);
330     return AddClassKeyToArray(path, array, cKeys);
331 }
332 
AddFSClassKeysToArray(UINT cidl,PCUITEMID_CHILD_ARRAY apidl,HKEY * array,UINT * cKeys)333 void AddFSClassKeysToArray(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, HKEY* array, UINT* cKeys)
334 {
335     // This function opens the association array keys in canonical order for filesystem items.
336     // The order is documented: learn.microsoft.com/en-us/windows/win32/shell/fa-associationarray
337 
338     ASSERT(cidl >= 1 && apidl);
339     PCUITEMID_CHILD pidl = apidl[0];
340     if (_ILIsValue(pidl))
341     {
342         WCHAR buf[MAX_PATH];
343         PWSTR name;
344         FileStructW* pFileData = _ILGetFileStructW(pidl);
345         if (pFileData)
346         {
347             name = pFileData->wszName;
348         }
349         else
350         {
351             _ILSimpleGetTextW(pidl, buf, _countof(buf));
352             name = buf;
353         }
354         LPCWSTR extension = PathFindExtension(name);
355 
356         if (extension)
357         {
358             WCHAR wszClass[MAX_PATH], wszSFA[23 + _countof(wszClass)];
359             DWORD dwSize = sizeof(wszClass);
360             if (RegGetValueW(HKEY_CLASSES_ROOT, extension, NULL, RRF_RT_REG_SZ, NULL, wszClass, &dwSize) != ERROR_SUCCESS ||
361                 !*wszClass || AddClassKeyToArray(wszClass, array, cKeys) != ERROR_SUCCESS)
362             {
363                 // Only add the extension key if the ProgId is not valid
364                 AddClassKeyToArray(extension, array, cKeys);
365 
366                 // "Open With" becomes the default when there are no verbs in the above keys
367                 if (cidl == 1)
368                     AddClassKeyToArray(L"Unknown", array, cKeys);
369             }
370 
371             swprintf(wszSFA, L"SystemFileAssociations\\%s", extension);
372             AddClassKeyToArray(wszSFA, array, cKeys);
373 
374             dwSize = sizeof(wszClass);
375             if (RegGetValueW(HKEY_CLASSES_ROOT, extension, L"PerceivedType ", RRF_RT_REG_SZ, NULL, wszClass, &dwSize) == ERROR_SUCCESS)
376             {
377                 swprintf(wszSFA, L"SystemFileAssociations\\%s", wszClass);
378                 AddClassKeyToArray(wszSFA, array, cKeys);
379             }
380         }
381 
382         AddClassKeyToArray(L"*", array, cKeys);
383         AddClassKeyToArray(L"AllFilesystemObjects", array, cKeys);
384     }
385     else if (_ILIsFolder(pidl))
386     {
387         // FIXME: Directory > Folder > AFO is the correct order and it's
388         // the order Windows reports in its undocumented association array
389         // but it is somehow not the order Windows adds the items to its menu!
390         // Until the correct algorithm in CDefaultContextMenu can be determined,
391         // we add the folder keys in "menu order".
392         AddClassKeyToArray(L"Folder", array, cKeys);
393         AddClassKeyToArray(L"AllFilesystemObjects", array, cKeys);
394         AddClassKeyToArray(L"Directory", array, cKeys);
395     }
396     else
397     {
398         ERR("Got non FS pidl\n");
399     }
400 }
401 
SH_GetApidlFromDataObject(IDataObject * pDataObject,PIDLIST_ABSOLUTE * ppidlfolder,PUITEMID_CHILD ** apidlItems,UINT * pcidl)402 HRESULT SH_GetApidlFromDataObject(IDataObject *pDataObject, PIDLIST_ABSOLUTE* ppidlfolder, PUITEMID_CHILD **apidlItems, UINT *pcidl)
403 {
404     CDataObjectHIDA cida(pDataObject);
405 
406     if (FAILED_UNEXPECTEDLY(cida.hr()))
407         return cida.hr();
408 
409     /* convert the data into pidl */
410     LPITEMIDLIST pidl;
411     LPITEMIDLIST *apidl = _ILCopyCidaToaPidl(&pidl, cida);
412     if (!apidl)
413     {
414         return E_OUTOFMEMORY;
415     }
416 
417     *ppidlfolder = pidl;
418     *apidlItems = apidl;
419     *pcidl = cida->cidl;
420 
421     return S_OK;
422 }
423 
424 /***********************************************************************
425  *  SHCreateLinks
426  *
427  *   Undocumented.
428  */
SHCreateLinks(HWND hWnd,LPCSTR lpszDir,IDataObject * lpDataObject,UINT uFlags,LPITEMIDLIST * lppidlLinks)429 HRESULT WINAPI SHCreateLinks( HWND hWnd, LPCSTR lpszDir, IDataObject * lpDataObject,
430                               UINT uFlags, LPITEMIDLIST *lppidlLinks)
431 {
432     FIXME("%p %s %p %08x %p\n", hWnd, lpszDir, lpDataObject, uFlags, lppidlLinks);
433     return E_NOTIMPL;
434 }
435 
436 /***********************************************************************
437  *  SHOpenFolderAndSelectItems
438  *
439  *   Unimplemented.
440  */
441 EXTERN_C HRESULT
442 WINAPI
SHOpenFolderAndSelectItems(PCIDLIST_ABSOLUTE pidlFolder,UINT cidl,PCUITEMID_CHILD_ARRAY apidl,DWORD dwFlags)443 SHOpenFolderAndSelectItems(PCIDLIST_ABSOLUTE pidlFolder,
444                            UINT cidl,
445                            PCUITEMID_CHILD_ARRAY apidl,
446                            DWORD dwFlags)
447 {
448     ERR("SHOpenFolderAndSelectItems() is hackplemented\n");
449     CComHeapPtr<ITEMIDLIST> freeItem;
450     PCIDLIST_ABSOLUTE pidlItem;
451     if (cidl)
452     {
453         /* Firefox sends a full pidl here dispite the fact it is a PCUITEMID_CHILD_ARRAY -_- */
454         if (!ILIsSingle(apidl[0]))
455         {
456             pidlItem = apidl[0];
457         }
458         else
459         {
460             HRESULT hr = SHILCombine(pidlFolder, apidl[0], &pidlItem);
461             if (FAILED_UNEXPECTEDLY(hr))
462                 return hr;
463             freeItem.Attach(const_cast<PIDLIST_ABSOLUTE>(pidlItem));
464         }
465     }
466     else
467     {
468         pidlItem = pidlFolder;
469     }
470 
471     CComPtr<IShellFolder> psfDesktop;
472 
473     HRESULT hr = SHGetDesktopFolder(&psfDesktop);
474     if (FAILED_UNEXPECTEDLY(hr))
475         return hr;
476 
477     STRRET strret;
478     hr = psfDesktop->GetDisplayNameOf(pidlItem, SHGDN_FORPARSING, &strret);
479     if (FAILED_UNEXPECTEDLY(hr))
480         return hr;
481 
482     WCHAR wszBuf[MAX_PATH];
483     hr = StrRetToBufW(&strret, pidlItem, wszBuf, _countof(wszBuf));
484     if (FAILED_UNEXPECTEDLY(hr))
485         return hr;
486 
487     WCHAR wszParams[MAX_PATH];
488     wcscpy(wszParams, L"/select,");
489     wcscat(wszParams, wszBuf);
490 
491     SHELLEXECUTEINFOW sei;
492     memset(&sei, 0, sizeof sei);
493     sei.cbSize = sizeof sei;
494     sei.fMask = SEE_MASK_WAITFORINPUTIDLE;
495     sei.lpFile = L"explorer.exe";
496     sei.lpParameters = wszParams;
497 
498     if (ShellExecuteExW(&sei))
499         return S_OK;
500     else
501         return E_FAIL;
502 }
503 
504 /*
505  * for internal use
506  */
507 HRESULT
SHELL32_ShowPropertiesDialog(IDataObject * pdtobj)508 SHELL32_ShowPropertiesDialog(IDataObject *pdtobj)
509 {
510     if (!pdtobj)
511         return E_INVALIDARG;
512 
513     CDataObjectHIDA cida(pdtobj);
514     if (FAILED_UNEXPECTEDLY(cida.hr()))
515         return cida.hr();
516     if (cida->cidl > 1)
517     {
518         ERR("SHMultiFileProperties is not yet implemented\n");
519         return E_FAIL;
520     }
521     return SHELL32_ShowFilesystemItemPropertiesDialogAsync(pdtobj);
522 }
523 
524 HRESULT
SHELL32_DefaultContextMenuCallBack(IShellFolder * psf,IDataObject * pdo,UINT msg)525 SHELL32_DefaultContextMenuCallBack(IShellFolder *psf, IDataObject *pdo, UINT msg)
526 {
527     switch (msg)
528     {
529         case DFM_MERGECONTEXTMENU:
530             return S_OK; // Yes, I want verbs
531         case DFM_INVOKECOMMAND:
532             return S_FALSE; // Do it for me please
533         case DFM_GETDEFSTATICID:
534             return S_FALSE; // Supposedly "required for Windows 7 to pick a default"
535     }
536     return E_NOTIMPL;
537 }
538