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