1 /*
2  * PROJECT:     shell32
3  * LICENSE:     GPL - See COPYING in the top level directory
4  * FILE:        dll/win32/shell32/shv_item_new.c
5  * PURPOSE:     provides default context menu implementation
6  * PROGRAMMERS: Johannes Anderwald (johannes.anderwald@reactos.org)
7  */
8 
9 #include "precomp.h"
10 #include <compat_undoc.h>
11 
12 WINE_DEFAULT_DEBUG_CHANNEL(dmenu);
13 
14 
15 // FIXME: 260 is correct, but should this be part of the SDK or just MAX_PATH?
16 #define MAX_VERB 260
17 
18 static HRESULT
19 SHELL_GetRegCLSID(HKEY hKey, LPCWSTR SubKey, LPCWSTR Value, CLSID &clsid)
20 {
21     WCHAR buf[42];
22     DWORD cb = sizeof(buf);
23     DWORD err = RegGetValueW(hKey, SubKey, Value, RRF_RT_REG_SZ, NULL, buf, &cb);
24     return !err ? CLSIDFromString(buf, &clsid) : HRESULT_FROM_WIN32(err);
25 }
26 
27 static inline bool RegValueExists(HKEY hKey, LPCWSTR Name)
28 {
29     return RegQueryValueExW(hKey, Name, NULL, NULL, NULL, NULL) == ERROR_SUCCESS;
30 }
31 
32 static BOOL InsertMenuItemAt(HMENU hMenu, UINT Pos, UINT Flags)
33 {
34     MENUITEMINFOW mii;
35     mii.cbSize = FIELD_OFFSET(MENUITEMINFOW, hbmpItem); // USER32 version agnostic
36     mii.fMask = MIIM_TYPE;
37     mii.fType = Flags;
38     return InsertMenuItemW(hMenu, Pos, TRUE, &mii);
39 }
40 
41 typedef struct _DynamicShellEntry_
42 {
43     UINT iIdCmdFirst;
44     UINT NumIds;
45     CLSID ClassID;
46     CComPtr<IContextMenu> pCM;
47 } DynamicShellEntry, *PDynamicShellEntry;
48 
49 typedef struct _StaticShellEntry_
50 {
51     CStringW Verb;
52     HKEY hkClass;
53 } StaticShellEntry, *PStaticShellEntry;
54 
55 #define DCM_FCIDM_SHVIEW_OFFSET 0x7000 // Offset from the menu ids in the menu resource to FCIDM_SHVIEW_*
56 
57 //
58 // verbs for InvokeCommandInfo
59 //
60 static const struct _StaticInvokeCommandMap_
61 {
62     LPCSTR szStringVerb;
63     WORD IntVerb;
64     SHORT DfmCmd;
65 } g_StaticInvokeCmdMap[] =
66 {
67     { "RunAs", 0 },  // Unimplemented
68     { "Print", 0 },  // Unimplemented
69     { "Preview", 0 }, // Unimplemented
70     { "Open",            FCIDM_SHVIEW_OPEN },
71     { CMDSTR_NEWFOLDERA, FCIDM_SHVIEW_NEWFOLDER,  (SHORT)DFM_CMD_NEWFOLDER },
72     { "cut",             FCIDM_SHVIEW_CUT,        /* ? */ },
73     { "copy",            FCIDM_SHVIEW_COPY,       (SHORT)DFM_CMD_COPY },
74     { "paste",           FCIDM_SHVIEW_INSERT,     (SHORT)DFM_CMD_PASTE },
75     { "link",            FCIDM_SHVIEW_CREATELINK, (SHORT)DFM_CMD_LINK },
76     { "delete",          FCIDM_SHVIEW_DELETE,     (SHORT)DFM_CMD_DELETE },
77     { "properties",      FCIDM_SHVIEW_PROPERTIES, (SHORT)DFM_CMD_PROPERTIES },
78     { "rename",          FCIDM_SHVIEW_RENAME,     (SHORT)DFM_CMD_RENAME },
79     { "copyto",          FCIDM_SHVIEW_COPYTO },
80     { "moveto",          FCIDM_SHVIEW_MOVETO },
81 };
82 
83 UINT MapVerbToDfmCmd(_In_ LPCSTR verba)
84 {
85     for (UINT i = 0; i < _countof(g_StaticInvokeCmdMap); ++i)
86     {
87         if (!lstrcmpiA(g_StaticInvokeCmdMap[i].szStringVerb, verba))
88             return (int)g_StaticInvokeCmdMap[i].DfmCmd;
89     }
90     return 0;
91 }
92 
93 class CDefaultContextMenu :
94     public CComObjectRootEx<CComMultiThreadModelNoCS>,
95     public IContextMenu3,
96     public IObjectWithSite,
97     public IServiceProvider
98 {
99     private:
100         CComPtr<IUnknown> m_site;
101         CComPtr<IShellFolder> m_psf;
102         CComPtr<IContextMenuCB> m_pmcb;
103         LPFNDFMCALLBACK m_pfnmcb;
104         UINT m_cidl;
105         PCUITEMID_CHILD_ARRAY m_apidl;
106         CComPtr<IDataObject> m_pDataObj;
107         HKEY* m_aKeys;
108         UINT m_cKeys;
109         PIDLIST_ABSOLUTE m_pidlFolder;
110         DWORD m_bGroupPolicyActive;
111         CAtlList<DynamicShellEntry> m_DynamicEntries;
112         UINT m_iIdSHEFirst; /* first used id */
113         UINT m_iIdSHELast; /* last used id */
114         CAtlList<StaticShellEntry> m_StaticEntries;
115         UINT m_iIdSCMFirst; /* first static used id */
116         UINT m_iIdSCMLast; /* last static used id */
117         UINT m_iIdCBFirst; /* first callback used id */
118         UINT m_iIdCBLast;  /* last callback used id */
119         UINT m_iIdDfltFirst; /* first default part id */
120         UINT m_iIdDfltLast; /* last default part id */
121         HWND m_hwnd; /* window passed to callback */
122 
123         HRESULT _DoCallback(UINT uMsg, WPARAM wParam, LPVOID lParam);
124         void AddStaticEntry(const HKEY hkeyClass, const WCHAR *szVerb, UINT uFlags);
125         void AddStaticEntriesForKey(HKEY hKey, UINT uFlags);
126         void TryPickDefault(HMENU hMenu, UINT idCmdFirst, UINT DfltOffset, UINT uFlags);
127         BOOL IsShellExtensionAlreadyLoaded(REFCLSID clsid);
128         HRESULT LoadDynamicContextMenuHandler(HKEY hKey, REFCLSID clsid);
129         BOOL EnumerateDynamicContextHandlerForKey(HKEY hRootKey);
130         UINT AddShellExtensionsToMenu(HMENU hMenu, UINT* pIndexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags);
131         UINT AddStaticContextMenusToMenu(HMENU hMenu, UINT* IndexMenu, UINT iIdCmdFirst, UINT iIdCmdLast, UINT uFlags);
132         HRESULT DoPaste(LPCMINVOKECOMMANDINFOEX lpcmi, BOOL bLink);
133         HRESULT DoOpenOrExplore(LPCMINVOKECOMMANDINFOEX lpcmi);
134         HRESULT DoCreateLink(LPCMINVOKECOMMANDINFOEX lpcmi);
135         HRESULT DoDelete(LPCMINVOKECOMMANDINFOEX lpcmi);
136         HRESULT DoCopyOrCut(LPCMINVOKECOMMANDINFOEX lpcmi, BOOL bCopy);
137         HRESULT DoRename(LPCMINVOKECOMMANDINFOEX lpcmi);
138         HRESULT DoProperties(LPCMINVOKECOMMANDINFOEX lpcmi);
139         HRESULT DoUndo(LPCMINVOKECOMMANDINFOEX lpcmi);
140         HRESULT DoCreateNewFolder(LPCMINVOKECOMMANDINFOEX lpici);
141         HRESULT DoCopyToMoveToFolder(LPCMINVOKECOMMANDINFOEX lpici, BOOL bCopy);
142         HRESULT InvokeShellExt(LPCMINVOKECOMMANDINFOEX lpcmi);
143         HRESULT InvokeRegVerb(LPCMINVOKECOMMANDINFOEX lpcmi);
144         DWORD BrowserFlagsFromVerb(LPCMINVOKECOMMANDINFOEX lpcmi, PStaticShellEntry pEntry);
145         HRESULT TryToBrowse(LPCMINVOKECOMMANDINFOEX lpcmi, LPCITEMIDLIST pidl, DWORD wFlags);
146         HRESULT InvokePidl(LPCMINVOKECOMMANDINFOEX lpcmi, LPCITEMIDLIST pidl, PStaticShellEntry pEntry);
147         PDynamicShellEntry GetDynamicEntry(UINT idCmd);
148         BOOL MapVerbToCmdId(PVOID Verb, PUINT idCmd, BOOL IsUnicode);
149 
150     public:
151         CDefaultContextMenu();
152         ~CDefaultContextMenu();
153         HRESULT WINAPI Initialize(const DEFCONTEXTMENU *pdcm, LPFNDFMCALLBACK lpfn);
154 
155         // IContextMenu
156         STDMETHOD(QueryContextMenu)(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) override;
157         STDMETHOD(InvokeCommand)(LPCMINVOKECOMMANDINFO lpcmi) override;
158         STDMETHOD(GetCommandString)(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen) override;
159 
160         // IContextMenu2
161         STDMETHOD(HandleMenuMsg)(UINT uMsg, WPARAM wParam, LPARAM lParam) override;
162 
163         // IContextMenu3
164         STDMETHOD(HandleMenuMsg2)(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plResult) override;
165 
166         // IObjectWithSite
167         STDMETHOD(SetSite)(IUnknown *pUnkSite) override;
168         STDMETHOD(GetSite)(REFIID riid, void **ppvSite) override;
169 
170         // IServiceProvider
171         virtual HRESULT STDMETHODCALLTYPE QueryService(REFGUID svc, REFIID riid, void**ppv)
172         {
173             return IUnknown_QueryService(m_site, svc, riid, ppv);
174         }
175 
176         BEGIN_COM_MAP(CDefaultContextMenu)
177         COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu)
178         COM_INTERFACE_ENTRY_IID(IID_IContextMenu2, IContextMenu2)
179         COM_INTERFACE_ENTRY_IID(IID_IContextMenu3, IContextMenu3)
180         COM_INTERFACE_ENTRY_IID(IID_IObjectWithSite, IObjectWithSite)
181         COM_INTERFACE_ENTRY_IID(IID_IServiceProvider, IServiceProvider)
182         END_COM_MAP()
183 };
184 
185 CDefaultContextMenu::CDefaultContextMenu() :
186     m_psf(NULL),
187     m_pmcb(NULL),
188     m_pfnmcb(NULL),
189     m_cidl(0),
190     m_apidl(NULL),
191     m_pDataObj(NULL),
192     m_aKeys(NULL),
193     m_cKeys(NULL),
194     m_pidlFolder(NULL),
195     m_bGroupPolicyActive(0),
196     m_iIdSHEFirst(0),
197     m_iIdSHELast(0),
198     m_iIdSCMFirst(0),
199     m_iIdSCMLast(0),
200     m_iIdCBFirst(0),
201     m_iIdCBLast(0),
202     m_iIdDfltFirst(0),
203     m_iIdDfltLast(0),
204     m_hwnd(NULL)
205 {
206 }
207 
208 CDefaultContextMenu::~CDefaultContextMenu()
209 {
210     for (POSITION it = m_DynamicEntries.GetHeadPosition(); it != NULL;)
211     {
212         const DynamicShellEntry& info = m_DynamicEntries.GetNext(it);
213         IUnknown_SetSite(info.pCM.p, NULL);
214     }
215     m_DynamicEntries.RemoveAll();
216     m_StaticEntries.RemoveAll();
217 
218     for (UINT i = 0; i < m_cKeys; i++)
219         RegCloseKey(m_aKeys[i]);
220     HeapFree(GetProcessHeap(), 0, m_aKeys);
221 
222     if (m_pidlFolder)
223         CoTaskMemFree(m_pidlFolder);
224     _ILFreeaPidl(const_cast<PITEMID_CHILD *>(m_apidl), m_cidl);
225 }
226 
227 HRESULT WINAPI CDefaultContextMenu::Initialize(const DEFCONTEXTMENU *pdcm, LPFNDFMCALLBACK lpfn)
228 {
229     TRACE("cidl %u\n", pdcm->cidl);
230 
231     if (!pdcm->pcmcb && !lpfn)
232     {
233         ERR("CDefaultContextMenu needs a callback!\n");
234         return E_INVALIDARG;
235     }
236 
237     m_cidl = pdcm->cidl;
238     m_apidl = const_cast<PCUITEMID_CHILD_ARRAY>(_ILCopyaPidl(pdcm->apidl, m_cidl));
239     if (m_cidl && !m_apidl)
240         return E_OUTOFMEMORY;
241     m_psf = pdcm->psf;
242     m_pmcb = pdcm->pcmcb;
243     m_pfnmcb = lpfn;
244     m_hwnd = pdcm->hwnd;
245 
246     m_cKeys = pdcm->cKeys;
247     if (pdcm->cKeys)
248     {
249         m_aKeys = (HKEY*)HeapAlloc(GetProcessHeap(), 0, sizeof(HKEY) * pdcm->cKeys);
250         if (!m_aKeys)
251             return E_OUTOFMEMORY;
252         memcpy(m_aKeys, pdcm->aKeys, sizeof(HKEY) * pdcm->cKeys);
253     }
254 
255     m_psf->GetUIObjectOf(pdcm->hwnd, m_cidl, m_apidl, IID_NULL_PPV_ARG(IDataObject, &m_pDataObj));
256 
257     if (pdcm->pidlFolder)
258     {
259         m_pidlFolder = ILClone(pdcm->pidlFolder);
260     }
261     else
262     {
263         CComPtr<IPersistFolder2> pf = NULL;
264         if (SUCCEEDED(m_psf->QueryInterface(IID_PPV_ARG(IPersistFolder2, &pf))))
265         {
266             if (FAILED(pf->GetCurFolder(&m_pidlFolder)))
267                 ERR("GetCurFolder failed\n");
268         }
269         TRACE("pidlFolder %p\n", m_pidlFolder);
270     }
271 
272     return S_OK;
273 }
274 
275 HRESULT CDefaultContextMenu::_DoCallback(UINT uMsg, WPARAM wParam, LPVOID lParam)
276 {
277     if (m_pmcb)
278     {
279         return m_pmcb->CallBack(m_psf, m_hwnd, m_pDataObj, uMsg, wParam, (LPARAM)lParam);
280     }
281     else if(m_pfnmcb)
282     {
283         return m_pfnmcb(m_psf, m_hwnd, m_pDataObj, uMsg, wParam, (LPARAM)lParam);
284     }
285 
286     return E_FAIL;
287 }
288 
289 void CDefaultContextMenu::AddStaticEntry(const HKEY hkeyClass, const WCHAR *szVerb, UINT uFlags)
290 {
291     POSITION it = m_StaticEntries.GetHeadPosition();
292     while (it != NULL)
293     {
294         const StaticShellEntry& info = m_StaticEntries.GetNext(it);
295         if (info.Verb.CompareNoCase(szVerb) == 0)
296         {
297             /* entry already exists */
298             return;
299         }
300     }
301 
302     TRACE("adding verb %s\n", debugstr_w(szVerb));
303 
304     if (!wcsicmp(szVerb, L"open") && !(uFlags & CMF_NODEFAULT))
305     {
306         /* open verb is always inserted in front */
307         m_StaticEntries.AddHead({ szVerb, hkeyClass });
308     }
309     else
310     {
311         m_StaticEntries.AddTail({ szVerb, hkeyClass });
312     }
313 }
314 
315 void CDefaultContextMenu::AddStaticEntriesForKey(HKEY hKey, UINT uFlags)
316 {
317     WCHAR wszName[40];
318     DWORD cchName, dwIndex = 0;
319     HKEY hShellKey;
320 
321     LRESULT lres = RegOpenKeyExW(hKey, L"shell", 0, KEY_READ, &hShellKey);
322     if (lres != STATUS_SUCCESS)
323         return;
324 
325     while(TRUE)
326     {
327         cchName = _countof(wszName);
328         if (RegEnumKeyExW(hShellKey, dwIndex++, wszName, &cchName, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
329             break;
330 
331         AddStaticEntry(hKey, wszName, uFlags);
332     }
333 
334     RegCloseKey(hShellKey);
335 }
336 
337 static
338 BOOL
339 HasClipboardData()
340 {
341     BOOL bRet = FALSE;
342     CComPtr<IDataObject> pDataObj;
343 
344     if (SUCCEEDED(OleGetClipboard(&pDataObj)))
345     {
346         FORMATETC formatetc;
347 
348         TRACE("pDataObj=%p\n", pDataObj.p);
349 
350         /* Set the FORMATETC structure*/
351         InitFormatEtc(formatetc, RegisterClipboardFormatW(CFSTR_SHELLIDLIST), TYMED_HGLOBAL);
352         bRet = SUCCEEDED(pDataObj->QueryGetData(&formatetc));
353     }
354 
355     return bRet;
356 }
357 
358 BOOL
359 CDefaultContextMenu::IsShellExtensionAlreadyLoaded(REFCLSID clsid)
360 {
361     POSITION it = m_DynamicEntries.GetHeadPosition();
362     while (it != NULL)
363     {
364         const DynamicShellEntry& info = m_DynamicEntries.GetNext(it);
365         if (info.ClassID == clsid)
366             return TRUE;
367     }
368 
369     return FALSE;
370 }
371 
372 HRESULT
373 CDefaultContextMenu::LoadDynamicContextMenuHandler(HKEY hKey, REFCLSID clsid)
374 {
375     HRESULT hr;
376     TRACE("LoadDynamicContextMenuHandler entered with This %p hKey %p pclsid %s\n", this, hKey, wine_dbgstr_guid(&clsid));
377 
378     if (IsShellExtensionAlreadyLoaded(clsid))
379         return S_OK;
380 
381     CComPtr<IContextMenu> pcm;
382     hr = SHCoCreateInstance(NULL, &clsid, NULL, IID_PPV_ARG(IContextMenu, &pcm));
383     if (FAILED(hr))
384     {
385         ERR("SHCoCreateInstance(IContextMenu) failed.clsid %s hr 0x%x\n", wine_dbgstr_guid(&clsid), hr);
386         return hr;
387     }
388 
389     CComPtr<IShellExtInit> pExtInit;
390     hr = pcm->QueryInterface(IID_PPV_ARG(IShellExtInit, &pExtInit));
391     if (FAILED(hr))
392     {
393         ERR("IContextMenu->QueryInterface(IShellExtInit) failed.clsid %s hr 0x%x\n", wine_dbgstr_guid(&clsid), hr);
394         return hr;
395     }
396 
397     hr = pExtInit->Initialize(m_pDataObj ? NULL : m_pidlFolder, m_pDataObj, hKey);
398     if (FAILED(hr))
399     {
400         WARN("IShellExtInit::Initialize failed.clsid %s hr 0x%x\n", wine_dbgstr_guid(&clsid), hr);
401         return hr;
402     }
403 
404     if (m_site)
405         IUnknown_SetSite(pcm, m_site);
406 
407     m_DynamicEntries.AddTail({ 0, 0, clsid, pcm });
408 
409     return S_OK;
410 }
411 
412 BOOL
413 CDefaultContextMenu::EnumerateDynamicContextHandlerForKey(HKEY hRootKey)
414 {
415     WCHAR wszName[MAX_PATH], wszBuf[MAX_PATH], *pwszClsid;
416     DWORD cchName;
417     HRESULT hr;
418     HKEY hKey;
419 
420     if (RegOpenKeyExW(hRootKey, L"shellex\\ContextMenuHandlers", 0, KEY_READ, &hKey) != ERROR_SUCCESS)
421     {
422         TRACE("RegOpenKeyExW failed\n");
423         return FALSE;
424     }
425 
426     DWORD dwIndex = 0;
427     while (TRUE)
428     {
429         cchName = _countof(wszName);
430         if (RegEnumKeyExW(hKey, dwIndex++, wszName, &cchName, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
431             break;
432 
433         /* Key name or key value is CLSID */
434         CLSID clsid;
435         hr = CLSIDFromString(wszName, &clsid);
436         if (hr == S_OK)
437             pwszClsid = wszName;
438         else
439         {
440             DWORD cchBuf = _countof(wszBuf);
441             if (RegGetValueW(hKey, wszName, NULL, RRF_RT_REG_SZ, NULL, wszBuf, &cchBuf) == ERROR_SUCCESS)
442                 hr = CLSIDFromString(wszBuf, &clsid);
443             pwszClsid = wszBuf;
444         }
445 
446         if (FAILED(hr))
447         {
448             ERR("CLSIDFromString failed for clsid %S hr 0x%x\n", pwszClsid, hr);
449             continue;
450         }
451 
452         if (m_bGroupPolicyActive)
453         {
454             if (RegGetValueW(HKEY_LOCAL_MACHINE,
455                              L"Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved",
456                              pwszClsid,
457                              RRF_RT_REG_SZ,
458                              NULL,
459                              NULL,
460                              NULL) != ERROR_SUCCESS)
461             {
462                 ERR("Shell extension %s not approved!\n", pwszClsid);
463                 continue;
464             }
465         }
466 
467         hr = LoadDynamicContextMenuHandler(hRootKey, clsid);
468         if (FAILED(hr))
469             WARN("Failed to get context menu entires from shell extension! clsid: %S\n", pwszClsid);
470     }
471 
472     RegCloseKey(hKey);
473     return TRUE;
474 }
475 
476 UINT
477 CDefaultContextMenu::AddShellExtensionsToMenu(HMENU hMenu, UINT* pIndexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
478 {
479     UINT cIds = 0;
480 
481     if (m_DynamicEntries.IsEmpty())
482         return cIds;
483 
484     POSITION it = m_DynamicEntries.GetHeadPosition();
485     while (it != NULL)
486     {
487         DynamicShellEntry& info = m_DynamicEntries.GetNext(it);
488 
489         HRESULT hr = info.pCM->QueryContextMenu(hMenu, *pIndexMenu, idCmdFirst + cIds, idCmdLast, uFlags);
490         if (SUCCEEDED(hr))
491         {
492             info.iIdCmdFirst = cIds;
493             info.NumIds = HRESULT_CODE(hr);
494             (*pIndexMenu) += info.NumIds;
495 
496             cIds += info.NumIds;
497             if (idCmdFirst + cIds >= idCmdLast)
498                 break;
499         }
500         TRACE("pEntry hr %x contextmenu %p cmdfirst %x num ids %x\n", hr, info.pCM.p, info.iIdCmdFirst, info.NumIds);
501     }
502     return cIds;
503 }
504 
505 UINT
506 CDefaultContextMenu::AddStaticContextMenusToMenu(
507     HMENU hMenu,
508     UINT* pIndexMenu,
509     UINT iIdCmdFirst,
510     UINT iIdCmdLast,
511     UINT uFlags)
512 {
513     UINT ntver = RosGetProcessEffectiveVersion();
514     MENUITEMINFOW mii = { sizeof(mii) };
515     UINT idResource;
516     WCHAR wszVerb[40];
517     UINT fState;
518     UINT cIds = 0, indexFirst = *pIndexMenu;
519 
520     mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE | MIIM_DATA;
521     mii.fType = MFT_STRING;
522 
523     POSITION it = m_StaticEntries.GetHeadPosition();
524     bool first = true;
525     while (it != NULL)
526     {
527         StaticShellEntry& info = m_StaticEntries.GetNext(it);
528         BOOL forceFirstPos = FALSE;
529 
530         fState = MFS_ENABLED;
531 
532         /* set first entry as default */
533         if (first)
534         {
535             fState |= MFS_DEFAULT;
536             first = false;
537         }
538 
539         if (info.Verb.CompareNoCase(L"open") == 0)
540         {
541             idResource = IDS_OPEN_VERB;
542             fState |= MFS_DEFAULT; /* override default when open verb is found */
543             forceFirstPos++;
544         }
545         else if (info.Verb.CompareNoCase(L"explore") == 0)
546         {
547             idResource = IDS_EXPLORE_VERB;
548             if (uFlags & CMF_EXPLORE)
549             {
550                 fState |= MFS_DEFAULT;
551                 forceFirstPos++;
552             }
553         }
554         else if (info.Verb.CompareNoCase(L"runas") == 0)
555             idResource = IDS_RUNAS_VERB;
556         else if (info.Verb.CompareNoCase(L"edit") == 0)
557             idResource = IDS_EDIT_VERB;
558         else if (info.Verb.CompareNoCase(L"find") == 0)
559             idResource = IDS_FIND_VERB;
560         else if (info.Verb.CompareNoCase(L"print") == 0)
561             idResource = IDS_PRINT_VERB;
562         else if (info.Verb.CompareNoCase(L"printto") == 0)
563             continue;
564         else
565             idResource = 0;
566 
567         /* By default use verb for menu item name */
568         mii.dwTypeData = (LPWSTR)info.Verb.GetString();
569 
570         WCHAR wszKey[sizeof("shell\\") + MAX_VERB];
571         HRESULT hr;
572         hr = StringCbPrintfW(wszKey, sizeof(wszKey), L"shell\\%s", info.Verb.GetString());
573         if (FAILED_UNEXPECTEDLY(hr))
574         {
575             continue;
576         }
577 
578         UINT cmdFlags = 0;
579         bool hide = false;
580         HKEY hkVerb;
581         if (idResource > 0)
582         {
583             if (!(uFlags & CMF_OPTIMIZEFORINVOKE))
584             {
585                 if (LoadStringW(shell32_hInstance, idResource, wszVerb, _countof(wszVerb)))
586                     mii.dwTypeData = wszVerb; /* use translated verb */
587                 else
588                     ERR("Failed to load string\n");
589             }
590 
591             if (RegOpenKeyW(info.hkClass, wszKey, &hkVerb) != ERROR_SUCCESS)
592                 hkVerb = NULL;
593         }
594         else
595         {
596             if (RegOpenKeyW(info.hkClass, wszKey, &hkVerb) == ERROR_SUCCESS)
597             {
598                 if (!(uFlags & CMF_OPTIMIZEFORINVOKE))
599                 {
600                     DWORD cbVerb = sizeof(wszVerb);
601 
602                     LONG res = RegLoadMUIStringW(hkVerb, L"MUIVerb", wszVerb, cbVerb, NULL, 0, NULL);
603                     if (res || !*wszVerb)
604                         res = RegLoadMUIStringW(hkVerb, NULL, wszVerb, cbVerb, NULL, 0, NULL);
605 
606                     if (res == ERROR_SUCCESS && *wszVerb)
607                     {
608                         /* use description for the menu entry */
609                         mii.dwTypeData = wszVerb;
610                     }
611                 }
612             }
613             else
614             {
615                 hkVerb = NULL;
616             }
617         }
618 
619         if (hkVerb)
620         {
621             if (!(uFlags & CMF_EXTENDEDVERBS))
622                 hide = RegValueExists(hkVerb, L"Extended");
623 
624             if (!hide)
625                 hide = RegValueExists(hkVerb, L"ProgrammaticAccessOnly");
626 
627             if (!hide && !(uFlags & CMF_DISABLEDVERBS))
628                 hide = RegValueExists(hkVerb, L"LegacyDisable");
629 
630             if (RegValueExists(hkVerb, L"NeverDefault"))
631                 fState &= ~MFS_DEFAULT;
632 
633             if (RegValueExists(hkVerb, L"SeparatorBefore"))
634                 cmdFlags |= ECF_SEPARATORBEFORE;
635             if (RegValueExists(hkVerb, L"SeparatorAfter"))
636                 cmdFlags |= ECF_SEPARATORAFTER;
637 
638             RegCloseKey(hkVerb);
639         }
640 
641         if (((uFlags & CMF_NODEFAULT) && ntver >= _WIN32_WINNT_VISTA) ||
642             ((uFlags & CMF_DONOTPICKDEFAULT) && ntver >= _WIN32_WINNT_WIN7))
643         {
644             fState &= ~MFS_DEFAULT;
645         }
646 
647         if (!hide)
648         {
649             if (cmdFlags & ECF_SEPARATORBEFORE)
650             {
651                 if (InsertMenuItemAt(hMenu, *pIndexMenu, MF_SEPARATOR))
652                     (*pIndexMenu)++;
653             }
654 
655             mii.fState = fState;
656             mii.wID = iIdCmdFirst + cIds;
657             if (InsertMenuItemW(hMenu, forceFirstPos ? indexFirst : *pIndexMenu, TRUE, &mii))
658                 (*pIndexMenu)++;
659 
660             if (cmdFlags & ECF_SEPARATORAFTER)
661             {
662                 if (InsertMenuItemAt(hMenu, *pIndexMenu, MF_SEPARATOR))
663                     (*pIndexMenu)++;
664             }
665         }
666         cIds++; // Always increment the id because it acts as the index into m_StaticEntries
667 
668         if (mii.wID >= iIdCmdLast)
669             break;
670     }
671 
672     return cIds;
673 }
674 
675 void WINAPI _InsertMenuItemW(
676     HMENU hMenu,
677     UINT indexMenu,
678     BOOL fByPosition,
679     UINT wID,
680     UINT fType,
681     LPCWSTR dwTypeData,
682     UINT fState)
683 {
684     MENUITEMINFOW mii;
685     WCHAR wszText[100];
686 
687     ZeroMemory(&mii, sizeof(mii));
688     mii.cbSize = sizeof(mii);
689     if (fType == MFT_SEPARATOR)
690         mii.fMask = MIIM_ID | MIIM_TYPE;
691     else if (fType == MFT_STRING)
692     {
693         mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE;
694         if (IS_INTRESOURCE(dwTypeData))
695         {
696             if (LoadStringW(shell32_hInstance, LOWORD((ULONG_PTR)dwTypeData), wszText, _countof(wszText)))
697                 mii.dwTypeData = wszText;
698             else
699             {
700                 ERR("failed to load string %p\n", dwTypeData);
701                 return;
702             }
703         }
704         else
705             mii.dwTypeData = (LPWSTR)dwTypeData;
706         mii.fState = fState;
707     }
708 
709     mii.wID = wID;
710     mii.fType = fType;
711     InsertMenuItemW(hMenu, indexMenu, fByPosition, &mii);
712 }
713 
714 void
715 CDefaultContextMenu::TryPickDefault(HMENU hMenu, UINT idCmdFirst, UINT DfltOffset, UINT uFlags)
716 {
717     // Are we allowed to pick a default?
718     if ((uFlags & CMF_NODEFAULT) ||
719         ((uFlags & CMF_DONOTPICKDEFAULT) && RosGetProcessEffectiveVersion() >= _WIN32_WINNT_WIN7))
720     {
721         return;
722     }
723 
724     // Do we already have a default?
725     if ((int)GetMenuDefaultItem(hMenu, MF_BYPOSITION, 0) != -1)
726         return;
727 
728     // Does the view want to pick one?
729     INT_PTR forceDfm = 0;
730     if (SUCCEEDED(_DoCallback(DFM_GETDEFSTATICID, 0, &forceDfm)) && forceDfm)
731     {
732         for (UINT i = 0; i < _countof(g_StaticInvokeCmdMap); ++i)
733         {
734             UINT menuItemId = g_StaticInvokeCmdMap[i].IntVerb + DfltOffset - DCM_FCIDM_SHVIEW_OFFSET;
735             if (g_StaticInvokeCmdMap[i].DfmCmd == forceDfm &&
736                 SetMenuDefaultItem(hMenu, menuItemId, MF_BYCOMMAND))
737             {
738                 return;
739             }
740         }
741     }
742 
743     // Don't want to pick something like cut or delete as the default but
744     // a static or dynamic verb is a good default.
745     if (m_iIdSCMLast > m_iIdSCMFirst || m_iIdSHELast > m_iIdSHEFirst)
746         SetMenuDefaultItem(hMenu, idCmdFirst, MF_BYCOMMAND);
747 }
748 
749 HRESULT
750 WINAPI
751 CDefaultContextMenu::QueryContextMenu(
752     HMENU hMenu,
753     UINT IndexMenu,
754     UINT idCmdFirst,
755     UINT idCmdLast,
756     UINT uFlags)
757 {
758     HRESULT hr;
759     UINT idCmdNext = idCmdFirst;
760     UINT cIds = 0;
761 
762     TRACE("BuildShellItemContextMenu entered\n");
763 
764     /* Load static verbs and shell extensions from registry */
765     for (UINT i = 0; i < m_cKeys && !(uFlags & CMF_NOVERBS); i++)
766     {
767         AddStaticEntriesForKey(m_aKeys[i], uFlags);
768         EnumerateDynamicContextHandlerForKey(m_aKeys[i]);
769     }
770 
771     /* Add static context menu handlers */
772     cIds = AddStaticContextMenusToMenu(hMenu, &IndexMenu, idCmdFirst, idCmdLast, uFlags);
773     m_iIdSCMFirst = 0; // FIXME: This should be = idCmdFirst?
774     m_iIdSCMLast = cIds;
775     idCmdNext = idCmdFirst + cIds;
776 
777     /* Add dynamic context menu handlers */
778     cIds += AddShellExtensionsToMenu(hMenu, &IndexMenu, idCmdNext, idCmdLast, uFlags);
779     m_iIdSHEFirst = m_iIdSCMLast;
780     m_iIdSHELast = cIds;
781     idCmdNext = idCmdFirst + cIds;
782     TRACE("SH_LoadContextMenuHandlers first %x last %x\n", m_iIdSHEFirst, m_iIdSHELast);
783 
784     /* Now let the callback add its own items */
785     QCMINFO qcminfo = {hMenu, IndexMenu, idCmdNext, idCmdLast, NULL};
786     if (SUCCEEDED(_DoCallback(DFM_MERGECONTEXTMENU, uFlags, &qcminfo)))
787     {
788         UINT added = qcminfo.idCmdFirst - idCmdNext;
789         cIds += added;
790         IndexMenu += added;
791         m_iIdCBFirst = m_iIdSHELast;
792         m_iIdCBLast = cIds;
793         idCmdNext = idCmdFirst + cIds;
794     }
795 
796     //TODO: DFM_MERGECONTEXTMENU_BOTTOM
797 
798     UINT idDefaultOffset = 0;
799     BOOL isBackgroundMenu = !m_cidl;
800     if (!(uFlags & CMF_VERBSONLY) && !isBackgroundMenu)
801     {
802         /* Get the attributes of the items */
803         SFGAOF rfg = SFGAO_BROWSABLE | SFGAO_CANCOPY | SFGAO_CANLINK | SFGAO_CANMOVE | SFGAO_CANDELETE | SFGAO_CANRENAME | SFGAO_HASPROPSHEET | SFGAO_FILESYSTEM | SFGAO_FOLDER;
804         hr = m_psf->GetAttributesOf(m_cidl, m_apidl, &rfg);
805         if (FAILED_UNEXPECTEDLY(hr))
806             return MAKE_HRESULT(SEVERITY_SUCCESS, 0, cIds);
807 
808         /* Add the default part of the menu */
809         HMENU hmenuDefault = LoadMenu(_AtlBaseModule.GetResourceInstance(), L"MENU_SHV_FILE");
810 
811         /* Remove uneeded entries */
812         if (!(rfg & SFGAO_CANMOVE))
813             DeleteMenu(hmenuDefault, IDM_CUT, MF_BYCOMMAND);
814         if (!(rfg & SFGAO_CANCOPY))
815             DeleteMenu(hmenuDefault, IDM_COPY, MF_BYCOMMAND);
816         if (!((rfg & SFGAO_FILESYSTEM) && HasClipboardData()))
817             DeleteMenu(hmenuDefault, IDM_INSERT, MF_BYCOMMAND);
818         if (!(rfg & SFGAO_CANLINK))
819             DeleteMenu(hmenuDefault, IDM_CREATELINK, MF_BYCOMMAND);
820         if (!(rfg & SFGAO_CANDELETE))
821             DeleteMenu(hmenuDefault, IDM_DELETE, MF_BYCOMMAND);
822         if (!(rfg & SFGAO_CANRENAME) || !(uFlags & CMF_CANRENAME))
823             DeleteMenu(hmenuDefault, IDM_RENAME, MF_BYCOMMAND);
824         if (!(rfg & SFGAO_HASPROPSHEET))
825             DeleteMenu(hmenuDefault, IDM_PROPERTIES, MF_BYCOMMAND);
826 
827         idDefaultOffset = idCmdNext;
828         UINT idMax = Shell_MergeMenus(hMenu, GetSubMenu(hmenuDefault, 0), IndexMenu, idCmdNext, idCmdLast, 0);
829         m_iIdDfltFirst = cIds;
830         cIds += idMax - idCmdNext;
831         m_iIdDfltLast = cIds;
832 
833         DestroyMenu(hmenuDefault);
834     }
835 
836     TryPickDefault(hMenu, idCmdFirst, idDefaultOffset, uFlags);
837 
838     // TODO: DFM_MERGECONTEXTMENU_TOP
839 
840     return MAKE_HRESULT(SEVERITY_SUCCESS, 0, cIds);
841 }
842 
843 HRESULT CDefaultContextMenu::DoPaste(LPCMINVOKECOMMANDINFOEX lpcmi, BOOL bLink)
844 {
845     HRESULT hr;
846 
847     CComPtr<IDataObject> pda;
848     hr = OleGetClipboard(&pda);
849     if (FAILED_UNEXPECTEDLY(hr))
850         return hr;
851 
852     FORMATETC formatetc2;
853     STGMEDIUM medium2;
854     InitFormatEtc(formatetc2, RegisterClipboardFormatW(CFSTR_PREFERREDDROPEFFECT), TYMED_HGLOBAL);
855 
856     DWORD dwKey= 0;
857 
858     if (SUCCEEDED(pda->GetData(&formatetc2, &medium2)))
859     {
860         DWORD * pdwFlag = (DWORD*)GlobalLock(medium2.hGlobal);
861         if (pdwFlag)
862         {
863             if (*pdwFlag == DROPEFFECT_COPY)
864                 dwKey = MK_CONTROL;
865             else
866                 dwKey = MK_SHIFT;
867         }
868         else
869         {
870             ERR("No drop effect obtained\n");
871         }
872         GlobalUnlock(medium2.hGlobal);
873     }
874 
875     if (bLink)
876     {
877         dwKey = MK_CONTROL|MK_SHIFT;
878     }
879 
880     CComPtr<IDropTarget> pdrop;
881     if (m_cidl)
882         hr = m_psf->GetUIObjectOf(NULL, 1, &m_apidl[0], IID_NULL_PPV_ARG(IDropTarget, &pdrop));
883     else
884         hr = m_psf->CreateViewObject(NULL, IID_PPV_ARG(IDropTarget, &pdrop));
885 
886     if (FAILED_UNEXPECTEDLY(hr))
887         return hr;
888 
889     SHSimulateDrop(pdrop, pda, dwKey, NULL, NULL);
890 
891     TRACE("CP result %x\n", hr);
892     return S_OK;
893 }
894 
895 HRESULT
896 CDefaultContextMenu::DoOpenOrExplore(LPCMINVOKECOMMANDINFOEX lpcmi)
897 {
898     UNIMPLEMENTED;
899     return E_FAIL;
900 }
901 
902 HRESULT CDefaultContextMenu::DoCreateLink(LPCMINVOKECOMMANDINFOEX lpcmi)
903 {
904     if (!m_cidl || !m_pDataObj)
905         return E_FAIL;
906 
907     CComPtr<IDropTarget> pDT;
908     HRESULT hr = m_psf->CreateViewObject(NULL, IID_PPV_ARG(IDropTarget, &pDT));
909     if (FAILED_UNEXPECTEDLY(hr))
910         return hr;
911 
912     SHSimulateDrop(pDT, m_pDataObj, MK_CONTROL|MK_SHIFT, NULL, NULL);
913 
914     return S_OK;
915 }
916 
917 HRESULT CDefaultContextMenu::DoDelete(LPCMINVOKECOMMANDINFOEX lpcmi)
918 {
919     if (!m_cidl || !m_pDataObj)
920         return E_FAIL;
921 
922     CComPtr<IDropTarget> pDT;
923     HRESULT hr = CRecyclerDropTarget_CreateInstance(IID_PPV_ARG(IDropTarget, &pDT));
924     if (FAILED_UNEXPECTEDLY(hr))
925         return hr;
926 
927     DWORD grfKeyState = (lpcmi->fMask & CMIC_MASK_SHIFT_DOWN) ? MK_SHIFT : 0;
928     SHSimulateDrop(pDT, m_pDataObj, grfKeyState, NULL, NULL);
929 
930     return S_OK;
931 }
932 
933 HRESULT CDefaultContextMenu::DoCopyOrCut(LPCMINVOKECOMMANDINFOEX lpcmi, BOOL bCopy)
934 {
935     if (!m_cidl || !m_pDataObj)
936         return E_FAIL;
937 
938     FORMATETC formatetc;
939     InitFormatEtc(formatetc, RegisterClipboardFormatW(CFSTR_PREFERREDDROPEFFECT), TYMED_HGLOBAL);
940     STGMEDIUM medium = {0};
941     medium.tymed = TYMED_HGLOBAL;
942     medium.hGlobal = GlobalAlloc(GHND, sizeof(DWORD));
943     DWORD* pdwFlag = (DWORD*)GlobalLock(medium.hGlobal);
944     if (pdwFlag)
945         *pdwFlag = bCopy ? DROPEFFECT_COPY : DROPEFFECT_MOVE;
946     GlobalUnlock(medium.hGlobal);
947     m_pDataObj->SetData(&formatetc, &medium, TRUE);
948 
949     HRESULT hr = OleSetClipboard(m_pDataObj);
950     if (FAILED_UNEXPECTEDLY(hr))
951         return hr;
952 
953     return S_OK;
954 }
955 
956 HRESULT CDefaultContextMenu::DoRename(LPCMINVOKECOMMANDINFOEX lpcmi)
957 {
958     CComPtr<IShellBrowser> psb;
959     HRESULT hr;
960 
961     if (!m_site || !m_cidl)
962         return E_FAIL;
963 
964     /* Get a pointer to the shell browser */
965     hr = IUnknown_QueryService(m_site, SID_IShellBrowser, IID_PPV_ARG(IShellBrowser, &psb));
966     if (FAILED_UNEXPECTEDLY(hr))
967         return hr;
968 
969     CComPtr<IShellView> lpSV;
970     hr = psb->QueryActiveShellView(&lpSV);
971     if (FAILED_UNEXPECTEDLY(hr))
972         return hr;
973 
974     SVSIF selFlags = SVSI_DESELECTOTHERS | SVSI_EDIT | SVSI_ENSUREVISIBLE | SVSI_FOCUSED | SVSI_SELECT;
975     hr = lpSV->SelectItem(m_apidl[0], selFlags);
976     if (FAILED_UNEXPECTEDLY(hr))
977         return hr;
978 
979     return S_OK;
980 }
981 
982 HRESULT
983 CDefaultContextMenu::DoProperties(
984     LPCMINVOKECOMMANDINFOEX lpcmi)
985 {
986     HRESULT hr = _DoCallback(DFM_INVOKECOMMAND, DFM_CMD_PROPERTIES, NULL);
987 
988     // We are asked to run the default property sheet
989     if (hr == S_FALSE)
990     {
991         return SHELL32_ShowPropertiesDialog(m_pDataObj);
992     }
993 
994     return hr;
995 }
996 
997 HRESULT
998 CDefaultContextMenu::DoUndo(LPCMINVOKECOMMANDINFOEX lpcmi)
999 {
1000     ERR("TODO: Undo\n");
1001     return E_NOTIMPL;
1002 }
1003 
1004 HRESULT
1005 CDefaultContextMenu::DoCopyToMoveToFolder(LPCMINVOKECOMMANDINFOEX lpici, BOOL bCopy)
1006 {
1007     HRESULT hr = E_FAIL;
1008     if (!m_pDataObj)
1009     {
1010         ERR("m_pDataObj is NULL\n");
1011         return hr;
1012     }
1013 
1014     CComPtr<IContextMenu> pContextMenu;
1015     if (bCopy)
1016         hr = SHCoCreateInstance(NULL, &CLSID_CopyToMenu, NULL,
1017                                 IID_PPV_ARG(IContextMenu, &pContextMenu));
1018     else
1019         hr = SHCoCreateInstance(NULL, &CLSID_MoveToMenu, NULL,
1020                                 IID_PPV_ARG(IContextMenu, &pContextMenu));
1021     if (FAILED_UNEXPECTEDLY(hr))
1022         return hr;
1023 
1024     CComPtr<IShellExtInit> pInit;
1025     hr = pContextMenu->QueryInterface(IID_PPV_ARG(IShellExtInit, &pInit));
1026     if (FAILED_UNEXPECTEDLY(hr))
1027         return hr;
1028 
1029     hr = pInit->Initialize(m_pidlFolder, m_pDataObj, NULL);
1030     if (FAILED_UNEXPECTEDLY(hr))
1031         return hr;
1032 
1033     if (bCopy)
1034         lpici->lpVerb = "copyto";
1035     else
1036         lpici->lpVerb = "moveto";
1037 
1038     return pContextMenu->InvokeCommand((LPCMINVOKECOMMANDINFO)lpici);
1039 }
1040 
1041 // This code is taken from CNewMenu and should be shared between the 2 classes
1042 HRESULT
1043 CDefaultContextMenu::DoCreateNewFolder(
1044     LPCMINVOKECOMMANDINFOEX lpici)
1045 {
1046     WCHAR wszPath[MAX_PATH];
1047     WCHAR wszName[MAX_PATH];
1048     WCHAR wszNewFolder[25];
1049     HRESULT hr;
1050 
1051     /* Get folder path */
1052     hr = SHGetPathFromIDListW(m_pidlFolder, wszPath);
1053     if (FAILED_UNEXPECTEDLY(hr))
1054         return hr;
1055 
1056     if (!LoadStringW(shell32_hInstance, IDS_NEWFOLDER, wszNewFolder, _countof(wszNewFolder)))
1057         return E_FAIL;
1058 
1059     /* Create the name of the new directory */
1060     if (!PathYetAnotherMakeUniqueName(wszName, wszPath, NULL, wszNewFolder))
1061         return E_FAIL;
1062 
1063     /* Create the new directory and show the appropriate dialog in case of error */
1064     if (SHCreateDirectory(lpici->hwnd, wszName) != ERROR_SUCCESS)
1065         return E_FAIL;
1066 
1067     /* Show and select the new item in the def view */
1068     LPITEMIDLIST pidl;
1069     PITEMID_CHILD pidlNewItem;
1070     CComPtr<IShellView> psv;
1071 
1072     /* Notify the view object about the new item */
1073     SHChangeNotify(SHCNE_MKDIR, SHCNF_PATHW | SHCNF_FLUSH, (LPCVOID)wszName, NULL);
1074 
1075     if (!m_site)
1076         return S_OK;
1077 
1078     /* Get a pointer to the shell view */
1079     hr = IUnknown_QueryService(m_site, SID_IFolderView, IID_PPV_ARG(IShellView, &psv));
1080     if (FAILED_UNEXPECTEDLY(hr))
1081         return S_OK;
1082 
1083     /* Attempt to get the pidl of the new item */
1084     hr = SHILCreateFromPathW(wszName, &pidl, NULL);
1085     if (FAILED_UNEXPECTEDLY(hr))
1086         return hr;
1087 
1088     pidlNewItem = ILFindLastID(pidl);
1089 
1090     hr = psv->SelectItem(pidlNewItem, SVSI_DESELECTOTHERS | SVSI_EDIT | SVSI_ENSUREVISIBLE |
1091                           SVSI_FOCUSED | SVSI_SELECT);
1092     if (FAILED_UNEXPECTEDLY(hr))
1093         return hr;
1094 
1095     SHFree(pidl);
1096 
1097     return S_OK;
1098 }
1099 
1100 PDynamicShellEntry CDefaultContextMenu::GetDynamicEntry(UINT idCmd)
1101 {
1102     POSITION it = m_DynamicEntries.GetHeadPosition();
1103     while (it != NULL)
1104     {
1105         DynamicShellEntry& info = m_DynamicEntries.GetNext(it);
1106 
1107         if (idCmd >= info.iIdCmdFirst + info.NumIds)
1108             continue;
1109 
1110         if (idCmd < info.iIdCmdFirst || idCmd > info.iIdCmdFirst + info.NumIds)
1111             return NULL;
1112 
1113         return &info;
1114     }
1115 
1116     return NULL;
1117 }
1118 
1119 BOOL
1120 CDefaultContextMenu::MapVerbToCmdId(PVOID Verb, PUINT idCmd, BOOL IsUnicode)
1121 {
1122     WCHAR UnicodeStr[MAX_VERB];
1123 
1124     /* Loop through all the static verbs looking for a match */
1125     for (UINT i = 0; i < _countof(g_StaticInvokeCmdMap); i++)
1126     {
1127         /* We can match both ANSI and unicode strings */
1128         if (IsUnicode)
1129         {
1130             /* The static verbs are ANSI, get a unicode version before doing the compare */
1131             SHAnsiToUnicode(g_StaticInvokeCmdMap[i].szStringVerb, UnicodeStr, MAX_VERB);
1132             if (!wcscmp(UnicodeStr, (LPWSTR)Verb))
1133             {
1134                 /* Return the Corresponding Id */
1135                 *idCmd = g_StaticInvokeCmdMap[i].IntVerb;
1136                 return TRUE;
1137             }
1138         }
1139         else
1140         {
1141             if (!strcmp(g_StaticInvokeCmdMap[i].szStringVerb, (LPSTR)Verb))
1142             {
1143                 *idCmd = g_StaticInvokeCmdMap[i].IntVerb;
1144                 return TRUE;
1145             }
1146         }
1147     }
1148 
1149     return FALSE;
1150 }
1151 
1152 HRESULT
1153 CDefaultContextMenu::InvokeShellExt(
1154     LPCMINVOKECOMMANDINFOEX lpcmi)
1155 {
1156     TRACE("verb %p first %x last %x\n", lpcmi->lpVerb, m_iIdSHEFirst, m_iIdSHELast);
1157 
1158     UINT idCmd = LOWORD(lpcmi->lpVerb);
1159     PDynamicShellEntry pEntry = GetDynamicEntry(idCmd);
1160     if (!pEntry)
1161         return E_FAIL;
1162 
1163     /* invoke the dynamic context menu */
1164     lpcmi->lpVerb = MAKEINTRESOURCEA(idCmd - pEntry->iIdCmdFirst);
1165     return pEntry->pCM->InvokeCommand((LPCMINVOKECOMMANDINFO)lpcmi);
1166 }
1167 
1168 DWORD
1169 CDefaultContextMenu::BrowserFlagsFromVerb(LPCMINVOKECOMMANDINFOEX lpcmi, PStaticShellEntry pEntry)
1170 {
1171     CComPtr<IShellBrowser> psb;
1172     HWND hwndTree;
1173     LPCWSTR FlagsName;
1174     WCHAR wszKey[sizeof("shell\\") + MAX_VERB];
1175     HRESULT hr;
1176     DWORD wFlags;
1177     DWORD cbVerb;
1178 
1179     if (!m_site)
1180         return 0;
1181 
1182     /* Get a pointer to the shell browser */
1183     hr = IUnknown_QueryService(m_site, SID_IShellBrowser, IID_PPV_ARG(IShellBrowser, &psb));
1184     if (FAILED(hr))
1185         return 0;
1186 
1187     /* See if we are in Explore or Browse mode. If the browser's tree is present, we are in Explore mode.*/
1188     if (SUCCEEDED(psb->GetControlWindow(FCW_TREE, &hwndTree)) && hwndTree)
1189         FlagsName = L"ExplorerFlags";
1190     else
1191         FlagsName = L"BrowserFlags";
1192 
1193     /* Try to get the flag from the verb */
1194     hr = StringCbPrintfW(wszKey, sizeof(wszKey), L"shell\\%s", pEntry->Verb.GetString());
1195     if (FAILED_UNEXPECTEDLY(hr))
1196         return 0;
1197 
1198     cbVerb = sizeof(wFlags);
1199     if (RegGetValueW(pEntry->hkClass, wszKey, FlagsName, RRF_RT_REG_DWORD, NULL, &wFlags, &cbVerb) == ERROR_SUCCESS)
1200     {
1201         return wFlags;
1202     }
1203 
1204     return 0;
1205 }
1206 
1207 HRESULT
1208 CDefaultContextMenu::TryToBrowse(
1209     LPCMINVOKECOMMANDINFOEX lpcmi, LPCITEMIDLIST pidlChild, DWORD wFlags)
1210 {
1211     CComPtr<IShellBrowser> psb;
1212     HRESULT hr;
1213 
1214     if (!m_site)
1215         return E_FAIL;
1216 
1217     /* Get a pointer to the shell browser */
1218     hr = IUnknown_QueryService(m_site, SID_IShellBrowser, IID_PPV_ARG(IShellBrowser, &psb));
1219     if (FAILED(hr))
1220         return hr;
1221 
1222     PIDLIST_ABSOLUTE pidl;
1223     hr = SHILCombine(m_pidlFolder, pidlChild, &pidl);
1224     if (FAILED_UNEXPECTEDLY(hr))
1225         return hr;
1226 
1227     hr = psb->BrowseObject(pidl, wFlags & ~SBSP_RELATIVE);
1228     ILFree(pidl);
1229     return hr;
1230 }
1231 
1232 HRESULT
1233 CDefaultContextMenu::InvokePidl(LPCMINVOKECOMMANDINFOEX lpcmi, LPCITEMIDLIST pidl, PStaticShellEntry pEntry)
1234 {
1235     LPITEMIDLIST pidlFull = ILCombine(m_pidlFolder, pidl);
1236     if (pidlFull == NULL)
1237     {
1238         return E_FAIL;
1239     }
1240 
1241     WCHAR wszPath[MAX_PATH];
1242     BOOL bHasPath = SHGetPathFromIDListW(pidlFull, wszPath);
1243 
1244     WCHAR wszDir[MAX_PATH];
1245     if (bHasPath)
1246     {
1247         wcscpy(wszDir, wszPath);
1248         PathRemoveFileSpec(wszDir);
1249     }
1250     else
1251     {
1252         if (!SHGetPathFromIDListW(m_pidlFolder, wszDir))
1253             *wszDir = UNICODE_NULL;
1254     }
1255 
1256     SHELLEXECUTEINFOW sei;
1257     ZeroMemory(&sei, sizeof(sei));
1258     sei.cbSize = sizeof(sei);
1259     sei.hwnd = lpcmi->hwnd;
1260     sei.nShow = SW_SHOWNORMAL;
1261     sei.lpVerb = pEntry->Verb;
1262     sei.lpDirectory = wszDir;
1263     sei.lpIDList = pidlFull;
1264     sei.hkeyClass = pEntry->hkClass;
1265     sei.fMask = SEE_MASK_CLASSKEY | SEE_MASK_IDLIST;
1266     if (bHasPath)
1267     {
1268         sei.lpFile = wszPath;
1269     }
1270 
1271     ShellExecuteExW(&sei);
1272 
1273     ILFree(pidlFull);
1274 
1275     return S_OK;
1276 }
1277 
1278 HRESULT
1279 CDefaultContextMenu::InvokeRegVerb(
1280     LPCMINVOKECOMMANDINFOEX lpcmi)
1281 {
1282     INT iCmd = LOWORD(lpcmi->lpVerb);
1283     HRESULT hr;
1284     UINT i;
1285 
1286     POSITION it = m_StaticEntries.FindIndex(iCmd);
1287 
1288     if (it == NULL)
1289         return E_INVALIDARG;
1290 
1291     PStaticShellEntry pEntry = &m_StaticEntries.GetAt(it);
1292 
1293     CRegKey VerbKey;
1294     WCHAR VerbKeyPath[sizeof("shell\\") + MAX_VERB];
1295     hr = StringCbPrintfW(VerbKeyPath, sizeof(VerbKeyPath), L"shell\\%s", pEntry->Verb.GetString());
1296     if (SUCCEEDED(hr) && m_pDataObj &&
1297         VerbKey.Open(pEntry->hkClass, VerbKeyPath, KEY_READ) == ERROR_SUCCESS)
1298     {
1299         CLSID clsid;
1300 
1301         DWORD KeyState = 0;
1302         if (lpcmi->fMask & CMIC_MASK_SHIFT_DOWN)
1303             KeyState |= MK_SHIFT;
1304         if (lpcmi->fMask & CMIC_MASK_CONTROL_DOWN)
1305             KeyState |= MK_CONTROL;
1306 
1307         POINTL *pPtl = NULL;
1308         C_ASSERT(sizeof(POINT) == sizeof(POINTL));
1309         if (lpcmi->fMask & CMIC_MASK_PTINVOKE)
1310             pPtl = (POINTL*)&lpcmi->ptInvoke;
1311 
1312         CComPtr<IExecuteCommand> pEC;
1313         hr = SHELL_GetRegCLSID(VerbKey, L"command", L"DelegateExecute", clsid);
1314         if (SUCCEEDED(hr))
1315             hr = CoCreateInstance(clsid, NULL, CLSCTX_ALL, IID_PPV_ARG(IExecuteCommand, &pEC));
1316         if (SUCCEEDED(hr))
1317         {
1318             CComPtr<IPropertyBag> pPB;
1319             SHCreatePropertyBagOnRegKey(VerbKey, NULL, STGM_READ, IID_PPV_ARG(IPropertyBag, &pPB));
1320             return InvokeIExecuteCommandWithDataObject(pEC, pEntry->Verb.GetString(), pPB, m_pDataObj,
1321                                                        lpcmi, static_cast<IContextMenu*>(this));
1322         }
1323 
1324         CComPtr<IDropTarget> pDT;
1325         hr = SHELL_GetRegCLSID(VerbKey, L"DropTarget", L"CLSID", clsid);
1326         if (SUCCEEDED(hr))
1327             hr = CoCreateInstance(clsid, NULL, CLSCTX_ALL, IID_PPV_ARG(IDropTarget, &pDT));
1328         if (SUCCEEDED(hr))
1329         {
1330             CComPtr<IPropertyBag> pPB;
1331             SHCreatePropertyBagOnRegKey(VerbKey, NULL, STGM_READ, IID_PPV_ARG(IPropertyBag, &pPB));
1332             IUnknown_SetSite(pDT, static_cast<IContextMenu*>(this));
1333             IUnknown_InitializeCommand(pDT, pEntry->Verb.GetString(), pPB);
1334             hr = SHSimulateDrop(pDT, m_pDataObj, KeyState, pPtl, NULL);
1335             IUnknown_SetSite(pDT, NULL);
1336             return hr;
1337         }
1338     }
1339 
1340     /* Get the browse flags to see if we need to browse */
1341     DWORD wFlags = BrowserFlagsFromVerb(lpcmi, pEntry);
1342 
1343     for (i=0; i < m_cidl; i++)
1344     {
1345         /* Check if we need to browse */
1346         if (wFlags)
1347         {
1348             hr = TryToBrowse(lpcmi, m_apidl[i], wFlags);
1349             if (SUCCEEDED(hr))
1350             {
1351                 /* In WinXP if we have browsed, we don't open any more folders.
1352                  * In Win7 we browse to the first folder we find and
1353                  * open new windows for each of the rest of the folders */
1354                 UINT ntver = RosGetProcessEffectiveVersion();
1355                 if (ntver >= _WIN32_WINNT_VISTA)
1356                     wFlags = 0; // FIXME: = SBSP_NEWBROWSER | (wFlags & ~SBSP_SAMEBROWSER);
1357                 else
1358                     i = m_cidl;
1359 
1360                 continue;
1361             }
1362         }
1363 
1364         InvokePidl(lpcmi, m_apidl[i], pEntry);
1365     }
1366 
1367     return S_OK;
1368 }
1369 
1370 HRESULT
1371 WINAPI
1372 CDefaultContextMenu::InvokeCommand(
1373     LPCMINVOKECOMMANDINFO lpcmi)
1374 {
1375     CMINVOKECOMMANDINFOEX LocalInvokeInfo = {};
1376     HRESULT Result;
1377     UINT CmdId;
1378 
1379     /* Take a local copy of the fixed members of the
1380        struct as we might need to modify the verb */
1381     memcpy(&LocalInvokeInfo, lpcmi, min(sizeof(LocalInvokeInfo), lpcmi->cbSize));
1382 
1383     /* Check if this is a string verb */
1384     if (!IS_INTRESOURCE(LocalInvokeInfo.lpVerb))
1385     {
1386         /* Get the ID which corresponds to this verb, and update our local copy */
1387         if (MapVerbToCmdId((LPVOID)LocalInvokeInfo.lpVerb, &CmdId, FALSE))
1388             LocalInvokeInfo.lpVerb = MAKEINTRESOURCEA(CmdId);
1389     }
1390 
1391     CmdId = LOWORD(LocalInvokeInfo.lpVerb);
1392 
1393     if (!m_DynamicEntries.IsEmpty() && CmdId >= m_iIdSHEFirst && CmdId < m_iIdSHELast)
1394     {
1395         LocalInvokeInfo.lpVerb -= m_iIdSHEFirst;
1396         Result = InvokeShellExt(&LocalInvokeInfo);
1397         return Result;
1398     }
1399 
1400     if (!m_StaticEntries.IsEmpty() && CmdId >= m_iIdSCMFirst && CmdId < m_iIdSCMLast)
1401     {
1402         LocalInvokeInfo.lpVerb -= m_iIdSCMFirst;
1403         Result = InvokeRegVerb(&LocalInvokeInfo);
1404         // TODO: if (FAILED(Result) && !(lpcmi->fMask & CMIC_MASK_FLAG_NO_UI)) SHELL_ErrorBox(m_pSite, Result);
1405         return Result;
1406     }
1407 
1408     if (m_iIdCBFirst != m_iIdCBLast && CmdId >= m_iIdCBFirst && CmdId < m_iIdCBLast)
1409     {
1410         Result = _DoCallback(DFM_INVOKECOMMAND, CmdId - m_iIdCBFirst, NULL);
1411         return Result;
1412     }
1413 
1414     if (m_iIdDfltFirst != m_iIdDfltLast && CmdId >= m_iIdDfltFirst && CmdId < m_iIdDfltLast)
1415     {
1416         CmdId -= m_iIdDfltFirst;
1417         /* See the definitions of IDM_CUT and co to see how this works */
1418         CmdId += DCM_FCIDM_SHVIEW_OFFSET;
1419     }
1420 
1421     if (LocalInvokeInfo.cbSize >= sizeof(CMINVOKECOMMANDINFOEX) && (LocalInvokeInfo.fMask & CMIC_MASK_PTINVOKE))
1422     {
1423         if (m_pDataObj && FAILED_UNEXPECTEDLY(DataObject_SetOffset(m_pDataObj, &LocalInvokeInfo.ptInvoke)))
1424         {
1425             ERR("Unable to add OFFSET to DataObject!\n");
1426         }
1427     }
1428 
1429     /* Check if this is a Id */
1430     switch (CmdId)
1431     {
1432     case FCIDM_SHVIEW_INSERT:
1433         Result = DoPaste(&LocalInvokeInfo, FALSE);
1434         break;
1435     case FCIDM_SHVIEW_INSERTLINK:
1436         Result = DoPaste(&LocalInvokeInfo, TRUE);
1437         break;
1438     case FCIDM_SHVIEW_OPEN:
1439     case FCIDM_SHVIEW_EXPLORE:
1440         Result = DoOpenOrExplore(&LocalInvokeInfo);
1441         break;
1442     case FCIDM_SHVIEW_COPY:
1443     case FCIDM_SHVIEW_CUT:
1444         Result = DoCopyOrCut(&LocalInvokeInfo, CmdId == FCIDM_SHVIEW_COPY);
1445         break;
1446     case FCIDM_SHVIEW_CREATELINK:
1447         Result = DoCreateLink(&LocalInvokeInfo);
1448         break;
1449     case FCIDM_SHVIEW_DELETE:
1450         Result = DoDelete(&LocalInvokeInfo);
1451         break;
1452     case FCIDM_SHVIEW_RENAME:
1453         Result = DoRename(&LocalInvokeInfo);
1454         break;
1455     case FCIDM_SHVIEW_PROPERTIES:
1456         Result = DoProperties(&LocalInvokeInfo);
1457         break;
1458     case FCIDM_SHVIEW_NEWFOLDER:
1459         Result = DoCreateNewFolder(&LocalInvokeInfo);
1460         break;
1461     case FCIDM_SHVIEW_COPYTO:
1462         Result = DoCopyToMoveToFolder(&LocalInvokeInfo, TRUE);
1463         break;
1464     case FCIDM_SHVIEW_MOVETO:
1465         Result = DoCopyToMoveToFolder(&LocalInvokeInfo, FALSE);
1466         break;
1467     case FCIDM_SHVIEW_UNDO:
1468         Result = DoUndo(&LocalInvokeInfo);
1469         break;
1470     default:
1471         Result = E_INVALIDARG;
1472         ERR("Unhandled Verb %xl\n", LOWORD(LocalInvokeInfo.lpVerb));
1473         break;
1474     }
1475 
1476     return Result;
1477 }
1478 
1479 HRESULT
1480 WINAPI
1481 CDefaultContextMenu::GetCommandString(
1482     UINT_PTR idCommand,
1483     UINT uFlags,
1484     UINT* lpReserved,
1485     LPSTR lpszName,
1486     UINT uMaxNameLen)
1487 {
1488     /* We don't handle the help text yet */
1489     if (uFlags == GCS_HELPTEXTA ||
1490         uFlags == GCS_HELPTEXTW ||
1491         HIWORD(idCommand) != 0)
1492     {
1493         return E_NOTIMPL;
1494     }
1495 
1496     UINT CmdId = LOWORD(idCommand);
1497 
1498     if (!m_DynamicEntries.IsEmpty() && CmdId >= m_iIdSHEFirst && CmdId < m_iIdSHELast)
1499     {
1500         idCommand -= m_iIdSHEFirst;
1501         PDynamicShellEntry pEntry = GetDynamicEntry(idCommand);
1502         if (!pEntry)
1503             return E_FAIL;
1504 
1505         idCommand -= pEntry->iIdCmdFirst;
1506         return pEntry->pCM->GetCommandString(idCommand,
1507                                              uFlags,
1508                                              lpReserved,
1509                                              lpszName,
1510                                              uMaxNameLen);
1511     }
1512 
1513     if (!m_StaticEntries.IsEmpty() && CmdId >= m_iIdSCMFirst && CmdId < m_iIdSCMLast)
1514     {
1515         /* Validation just returns S_OK on a match. The id exists. */
1516         if (uFlags == GCS_VALIDATEA || uFlags == GCS_VALIDATEW)
1517             return S_OK;
1518 
1519         CmdId -= m_iIdSCMFirst;
1520 
1521         POSITION it = m_StaticEntries.FindIndex(CmdId);
1522 
1523         if (it == NULL)
1524             return E_INVALIDARG;
1525 
1526         PStaticShellEntry pEntry = &m_StaticEntries.GetAt(it);
1527 
1528         if (uFlags == GCS_VERBW)
1529             return StringCchCopyW((LPWSTR)lpszName, uMaxNameLen, pEntry->Verb);
1530 
1531         if (uFlags == GCS_VERBA)
1532         {
1533             if (SHUnicodeToAnsi(pEntry->Verb, lpszName, uMaxNameLen))
1534                 return S_OK;
1535         }
1536 
1537         return E_INVALIDARG;
1538     }
1539 
1540     //FIXME: Should we handle callbacks here?
1541     if (m_iIdDfltFirst != m_iIdDfltLast && CmdId >= m_iIdDfltFirst && CmdId < m_iIdDfltLast)
1542     {
1543         CmdId -= m_iIdDfltFirst;
1544         /* See the definitions of IDM_CUT and co to see how this works */
1545         CmdId += DCM_FCIDM_SHVIEW_OFFSET;
1546     }
1547 
1548     /* Loop looking for a matching Id */
1549     for (UINT i = 0; i < _countof(g_StaticInvokeCmdMap); i++)
1550     {
1551         if (g_StaticInvokeCmdMap[i].IntVerb == CmdId)
1552         {
1553             /* Validation just returns S_OK on a match */
1554             if (uFlags == GCS_VALIDATEA || uFlags == GCS_VALIDATEW)
1555                 return S_OK;
1556 
1557             /* Return a copy of the ANSI verb */
1558             if (uFlags == GCS_VERBA)
1559                 return StringCchCopyA(lpszName, uMaxNameLen, g_StaticInvokeCmdMap[i].szStringVerb);
1560 
1561             /* Convert the ANSI verb to unicode and return that */
1562             if (uFlags == GCS_VERBW)
1563             {
1564                 if (SHAnsiToUnicode(g_StaticInvokeCmdMap[i].szStringVerb, (LPWSTR)lpszName, uMaxNameLen))
1565                     return S_OK;
1566             }
1567         }
1568     }
1569 
1570     return E_INVALIDARG;
1571 }
1572 
1573 HRESULT
1574 WINAPI
1575 CDefaultContextMenu::HandleMenuMsg(
1576     UINT uMsg,
1577     WPARAM wParam,
1578     LPARAM lParam)
1579 {
1580     /* FIXME: Should we implement this as well? */
1581     return S_OK;
1582 }
1583 
1584 HRESULT SHGetMenuIdFromMenuMsg(UINT uMsg, LPARAM lParam, UINT *CmdId)
1585 {
1586     if (uMsg == WM_DRAWITEM)
1587     {
1588         DRAWITEMSTRUCT* pDrawStruct = reinterpret_cast<DRAWITEMSTRUCT*>(lParam);
1589         *CmdId = pDrawStruct->itemID;
1590         return S_OK;
1591     }
1592     else if (uMsg == WM_MEASUREITEM)
1593     {
1594         MEASUREITEMSTRUCT* pMeasureStruct = reinterpret_cast<MEASUREITEMSTRUCT*>(lParam);
1595         *CmdId = pMeasureStruct->itemID;
1596         return S_OK;
1597     }
1598 
1599     return E_FAIL;
1600 }
1601 
1602 HRESULT SHSetMenuIdInMenuMsg(UINT uMsg, LPARAM lParam, UINT CmdId)
1603 {
1604     if (uMsg == WM_DRAWITEM)
1605     {
1606         DRAWITEMSTRUCT* pDrawStruct = reinterpret_cast<DRAWITEMSTRUCT*>(lParam);
1607         pDrawStruct->itemID = CmdId;
1608         return S_OK;
1609     }
1610     else if (uMsg == WM_MEASUREITEM)
1611     {
1612         MEASUREITEMSTRUCT* pMeasureStruct = reinterpret_cast<MEASUREITEMSTRUCT*>(lParam);
1613         pMeasureStruct->itemID = CmdId;
1614         return S_OK;
1615     }
1616 
1617     return E_FAIL;
1618 }
1619 
1620 HRESULT
1621 WINAPI
1622 CDefaultContextMenu::HandleMenuMsg2(
1623     UINT uMsg,
1624     WPARAM wParam,
1625     LPARAM lParam,
1626     LRESULT *plResult)
1627 {
1628     if (uMsg == WM_INITMENUPOPUP)
1629     {
1630         POSITION it = m_DynamicEntries.GetHeadPosition();
1631         while (it != NULL)
1632         {
1633             DynamicShellEntry& info = m_DynamicEntries.GetNext(it);
1634             SHForwardContextMenuMsg(info.pCM, uMsg, wParam, lParam, plResult, TRUE);
1635         }
1636         return S_OK;
1637     }
1638 
1639     UINT CmdId;
1640     HRESULT hr = SHGetMenuIdFromMenuMsg(uMsg, lParam, &CmdId);
1641     if (FAILED(hr))
1642         return S_FALSE;
1643 
1644     if (CmdId < m_iIdSHEFirst || CmdId >= m_iIdSHELast)
1645         return S_FALSE;
1646 
1647     CmdId -= m_iIdSHEFirst;
1648     PDynamicShellEntry pEntry = GetDynamicEntry(CmdId);
1649     if (pEntry)
1650     {
1651         SHSetMenuIdInMenuMsg(uMsg, lParam, CmdId - pEntry->iIdCmdFirst);
1652         SHForwardContextMenuMsg(pEntry->pCM, uMsg, wParam, lParam, plResult, TRUE);
1653     }
1654 
1655    return S_OK;
1656 }
1657 
1658 HRESULT
1659 WINAPI
1660 CDefaultContextMenu::SetSite(IUnknown *pUnkSite)
1661 {
1662     m_site = pUnkSite;
1663     return S_OK;
1664 }
1665 
1666 HRESULT
1667 WINAPI
1668 CDefaultContextMenu::GetSite(REFIID riid, void **ppvSite)
1669 {
1670     if (!m_site)
1671         return E_FAIL;
1672 
1673     return m_site->QueryInterface(riid, ppvSite);
1674 }
1675 
1676 static
1677 HRESULT
1678 CDefaultContextMenu_CreateInstance(const DEFCONTEXTMENU *pdcm, LPFNDFMCALLBACK lpfn, REFIID riid, void **ppv)
1679 {
1680     return ShellObjectCreatorInit<CDefaultContextMenu>(pdcm, lpfn, riid, ppv);
1681 }
1682 
1683 /*************************************************************************
1684  * SHCreateDefaultContextMenu            [SHELL32.325] Vista API
1685  *
1686  */
1687 
1688 HRESULT
1689 WINAPI
1690 SHCreateDefaultContextMenu(const DEFCONTEXTMENU *pdcm, REFIID riid, void **ppv)
1691 {
1692     HRESULT hr;
1693 
1694     if (!ppv)
1695         return E_INVALIDARG;
1696 
1697     hr = CDefaultContextMenu_CreateInstance(pdcm, NULL, riid, ppv);
1698     if (FAILED_UNEXPECTEDLY(hr))
1699         return hr;
1700 
1701     return S_OK;
1702 }
1703 
1704 /*************************************************************************
1705  * CDefFolderMenu_Create2            [SHELL32.701]
1706  *
1707  */
1708 
1709 HRESULT
1710 WINAPI
1711 CDefFolderMenu_Create2(
1712     PCIDLIST_ABSOLUTE pidlFolder,
1713     HWND hwnd,
1714     UINT cidl,
1715     PCUITEMID_CHILD_ARRAY apidl,
1716     IShellFolder *psf,
1717     LPFNDFMCALLBACK lpfn,
1718     UINT nKeys,
1719     const HKEY *ahkeyClsKeys,
1720     IContextMenu **ppcm)
1721 {
1722     DEFCONTEXTMENU dcm;
1723     dcm.hwnd = hwnd;
1724     dcm.pcmcb = NULL;
1725     dcm.pidlFolder = pidlFolder;
1726     dcm.psf = psf;
1727     dcm.cidl = cidl;
1728     dcm.apidl = apidl;
1729     dcm.punkAssociationInfo = NULL;
1730     dcm.cKeys = nKeys;
1731     dcm.aKeys = ahkeyClsKeys;
1732 
1733     HRESULT hr = CDefaultContextMenu_CreateInstance(&dcm, lpfn, IID_PPV_ARG(IContextMenu, ppcm));
1734     if (FAILED_UNEXPECTEDLY(hr))
1735         return hr;
1736 
1737     return S_OK;
1738 }
1739