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