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