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