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(hr))
373     {
374         ERR("SHCoCreateInstance(IContextMenu) failed.clsid %s hr 0x%x\n", wine_dbgstr_guid(pclsid), hr);
375         return hr;
376     }
377 
378     CComPtr<IShellExtInit> pExtInit;
379     hr = pcm->QueryInterface(IID_PPV_ARG(IShellExtInit, &pExtInit));
380     if (FAILED(hr))
381     {
382         ERR("IContextMenu->QueryInterface(IShellExtInit) failed.clsid %s hr 0x%x\n", wine_dbgstr_guid(pclsid), hr);
383         return hr;
384     }
385 
386     hr = pExtInit->Initialize(m_pidlFolder, m_pDataObj, hKey);
387     if (FAILED(hr))
388     {
389         ERR("IShellExtInit::Initialize failed.clsid %s hr 0x%x\n", wine_dbgstr_guid(pclsid), hr);
390         return hr;
391     }
392 
393     if (m_site)
394         IUnknown_SetSite(pcm, m_site);
395 
396     PDynamicShellEntry pEntry = (DynamicShellEntry *)HeapAlloc(GetProcessHeap(), 0, sizeof(DynamicShellEntry));
397     if (!pEntry)
398         return E_OUTOFMEMORY;
399 
400     pEntry->iIdCmdFirst = 0;
401     pEntry->pNext = NULL;
402     pEntry->NumIds = 0;
403     pEntry->pCM = pcm.Detach();
404     memcpy(&pEntry->ClassID, pclsid, sizeof(CLSID));
405 
406     if (m_pDynamicEntries)
407     {
408         PDynamicShellEntry pLastEntry = m_pDynamicEntries;
409 
410         while (pLastEntry->pNext)
411             pLastEntry = pLastEntry->pNext;
412 
413         pLastEntry->pNext = pEntry;
414     }
415     else
416         m_pDynamicEntries = pEntry;
417 
418     return S_OK;
419 }
420 
421 BOOL
422 CDefaultContextMenu::EnumerateDynamicContextHandlerForKey(HKEY hRootKey)
423 {
424     WCHAR wszName[MAX_PATH], wszBuf[MAX_PATH], *pwszClsid;
425     DWORD cchName;
426     HRESULT hr;
427     HKEY hKey;
428 
429     if (RegOpenKeyExW(hRootKey, L"shellex\\ContextMenuHandlers", 0, KEY_READ, &hKey) != ERROR_SUCCESS)
430     {
431         TRACE("RegOpenKeyExW failed\n");
432         return FALSE;
433     }
434 
435     DWORD dwIndex = 0;
436     while (TRUE)
437     {
438         cchName = _countof(wszName);
439         if (RegEnumKeyExW(hKey, dwIndex++, wszName, &cchName, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
440             break;
441 
442         /* Key name or key value is CLSID */
443         CLSID clsid;
444         hr = CLSIDFromString(wszName, &clsid);
445         if (hr == S_OK)
446             pwszClsid = wszName;
447         else
448         {
449             DWORD cchBuf = _countof(wszBuf);
450             if (RegGetValueW(hKey, wszName, NULL, RRF_RT_REG_SZ, NULL, wszBuf, &cchBuf) == ERROR_SUCCESS)
451                 hr = CLSIDFromString(wszBuf, &clsid);
452             pwszClsid = wszBuf;
453         }
454 
455         if (FAILED(hr))
456         {
457             ERR("CLSIDFromString failed for clsid %S hr 0x%x\n", pwszClsid, hr);
458             continue;
459         }
460 
461         if (m_bGroupPolicyActive)
462         {
463             if (RegGetValueW(HKEY_LOCAL_MACHINE,
464                              L"Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved",
465                              pwszClsid,
466                              RRF_RT_REG_SZ,
467                              NULL,
468                              NULL,
469                              NULL) != ERROR_SUCCESS)
470             {
471                 ERR("Shell extension %s not approved!\n", pwszClsid);
472                 continue;
473             }
474         }
475 
476         hr = LoadDynamicContextMenuHandler(hKey, &clsid);
477         if (FAILED(hr))
478             WARN("Failed to get context menu entires from shell extension! clsid: %S\n", pwszClsid);
479     }
480 
481     RegCloseKey(hKey);
482     return TRUE;
483 }
484 
485 UINT
486 CDefaultContextMenu::AddShellExtensionsToMenu(HMENU hMenu, UINT* pIndexMenu, UINT idCmdFirst, UINT idCmdLast)
487 {
488     UINT cIds = 0;
489 
490     if (!m_pDynamicEntries)
491         return cIds;
492 
493     PDynamicShellEntry pEntry = m_pDynamicEntries;
494     do
495     {
496         HRESULT hr = pEntry->pCM->QueryContextMenu(hMenu, *pIndexMenu, idCmdFirst + cIds, idCmdLast, CMF_NORMAL);
497         if (SUCCEEDED(hr))
498         {
499             pEntry->iIdCmdFirst = cIds;
500             pEntry->NumIds = LOWORD(hr);
501             (*pIndexMenu) += pEntry->NumIds;
502 
503             cIds += pEntry->NumIds;
504             if(idCmdFirst + cIds >= idCmdLast)
505                 break;
506         }
507         TRACE("pEntry %p hr %x contextmenu %p cmdfirst %x num ids %x\n", pEntry, hr, pEntry->pCM, pEntry->iIdCmdFirst, pEntry->NumIds);
508         pEntry = pEntry->pNext;
509     } while (pEntry);
510 
511     return cIds;
512 }
513 
514 UINT
515 CDefaultContextMenu::AddStaticContextMenusToMenu(
516     HMENU hMenu,
517     UINT* pIndexMenu,
518     UINT iIdCmdFirst,
519     UINT iIdCmdLast)
520 {
521     MENUITEMINFOW mii;
522     UINT idResource;
523     WCHAR wszVerb[40];
524     UINT fState;
525     UINT cIds = 0;
526 
527     mii.cbSize = sizeof(mii);
528     mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE | MIIM_DATA;
529     mii.fType = MFT_STRING;
530     mii.dwTypeData = NULL;
531 
532     PStaticShellEntry pEntry = m_pStaticEntries;
533 
534     while (pEntry)
535     {
536         fState = MFS_ENABLED;
537         mii.dwTypeData = NULL;
538 
539         /* set first entry as default */
540         if (pEntry == m_pStaticEntries)
541             fState |= MFS_DEFAULT;
542 
543         if (!wcsicmp(pEntry->szVerb, L"open"))
544         {
545             /* override default when open verb is found */
546             fState |= MFS_DEFAULT;
547             idResource = IDS_OPEN_VERB;
548         }
549         else if (!wcsicmp(pEntry->szVerb, L"explore"))
550             idResource = IDS_EXPLORE_VERB;
551         else if (!wcsicmp(pEntry->szVerb, L"runas"))
552             idResource = IDS_RUNAS_VERB;
553         else if (!wcsicmp(pEntry->szVerb, L"edit"))
554             idResource = IDS_EDIT_VERB;
555         else if (!wcsicmp(pEntry->szVerb, L"find"))
556             idResource = IDS_FIND_VERB;
557         else if (!wcsicmp(pEntry->szVerb, L"print"))
558             idResource = IDS_PRINT_VERB;
559         else if (!wcsicmp(pEntry->szVerb, L"printto"))
560         {
561             pEntry = pEntry->pNext;
562             continue;
563         }
564         else
565             idResource = 0;
566 
567         /* By default use verb for menu item name */
568         mii.dwTypeData = pEntry->szVerb;
569 
570         if (idResource > 0)
571         {
572             if (LoadStringW(shell32_hInstance, idResource, wszVerb, _countof(wszVerb)))
573                 mii.dwTypeData = wszVerb; /* use translated verb */
574             else
575                 ERR("Failed to load string\n");
576         }
577         else
578         {
579             WCHAR wszKey[256];
580             HRESULT hr = StringCbPrintfW(wszKey, sizeof(wszKey), L"shell\\%s", pEntry->szVerb);
581 
582             if (SUCCEEDED(hr))
583             {
584                 HKEY hkVerb;
585                 DWORD cbVerb = sizeof(wszVerb);
586                 LONG res = RegOpenKeyW(pEntry->hkClass, wszKey, &hkVerb);
587                 if (res == ERROR_SUCCESS)
588                 {
589                     res = RegLoadMUIStringW(hkVerb,
590                                             NULL,
591                                             wszVerb,
592                                             cbVerb,
593                                             NULL,
594                                             0,
595                                             NULL);
596                     if (res == ERROR_SUCCESS)
597                     {
598                         /* use description for the menu entry */
599                         mii.dwTypeData = wszVerb;
600                     }
601 
602                     RegCloseKey(hkVerb);
603                 }
604             }
605         }
606 
607         mii.cch = wcslen(mii.dwTypeData);
608         mii.fState = fState;
609         mii.wID = iIdCmdFirst + cIds;
610         InsertMenuItemW(hMenu, *pIndexMenu, TRUE, &mii);
611         (*pIndexMenu)++;
612         cIds++;
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     if (!bCopy)
844     {
845         FORMATETC formatetc;
846         STGMEDIUM medium;
847         InitFormatEtc(formatetc, RegisterClipboardFormatW(CFSTR_PREFERREDDROPEFFECT), TYMED_HGLOBAL);
848         m_pDataObj->GetData(&formatetc, &medium);
849         DWORD * pdwFlag = (DWORD*)GlobalLock(medium.hGlobal);
850         if (pdwFlag)
851             *pdwFlag = DROPEFFECT_MOVE;
852         GlobalUnlock(medium.hGlobal);
853         m_pDataObj->SetData(&formatetc, &medium, TRUE);
854     }
855 
856     HRESULT hr = OleSetClipboard(m_pDataObj);
857     if (FAILED_UNEXPECTEDLY(hr))
858         return hr;
859 
860     return S_OK;
861 }
862 
863 HRESULT CDefaultContextMenu::DoRename(LPCMINVOKECOMMANDINFO lpcmi)
864 {
865     CComPtr<IShellBrowser> psb;
866     HRESULT hr;
867 
868     if (!m_site || !m_cidl)
869         return E_FAIL;
870 
871     /* Get a pointer to the shell browser */
872     hr = IUnknown_QueryService(m_site, SID_IShellBrowser, IID_PPV_ARG(IShellBrowser, &psb));
873     if (FAILED_UNEXPECTEDLY(hr))
874         return hr;
875 
876     CComPtr<IShellView> lpSV;
877     hr = psb->QueryActiveShellView(&lpSV);
878     if (FAILED_UNEXPECTEDLY(hr))
879         return hr;
880 
881     SVSIF selFlags = SVSI_DESELECTOTHERS | SVSI_EDIT | SVSI_ENSUREVISIBLE | SVSI_FOCUSED | SVSI_SELECT;
882     hr = lpSV->SelectItem(m_apidl[0], selFlags);
883     if (FAILED_UNEXPECTEDLY(hr))
884         return hr;
885 
886     return S_OK;
887 }
888 
889 HRESULT
890 CDefaultContextMenu::DoProperties(
891     LPCMINVOKECOMMANDINFO lpcmi)
892 {
893     _DoCallback(DFM_INVOKECOMMAND, DFM_CMD_PROPERTIES, NULL);
894 
895     return S_OK;
896 }
897 
898 // This code is taken from CNewMenu and should be shared between the 2 classes
899 HRESULT
900 CDefaultContextMenu::DoCreateNewFolder(
901     LPCMINVOKECOMMANDINFO lpici)
902 {
903     WCHAR wszPath[MAX_PATH];
904     WCHAR wszName[MAX_PATH];
905     WCHAR wszNewFolder[25];
906     HRESULT hr;
907 
908     /* Get folder path */
909     hr = SHGetPathFromIDListW(m_pidlFolder, wszPath);
910     if (FAILED_UNEXPECTEDLY(hr))
911         return hr;
912 
913     if (!LoadStringW(shell32_hInstance, IDS_NEWFOLDER, wszNewFolder, _countof(wszNewFolder)))
914         return E_FAIL;
915 
916     /* Create the name of the new directory */
917     if (!PathYetAnotherMakeUniqueName(wszName, wszPath, NULL, wszNewFolder))
918         return E_FAIL;
919 
920     /* Create the new directory and show the appropriate dialog in case of error */
921     if (SHCreateDirectory(lpici->hwnd, wszName) != ERROR_SUCCESS)
922         return E_FAIL;
923 
924     /* Show and select the new item in the def view */
925     LPITEMIDLIST pidl;
926     PITEMID_CHILD pidlNewItem;
927     CComPtr<IShellView> psv;
928 
929     /* Notify the view object about the new item */
930     SHChangeNotify(SHCNE_MKDIR, SHCNF_PATHW, (LPCVOID)wszName, NULL);
931 
932     if (!m_site)
933         return S_OK;
934 
935     /* Get a pointer to the shell view */
936     hr = IUnknown_QueryService(m_site, SID_IFolderView, IID_PPV_ARG(IShellView, &psv));
937     if (FAILED_UNEXPECTEDLY(hr))
938         return S_OK;
939 
940     /* Attempt to get the pidl of the new item */
941     hr = SHILCreateFromPathW(wszName, &pidl, NULL);
942     if (FAILED_UNEXPECTEDLY(hr))
943         return hr;
944 
945     pidlNewItem = ILFindLastID(pidl);
946 
947     hr = psv->SelectItem(pidlNewItem, SVSI_DESELECTOTHERS | SVSI_EDIT | SVSI_ENSUREVISIBLE |
948                           SVSI_FOCUSED | SVSI_SELECT);
949     if (FAILED_UNEXPECTEDLY(hr))
950         return hr;
951 
952     SHFree(pidl);
953 
954     return S_OK;
955 }
956 
957 PDynamicShellEntry CDefaultContextMenu::GetDynamicEntry(UINT idCmd)
958 {
959     PDynamicShellEntry pEntry = m_pDynamicEntries;
960 
961     while(pEntry && idCmd >= pEntry->iIdCmdFirst + pEntry->NumIds)
962         pEntry = pEntry->pNext;
963 
964     if (!pEntry)
965         return NULL;
966 
967     if (idCmd < pEntry->iIdCmdFirst || idCmd > pEntry->iIdCmdFirst + pEntry->NumIds)
968         return NULL;
969 
970     return pEntry;
971 }
972 
973 // FIXME: 260 is correct, but should this be part of the SDK or just MAX_PATH?
974 #define MAX_VERB 260
975 
976 BOOL
977 CDefaultContextMenu::MapVerbToCmdId(PVOID Verb, PUINT idCmd, BOOL IsUnicode)
978 {
979     WCHAR UnicodeStr[MAX_VERB];
980 
981     /* Loop through all the static verbs looking for a match */
982     for (UINT i = 0; i < _countof(g_StaticInvokeCmdMap); i++)
983     {
984         /* We can match both ANSI and unicode strings */
985         if (IsUnicode)
986         {
987             /* The static verbs are ANSI, get a unicode version before doing the compare */
988             SHAnsiToUnicode(g_StaticInvokeCmdMap[i].szStringVerb, UnicodeStr, MAX_VERB);
989             if (!wcscmp(UnicodeStr, (LPWSTR)Verb))
990             {
991                 /* Return the Corresponding Id */
992                 *idCmd = g_StaticInvokeCmdMap[i].IntVerb;
993                 return TRUE;
994             }
995         }
996         else
997         {
998             if (!strcmp(g_StaticInvokeCmdMap[i].szStringVerb, (LPSTR)Verb))
999             {
1000                 *idCmd = g_StaticInvokeCmdMap[i].IntVerb;
1001                 return TRUE;
1002             }
1003         }
1004     }
1005 
1006     return FALSE;
1007 }
1008 
1009 HRESULT
1010 CDefaultContextMenu::InvokeShellExt(
1011     LPCMINVOKECOMMANDINFO lpcmi)
1012 {
1013     TRACE("verb %p first %x last %x\n", lpcmi->lpVerb, m_iIdSHEFirst, m_iIdSHELast);
1014 
1015     UINT idCmd = LOWORD(lpcmi->lpVerb);
1016     PDynamicShellEntry pEntry = GetDynamicEntry(idCmd);
1017     if (!pEntry)
1018         return E_FAIL;
1019 
1020     /* invoke the dynamic context menu */
1021     lpcmi->lpVerb = MAKEINTRESOURCEA(idCmd - pEntry->iIdCmdFirst);
1022     return pEntry->pCM->InvokeCommand(lpcmi);
1023 }
1024 
1025 DWORD
1026 CDefaultContextMenu::BrowserFlagsFromVerb(LPCMINVOKECOMMANDINFO lpcmi, PStaticShellEntry pEntry)
1027 {
1028     CComPtr<IShellBrowser> psb;
1029     HWND hwndTree;
1030     LPCWSTR FlagsName;
1031     WCHAR wszKey[256];
1032     HRESULT hr;
1033     DWORD wFlags;
1034     DWORD cbVerb;
1035 
1036     if (!m_site)
1037         return 0;
1038 
1039     /* Get a pointer to the shell browser */
1040     hr = IUnknown_QueryService(m_site, SID_IShellBrowser, IID_PPV_ARG(IShellBrowser, &psb));
1041     if (FAILED_UNEXPECTEDLY(hr))
1042         return 0;
1043 
1044     /* See if we are in Explore or Browse mode. If the browser's tree is present, we are in Explore mode.*/
1045     if (SUCCEEDED(psb->GetControlWindow(FCW_TREE, &hwndTree)) && hwndTree)
1046         FlagsName = L"ExplorerFlags";
1047     else
1048         FlagsName = L"BrowserFlags";
1049 
1050     /* Try to get the flag from the verb */
1051     hr = StringCbPrintfW(wszKey, sizeof(wszKey), L"shell\\%s", pEntry->szVerb);
1052     if (FAILED_UNEXPECTEDLY(hr))
1053         return 0;
1054 
1055     cbVerb = sizeof(wFlags);
1056     if (RegGetValueW(pEntry->hkClass, wszKey, FlagsName, RRF_RT_REG_DWORD, NULL, &wFlags, &cbVerb) == ERROR_SUCCESS)
1057     {
1058         return wFlags;
1059     }
1060 
1061     return 0;
1062 }
1063 
1064 HRESULT
1065 CDefaultContextMenu::TryToBrowse(
1066     LPCMINVOKECOMMANDINFO lpcmi, LPCITEMIDLIST pidl, DWORD wFlags)
1067 {
1068     CComPtr<IShellBrowser> psb;
1069     HRESULT hr;
1070 
1071     if (!m_site)
1072         return E_FAIL;
1073 
1074     /* Get a pointer to the shell browser */
1075     hr = IUnknown_QueryService(m_site, SID_IShellBrowser, IID_PPV_ARG(IShellBrowser, &psb));
1076     if (FAILED_UNEXPECTEDLY(hr))
1077         return 0;
1078 
1079     return psb->BrowseObject(ILCombine(m_pidlFolder, pidl), wFlags);
1080 }
1081 
1082 HRESULT
1083 CDefaultContextMenu::InvokePidl(LPCMINVOKECOMMANDINFO lpcmi, LPCITEMIDLIST pidl, PStaticShellEntry pEntry)
1084 {
1085     LPITEMIDLIST pidlFull = ILCombine(m_pidlFolder, pidl);
1086     if (pidlFull == NULL)
1087     {
1088         return E_FAIL;
1089     }
1090 
1091     WCHAR wszPath[MAX_PATH];
1092     BOOL bHasPath = SHGetPathFromIDListW(pidlFull, wszPath);
1093 
1094     WCHAR wszDir[MAX_PATH];
1095     if (bHasPath)
1096     {
1097         wcscpy(wszDir, wszPath);
1098         PathRemoveFileSpec(wszDir);
1099     }
1100     else
1101     {
1102         SHGetPathFromIDListW(m_pidlFolder, wszDir);
1103     }
1104 
1105     SHELLEXECUTEINFOW sei;
1106     ZeroMemory(&sei, sizeof(sei));
1107     sei.cbSize = sizeof(sei);
1108     sei.hwnd = lpcmi->hwnd;
1109     sei.nShow = SW_SHOWNORMAL;
1110     sei.lpVerb = pEntry->szVerb;
1111     sei.lpDirectory = wszDir;
1112     sei.lpIDList = pidlFull;
1113     sei.hkeyClass = pEntry->hkClass;
1114     sei.fMask = SEE_MASK_CLASSKEY | SEE_MASK_IDLIST;
1115     if (bHasPath)
1116     {
1117         sei.lpFile = wszPath;
1118     }
1119 
1120     ShellExecuteExW(&sei);
1121 
1122     ILFree(pidlFull);
1123 
1124     return S_OK;
1125 }
1126 
1127 HRESULT
1128 CDefaultContextMenu::InvokeRegVerb(
1129     LPCMINVOKECOMMANDINFO lpcmi)
1130 {
1131     PStaticShellEntry pEntry = m_pStaticEntries;
1132     INT iCmd = LOWORD(lpcmi->lpVerb) - m_iIdSCMFirst;
1133     HRESULT hr;
1134     UINT i;
1135 
1136     while (pEntry && (iCmd--) > 0)
1137         pEntry = pEntry->pNext;
1138 
1139     if (iCmd > 0)
1140         return E_FAIL;
1141 
1142     /* Get the browse flags to see if we need to browse */
1143     DWORD wFlags = BrowserFlagsFromVerb(lpcmi, pEntry);
1144     BOOL bBrowsed = FALSE;
1145 
1146     for (i=0; i < m_cidl; i++)
1147     {
1148         /* Check if we need to browse */
1149         if (wFlags > 0)
1150         {
1151             /* In xp if we have browsed, we don't open any more folders.
1152              * In win7 we browse to the first folder we find and
1153              * open new windows for each of the rest of the folders */
1154             if (bBrowsed)
1155                 continue;
1156 
1157             hr = TryToBrowse(lpcmi, m_apidl[i], wFlags);
1158             if (SUCCEEDED(hr))
1159             {
1160                 bBrowsed = TRUE;
1161                 continue;
1162             }
1163         }
1164 
1165         InvokePidl(lpcmi, m_apidl[i], pEntry);
1166     }
1167 
1168     return S_OK;
1169 }
1170 
1171 HRESULT
1172 WINAPI
1173 CDefaultContextMenu::InvokeCommand(
1174     LPCMINVOKECOMMANDINFO lpcmi)
1175 {
1176     CMINVOKECOMMANDINFO LocalInvokeInfo;
1177     HRESULT Result;
1178     UINT CmdId;
1179 
1180     /* Take a local copy of the fixed members of the
1181        struct as we might need to modify the verb */
1182     LocalInvokeInfo = *lpcmi;
1183 
1184     /* Check if this is a string verb */
1185     if (HIWORD(LocalInvokeInfo.lpVerb))
1186     {
1187         /* Get the ID which corresponds to this verb, and update our local copy */
1188         if (MapVerbToCmdId((LPVOID)LocalInvokeInfo.lpVerb, &CmdId, FALSE))
1189             LocalInvokeInfo.lpVerb = MAKEINTRESOURCEA(CmdId);
1190     }
1191 
1192     CmdId = LOWORD(LocalInvokeInfo.lpVerb);
1193 
1194     if (m_pDynamicEntries && CmdId >= m_iIdSHEFirst && CmdId < m_iIdSHELast)
1195     {
1196         LocalInvokeInfo.lpVerb -= m_iIdSHEFirst;
1197         Result = InvokeShellExt(&LocalInvokeInfo);
1198         return Result;
1199     }
1200 
1201     if (m_pStaticEntries && CmdId >= m_iIdSCMFirst && CmdId < m_iIdSCMLast)
1202     {
1203         LocalInvokeInfo.lpVerb -= m_iIdSCMFirst;
1204         Result = InvokeRegVerb(&LocalInvokeInfo);
1205         return Result;
1206     }
1207 
1208     if (m_iIdCBFirst != m_iIdCBLast && CmdId >= m_iIdCBFirst && CmdId < m_iIdCBLast)
1209     {
1210         Result = _DoCallback(DFM_INVOKECOMMAND, CmdId - m_iIdCBFirst, NULL);
1211         return Result;
1212     }
1213 
1214     if (m_iIdDfltFirst != m_iIdDfltLast && CmdId >= m_iIdDfltFirst && CmdId < m_iIdDfltLast)
1215     {
1216         CmdId -= m_iIdDfltFirst;
1217         /* See the definitions of IDM_CUT and co to see how this works */
1218         CmdId += 0x7000;
1219     }
1220 
1221     /* Check if this is a Id */
1222     switch (CmdId)
1223     {
1224     case FCIDM_SHVIEW_INSERT:
1225         Result = DoPaste(&LocalInvokeInfo, FALSE);
1226         break;
1227     case FCIDM_SHVIEW_INSERTLINK:
1228         Result = DoPaste(&LocalInvokeInfo, TRUE);
1229         break;
1230     case FCIDM_SHVIEW_OPEN:
1231     case FCIDM_SHVIEW_EXPLORE:
1232         Result = DoOpenOrExplore(&LocalInvokeInfo);
1233         break;
1234     case FCIDM_SHVIEW_COPY:
1235     case FCIDM_SHVIEW_CUT:
1236         Result = DoCopyOrCut(&LocalInvokeInfo, CmdId == FCIDM_SHVIEW_COPY);
1237         break;
1238     case FCIDM_SHVIEW_CREATELINK:
1239         Result = DoCreateLink(&LocalInvokeInfo);
1240         break;
1241     case FCIDM_SHVIEW_DELETE:
1242         Result = DoDelete(&LocalInvokeInfo);
1243         break;
1244     case FCIDM_SHVIEW_RENAME:
1245         Result = DoRename(&LocalInvokeInfo);
1246         break;
1247     case FCIDM_SHVIEW_PROPERTIES:
1248         Result = DoProperties(&LocalInvokeInfo);
1249         break;
1250     case FCIDM_SHVIEW_NEWFOLDER:
1251         Result = DoCreateNewFolder(&LocalInvokeInfo);
1252         break;
1253     default:
1254         Result = E_INVALIDARG;
1255         ERR("Unhandled Verb %xl\n", LOWORD(LocalInvokeInfo.lpVerb));
1256         break;
1257     }
1258 
1259     return Result;
1260 }
1261 
1262 HRESULT
1263 WINAPI
1264 CDefaultContextMenu::GetCommandString(
1265     UINT_PTR idCommand,
1266     UINT uFlags,
1267     UINT* lpReserved,
1268     LPSTR lpszName,
1269     UINT uMaxNameLen)
1270 {
1271     /* We don't handle the help text yet */
1272     if (uFlags == GCS_HELPTEXTA ||
1273         uFlags == GCS_HELPTEXTW ||
1274         HIWORD(idCommand) != 0)
1275     {
1276         return E_NOTIMPL;
1277     }
1278 
1279     UINT CmdId = LOWORD(idCommand);
1280 
1281     if (m_pDynamicEntries && CmdId >= m_iIdSHEFirst && CmdId < m_iIdSHELast)
1282     {
1283         idCommand -= m_iIdSHEFirst;
1284         PDynamicShellEntry pEntry = GetDynamicEntry(idCommand);
1285         if (!pEntry)
1286             return E_FAIL;
1287 
1288         idCommand -= pEntry->iIdCmdFirst;
1289         return pEntry->pCM->GetCommandString(idCommand,
1290                                              uFlags,
1291                                              lpReserved,
1292                                              lpszName,
1293                                              uMaxNameLen);
1294     }
1295 
1296     if (m_pStaticEntries && CmdId >= m_iIdSCMFirst && CmdId < m_iIdSCMLast)
1297     {
1298         /* Validation just returns S_OK on a match. The id exists. */
1299         if (uFlags == GCS_VALIDATEA || uFlags == GCS_VALIDATEW)
1300             return S_OK;
1301 
1302         CmdId -= m_iIdSCMFirst;
1303 
1304         PStaticShellEntry pEntry = m_pStaticEntries;
1305         while (pEntry && (CmdId--) > 0)
1306             pEntry = pEntry->pNext;
1307 
1308         if (!pEntry)
1309             return E_INVALIDARG;
1310 
1311         if (uFlags == GCS_VERBW)
1312             return StringCchCopyW((LPWSTR)lpszName, uMaxNameLen, pEntry->szVerb);
1313 
1314         if (uFlags == GCS_VERBA)
1315         {
1316             if (SHUnicodeToAnsi(pEntry->szVerb, lpszName, uMaxNameLen))
1317                 return S_OK;
1318         }
1319 
1320         return E_INVALIDARG;
1321     }
1322 
1323     //FIXME: Should we handle callbacks here?
1324     if (m_iIdDfltFirst != m_iIdDfltLast && CmdId >= m_iIdDfltFirst && CmdId < m_iIdDfltLast)
1325     {
1326         CmdId -= m_iIdDfltFirst;
1327         /* See the definitions of IDM_CUT and co to see how this works */
1328         CmdId += 0x7000;
1329     }
1330 
1331     /* Loop looking for a matching Id */
1332     for (UINT i = 0; i < _countof(g_StaticInvokeCmdMap); i++)
1333     {
1334         if (g_StaticInvokeCmdMap[i].IntVerb == CmdId)
1335         {
1336             /* Validation just returns S_OK on a match */
1337             if (uFlags == GCS_VALIDATEA || uFlags == GCS_VALIDATEW)
1338                 return S_OK;
1339 
1340             /* Return a copy of the ANSI verb */
1341             if (uFlags == GCS_VERBA)
1342                 return StringCchCopyA(lpszName, uMaxNameLen, g_StaticInvokeCmdMap[i].szStringVerb);
1343 
1344             /* Convert the ANSI verb to unicode and return that */
1345             if (uFlags == GCS_VERBW)
1346             {
1347                 if (SHAnsiToUnicode(g_StaticInvokeCmdMap[i].szStringVerb, (LPWSTR)lpszName, uMaxNameLen))
1348                     return S_OK;
1349             }
1350         }
1351     }
1352 
1353     return E_INVALIDARG;
1354 }
1355 
1356 HRESULT
1357 WINAPI
1358 CDefaultContextMenu::HandleMenuMsg(
1359     UINT uMsg,
1360     WPARAM wParam,
1361     LPARAM lParam)
1362 {
1363     /* FIXME: Should we implement this as well? */
1364     return S_OK;
1365 }
1366 
1367 HRESULT SHGetMenuIdFromMenuMsg(UINT uMsg, LPARAM lParam, UINT *CmdId)
1368 {
1369     if (uMsg == WM_DRAWITEM)
1370     {
1371         DRAWITEMSTRUCT* pDrawStruct = reinterpret_cast<DRAWITEMSTRUCT*>(lParam);
1372         *CmdId = pDrawStruct->itemID;
1373         return S_OK;
1374     }
1375     else if (uMsg == WM_MEASUREITEM)
1376     {
1377         MEASUREITEMSTRUCT* pMeasureStruct = reinterpret_cast<MEASUREITEMSTRUCT*>(lParam);
1378         *CmdId = pMeasureStruct->itemID;
1379         return S_OK;
1380     }
1381 
1382     return E_FAIL;
1383 }
1384 
1385 HRESULT SHSetMenuIdInMenuMsg(UINT uMsg, LPARAM lParam, UINT CmdId)
1386 {
1387     if (uMsg == WM_DRAWITEM)
1388     {
1389         DRAWITEMSTRUCT* pDrawStruct = reinterpret_cast<DRAWITEMSTRUCT*>(lParam);
1390         pDrawStruct->itemID = CmdId;
1391         return S_OK;
1392     }
1393     else if (uMsg == WM_MEASUREITEM)
1394     {
1395         MEASUREITEMSTRUCT* pMeasureStruct = reinterpret_cast<MEASUREITEMSTRUCT*>(lParam);
1396         pMeasureStruct->itemID = CmdId;
1397         return S_OK;
1398     }
1399 
1400     return E_FAIL;
1401 }
1402 
1403 HRESULT
1404 WINAPI
1405 CDefaultContextMenu::HandleMenuMsg2(
1406     UINT uMsg,
1407     WPARAM wParam,
1408     LPARAM lParam,
1409     LRESULT *plResult)
1410 {
1411     if (uMsg == WM_INITMENUPOPUP)
1412     {
1413         PDynamicShellEntry pEntry = m_pDynamicEntries;
1414         while (pEntry)
1415         {
1416             SHForwardContextMenuMsg(pEntry->pCM, uMsg, wParam, lParam, plResult, TRUE);
1417             pEntry = pEntry->pNext;
1418         }
1419         return S_OK;
1420     }
1421 
1422     UINT CmdId;
1423     HRESULT hr = SHGetMenuIdFromMenuMsg(uMsg, lParam, &CmdId);
1424     if (FAILED(hr))
1425         return S_FALSE;
1426 
1427     if (CmdId < m_iIdSHEFirst || CmdId >= m_iIdSHELast)
1428         return S_FALSE;
1429 
1430     CmdId -= m_iIdSHEFirst;
1431     PDynamicShellEntry pEntry = GetDynamicEntry(CmdId);
1432     if (pEntry)
1433     {
1434         SHSetMenuIdInMenuMsg(uMsg, lParam, CmdId - pEntry->iIdCmdFirst);
1435         SHForwardContextMenuMsg(pEntry->pCM, uMsg, wParam, lParam, plResult, TRUE);
1436     }
1437 
1438    return S_OK;
1439 }
1440 
1441 HRESULT
1442 WINAPI
1443 CDefaultContextMenu::SetSite(IUnknown *pUnkSite)
1444 {
1445     m_site = pUnkSite;
1446     return S_OK;
1447 }
1448 
1449 HRESULT
1450 WINAPI
1451 CDefaultContextMenu::GetSite(REFIID riid, void **ppvSite)
1452 {
1453     if (!m_site)
1454         return E_FAIL;
1455 
1456     return m_site->QueryInterface(riid, ppvSite);
1457 }
1458 
1459 static
1460 HRESULT
1461 CDefaultContextMenu_CreateInstance(const DEFCONTEXTMENU *pdcm, LPFNDFMCALLBACK lpfn, REFIID riid, void **ppv)
1462 {
1463     return ShellObjectCreatorInit<CDefaultContextMenu>(pdcm, lpfn, riid, ppv);
1464 }
1465 
1466 /*************************************************************************
1467  * SHCreateDefaultContextMenu            [SHELL32.325] Vista API
1468  *
1469  */
1470 
1471 HRESULT
1472 WINAPI
1473 SHCreateDefaultContextMenu(const DEFCONTEXTMENU *pdcm, REFIID riid, void **ppv)
1474 {
1475     HRESULT hr;
1476 
1477     if (!ppv)
1478         return E_INVALIDARG;
1479 
1480     hr = CDefaultContextMenu_CreateInstance(pdcm, NULL, riid, ppv);
1481     if (FAILED_UNEXPECTEDLY(hr))
1482         return hr;
1483 
1484     return S_OK;
1485 }
1486 
1487 /*************************************************************************
1488  * CDefFolderMenu_Create2            [SHELL32.701]
1489  *
1490  */
1491 
1492 HRESULT
1493 WINAPI
1494 CDefFolderMenu_Create2(
1495     PCIDLIST_ABSOLUTE pidlFolder,
1496     HWND hwnd,
1497     UINT cidl,
1498     PCUITEMID_CHILD_ARRAY apidl,
1499     IShellFolder *psf,
1500     LPFNDFMCALLBACK lpfn,
1501     UINT nKeys,
1502     const HKEY *ahkeyClsKeys,
1503     IContextMenu **ppcm)
1504 {
1505     DEFCONTEXTMENU dcm;
1506     dcm.hwnd = hwnd;
1507     dcm.pcmcb = NULL;
1508     dcm.pidlFolder = pidlFolder;
1509     dcm.psf = psf;
1510     dcm.cidl = cidl;
1511     dcm.apidl = apidl;
1512     dcm.punkAssociationInfo = NULL;
1513     dcm.cKeys = nKeys;
1514     dcm.aKeys = ahkeyClsKeys;
1515 
1516     HRESULT hr = CDefaultContextMenu_CreateInstance(&dcm, lpfn, IID_PPV_ARG(IContextMenu, ppcm));
1517     if (FAILED_UNEXPECTEDLY(hr))
1518         return hr;
1519 
1520     return S_OK;
1521 }
1522