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