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