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