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