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 #include <compat_undoc.h>
11
12 WINE_DEFAULT_DEBUG_CHANNEL(dmenu);
13
14 // FIXME: 260 is correct, but should this be part of the SDK or just MAX_PATH?
15 #define MAX_VERB 260
16
17 static HRESULT
SHELL_GetRegCLSID(HKEY hKey,LPCWSTR SubKey,LPCWSTR Value,CLSID & clsid)18 SHELL_GetRegCLSID(HKEY hKey, LPCWSTR SubKey, LPCWSTR Value, CLSID &clsid)
19 {
20 WCHAR buf[42];
21 DWORD cb = sizeof(buf);
22 DWORD err = RegGetValueW(hKey, SubKey, Value, RRF_RT_REG_SZ, NULL, buf, &cb);
23 return !err ? CLSIDFromString(buf, &clsid) : HRESULT_FROM_WIN32(err);
24 }
25
InsertMenuItemAt(HMENU hMenu,UINT Pos,UINT Flags)26 static BOOL InsertMenuItemAt(HMENU hMenu, UINT Pos, UINT Flags)
27 {
28 MENUITEMINFOW mii;
29 mii.cbSize = FIELD_OFFSET(MENUITEMINFOW, hbmpItem); // USER32 version agnostic
30 mii.fMask = MIIM_TYPE;
31 mii.fType = Flags;
32 return InsertMenuItemW(hMenu, Pos, TRUE, &mii);
33 }
34
35 typedef struct _DynamicShellEntry_
36 {
37 UINT iIdCmdFirst;
38 UINT NumIds;
39 CLSID ClassID;
40 CComPtr<IContextMenu> pCM;
41 } DynamicShellEntry, *PDynamicShellEntry;
42
43 typedef struct _StaticShellEntry_
44 {
45 CStringW Verb;
46 HKEY hkClass;
47 } StaticShellEntry, *PStaticShellEntry;
48
49 #define DCM_FCIDM_SHVIEW_OFFSET 0x7000 // Offset from the menu ids in the menu resource to FCIDM_SHVIEW_*
50
51 //
52 // verbs for InvokeCommandInfo
53 //
54 static const struct _StaticInvokeCommandMap_
55 {
56 LPCSTR szStringVerb;
57 WORD IntVerb;
58 SHORT DfmCmd;
59 } g_StaticInvokeCmdMap[] =
60 {
61 { "RunAs", 0 }, // Unimplemented
62 { "Print", 0 }, // Unimplemented
63 { "Preview", 0 }, // Unimplemented
64 { "Open", FCIDM_SHVIEW_OPEN },
65 { CMDSTR_NEWFOLDERA, FCIDM_SHVIEW_NEWFOLDER, (SHORT)DFM_CMD_NEWFOLDER },
66 { "cut", FCIDM_SHVIEW_CUT, /* ? */ },
67 { "copy", FCIDM_SHVIEW_COPY, (SHORT)DFM_CMD_COPY },
68 { "paste", FCIDM_SHVIEW_INSERT, (SHORT)DFM_CMD_PASTE },
69 { "link", FCIDM_SHVIEW_CREATELINK, (SHORT)DFM_CMD_LINK },
70 { "delete", FCIDM_SHVIEW_DELETE, (SHORT)DFM_CMD_DELETE },
71 { "properties", FCIDM_SHVIEW_PROPERTIES, (SHORT)DFM_CMD_PROPERTIES },
72 { "rename", FCIDM_SHVIEW_RENAME, (SHORT)DFM_CMD_RENAME },
73 { "copyto", FCIDM_SHVIEW_COPYTO },
74 { "moveto", FCIDM_SHVIEW_MOVETO },
75 };
76
MapVerbToDfmCmd(_In_ LPCSTR verba)77 UINT MapVerbToDfmCmd(_In_ LPCSTR verba)
78 {
79 for (UINT i = 0; i < _countof(g_StaticInvokeCmdMap); ++i)
80 {
81 if (!lstrcmpiA(g_StaticInvokeCmdMap[i].szStringVerb, verba))
82 return (int)g_StaticInvokeCmdMap[i].DfmCmd;
83 }
84 return 0;
85 }
86
87 static HRESULT
MapVerbToCmdId(PVOID Verb,BOOL IsUnicode,IContextMenu * pCM,UINT idFirst,UINT idLast)88 MapVerbToCmdId(PVOID Verb, BOOL IsUnicode, IContextMenu *pCM, UINT idFirst, UINT idLast)
89 {
90 const UINT gcs = IsUnicode ? GCS_VERBW : GCS_VERBA;
91 for (UINT id = idFirst; id <= idLast; ++id)
92 {
93 WCHAR buf[MAX_PATH];
94 *buf = UNICODE_NULL;
95 HRESULT hr = pCM->GetCommandString(id, gcs, NULL, (LPSTR)buf, _countof(buf));
96 if (FAILED(hr) || !*buf)
97 continue;
98 else if (IsUnicode && !_wcsicmp((LPWSTR)Verb, buf))
99 return id;
100 else if (!IsUnicode && !lstrcmpiA((LPSTR)Verb, (LPSTR)buf))
101 return id;
102 }
103 return HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
104 }
105
IsVerbListSeparator(WCHAR Ch)106 static inline bool IsVerbListSeparator(WCHAR Ch)
107 {
108 return Ch == L' ' || Ch == L','; // learn.microsoft.com/en-us/windows/win32/shell/context-menu-handlers
109 }
110
FindVerbInDefaultVerbList(LPCWSTR List,LPCWSTR Verb)111 static int FindVerbInDefaultVerbList(LPCWSTR List, LPCWSTR Verb)
112 {
113 for (UINT index = 0; *List; ++index)
114 {
115 while (IsVerbListSeparator(*List))
116 List++;
117 LPCWSTR Start = List;
118 while (*List && !IsVerbListSeparator(*List))
119 List++;
120 // "List > Start" to verify that the list item is non-empty to avoid the edge case where Verb is "" and the list contains ",,"
121 if (!_wcsnicmp(Verb, Start, List - Start) && List > Start)
122 return index;
123 }
124 return -1;
125 }
126
SHELL32_EnumDefaultVerbList(LPCWSTR List,UINT Index,LPWSTR Verb,SIZE_T cchMax)127 EXTERN_C HRESULT SHELL32_EnumDefaultVerbList(LPCWSTR List, UINT Index, LPWSTR Verb, SIZE_T cchMax)
128 {
129 for (UINT i = 0; *List; ++i)
130 {
131 while (IsVerbListSeparator(*List))
132 List++;
133 LPCWSTR Start = List;
134 while (*List && !IsVerbListSeparator(*List))
135 List++;
136 if (List > Start && i == Index)
137 return StringCchCopyNW(Verb, cchMax, Start, List - Start);
138 }
139 return HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS);
140 }
141
142 class CDefaultContextMenu :
143 public CComObjectRootEx<CComMultiThreadModelNoCS>,
144 public IContextMenu3,
145 public IObjectWithSite,
146 public IServiceProvider
147 {
148 private:
149 CComPtr<IUnknown> m_site;
150 CComPtr<IShellFolder> m_psf;
151 CComPtr<IContextMenuCB> m_pmcb;
152 LPFNDFMCALLBACK m_pfnmcb;
153 UINT m_cidl;
154 PCUITEMID_CHILD_ARRAY m_apidl;
155 CComPtr<IDataObject> m_pDataObj;
156 HKEY* m_aKeys;
157 UINT m_cKeys;
158 PIDLIST_ABSOLUTE m_pidlFolder;
159 DWORD m_bGroupPolicyActive;
160 CAtlList<DynamicShellEntry> m_DynamicEntries;
161 UINT m_iIdSHEFirst; /* first used id */
162 UINT m_iIdSHELast; /* last used id */
163 CAtlList<StaticShellEntry> m_StaticEntries;
164 UINT m_iIdSCMFirst; /* first static used id */
165 UINT m_iIdSCMLast; /* last static used id */
166 UINT m_iIdCBFirst; /* first callback used id */
167 UINT m_iIdCBLast; /* last callback used id */
168 UINT m_iIdDfltFirst; /* first default part id */
169 UINT m_iIdDfltLast; /* last default part id */
170 HWND m_hwnd; /* window passed to callback */
171 WCHAR m_DefVerbs[MAX_PATH];
172
173 HRESULT _DoCallback(UINT uMsg, WPARAM wParam, LPVOID lParam);
174 HRESULT _DoInvokeCommandCallback(LPCMINVOKECOMMANDINFOEX lpcmi, WPARAM CmdId);
175 void AddStaticEntry(const HKEY hkeyClass, const WCHAR *szVerb, UINT uFlags);
176 void AddStaticEntriesForKey(HKEY hKey, UINT uFlags);
177 void TryPickDefault(HMENU hMenu, UINT idCmdFirst, UINT DfltOffset, UINT uFlags);
178 BOOL IsShellExtensionAlreadyLoaded(REFCLSID clsid);
179 HRESULT LoadDynamicContextMenuHandler(HKEY hKey, REFCLSID clsid);
180 BOOL EnumerateDynamicContextHandlerForKey(HKEY hRootKey);
181 UINT AddShellExtensionsToMenu(HMENU hMenu, UINT* pIndexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags);
182 UINT AddStaticContextMenusToMenu(HMENU hMenu, UINT* IndexMenu, UINT iIdCmdFirst, UINT iIdCmdLast, UINT uFlags);
183 HRESULT DoPaste(LPCMINVOKECOMMANDINFOEX lpcmi, BOOL bLink);
184 HRESULT DoOpenOrExplore(LPCMINVOKECOMMANDINFOEX lpcmi);
185 HRESULT DoCreateLink(LPCMINVOKECOMMANDINFOEX lpcmi);
186 HRESULT DoDelete(LPCMINVOKECOMMANDINFOEX lpcmi);
187 HRESULT DoCopyOrCut(LPCMINVOKECOMMANDINFOEX lpcmi, BOOL bCopy);
188 HRESULT DoRename(LPCMINVOKECOMMANDINFOEX lpcmi);
189 HRESULT DoProperties(LPCMINVOKECOMMANDINFOEX lpcmi);
190 HRESULT DoUndo(LPCMINVOKECOMMANDINFOEX lpcmi);
191 HRESULT DoCreateNewFolder(LPCMINVOKECOMMANDINFOEX lpici);
192 HRESULT DoCopyToMoveToFolder(LPCMINVOKECOMMANDINFOEX lpici, BOOL bCopy);
193 HRESULT InvokeShellExt(LPCMINVOKECOMMANDINFOEX lpcmi);
194 HRESULT InvokeRegVerb(LPCMINVOKECOMMANDINFOEX lpcmi);
195 DWORD BrowserFlagsFromVerb(LPCMINVOKECOMMANDINFOEX lpcmi, PStaticShellEntry pEntry);
196 HRESULT TryToBrowse(LPCMINVOKECOMMANDINFOEX lpcmi, LPCITEMIDLIST pidl, DWORD wFlags);
197 HRESULT InvokePidl(LPCMINVOKECOMMANDINFOEX lpcmi, LPCITEMIDLIST pidl, PStaticShellEntry pEntry);
198 PDynamicShellEntry GetDynamicEntry(UINT idCmd);
199 BOOL MapVerbToCmdId(PVOID Verb, PUINT idCmd, BOOL IsUnicode);
200
201 public:
202 CDefaultContextMenu();
203 ~CDefaultContextMenu();
204 HRESULT WINAPI Initialize(const DEFCONTEXTMENU *pdcm, LPFNDFMCALLBACK lpfn);
205
206 // IContextMenu
207 STDMETHOD(QueryContextMenu)(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) override;
208 STDMETHOD(InvokeCommand)(LPCMINVOKECOMMANDINFO lpcmi) override;
209 STDMETHOD(GetCommandString)(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen) override;
210
211 // IContextMenu2
212 STDMETHOD(HandleMenuMsg)(UINT uMsg, WPARAM wParam, LPARAM lParam) override;
213
214 // IContextMenu3
215 STDMETHOD(HandleMenuMsg2)(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plResult) override;
216
217 // IObjectWithSite
218 STDMETHOD(SetSite)(IUnknown *pUnkSite) override;
219 STDMETHOD(GetSite)(REFIID riid, void **ppvSite) override;
220
221 // IServiceProvider
STDMETHOD(QueryService)222 STDMETHOD(QueryService)(REFGUID svc, REFIID riid, void**ppv) override
223 {
224 return IUnknown_QueryService(m_site, svc, riid, ppv);
225 }
226
227 BEGIN_COM_MAP(CDefaultContextMenu)
228 COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu)
229 COM_INTERFACE_ENTRY_IID(IID_IContextMenu2, IContextMenu2)
230 COM_INTERFACE_ENTRY_IID(IID_IContextMenu3, IContextMenu3)
231 COM_INTERFACE_ENTRY_IID(IID_IObjectWithSite, IObjectWithSite)
232 COM_INTERFACE_ENTRY_IID(IID_IServiceProvider, IServiceProvider)
233 END_COM_MAP()
234 };
235
CDefaultContextMenu()236 CDefaultContextMenu::CDefaultContextMenu() :
237 m_psf(NULL),
238 m_pmcb(NULL),
239 m_pfnmcb(NULL),
240 m_cidl(0),
241 m_apidl(NULL),
242 m_pDataObj(NULL),
243 m_aKeys(NULL),
244 m_cKeys(NULL),
245 m_pidlFolder(NULL),
246 m_bGroupPolicyActive(0),
247 m_iIdSHEFirst(0),
248 m_iIdSHELast(0),
249 m_iIdSCMFirst(0),
250 m_iIdSCMLast(0),
251 m_iIdCBFirst(0),
252 m_iIdCBLast(0),
253 m_iIdDfltFirst(0),
254 m_iIdDfltLast(0),
255 m_hwnd(NULL)
256 {
257 *m_DefVerbs = UNICODE_NULL;
258 }
259
~CDefaultContextMenu()260 CDefaultContextMenu::~CDefaultContextMenu()
261 {
262 for (POSITION it = m_DynamicEntries.GetHeadPosition(); it != NULL;)
263 {
264 const DynamicShellEntry& info = m_DynamicEntries.GetNext(it);
265 IUnknown_SetSite(info.pCM.p, NULL);
266 }
267 m_DynamicEntries.RemoveAll();
268 m_StaticEntries.RemoveAll();
269
270 for (UINT i = 0; i < m_cKeys; i++)
271 RegCloseKey(m_aKeys[i]);
272 HeapFree(GetProcessHeap(), 0, m_aKeys);
273
274 if (m_pidlFolder)
275 CoTaskMemFree(m_pidlFolder);
276 _ILFreeaPidl(const_cast<PITEMID_CHILD *>(m_apidl), m_cidl);
277 }
278
Initialize(const DEFCONTEXTMENU * pdcm,LPFNDFMCALLBACK lpfn)279 HRESULT WINAPI CDefaultContextMenu::Initialize(const DEFCONTEXTMENU *pdcm, LPFNDFMCALLBACK lpfn)
280 {
281 TRACE("cidl %u\n", pdcm->cidl);
282
283 if (!pdcm->pcmcb && !lpfn)
284 {
285 ERR("CDefaultContextMenu needs a callback!\n");
286 return E_INVALIDARG;
287 }
288
289 m_cidl = pdcm->cidl;
290 m_apidl = const_cast<PCUITEMID_CHILD_ARRAY>(_ILCopyaPidl(pdcm->apidl, m_cidl));
291 if (m_cidl && !m_apidl)
292 return E_OUTOFMEMORY;
293 m_psf = pdcm->psf;
294 m_pmcb = pdcm->pcmcb;
295 m_pfnmcb = lpfn;
296 m_hwnd = pdcm->hwnd;
297
298 m_cKeys = pdcm->cKeys;
299 if (pdcm->cKeys)
300 {
301 m_aKeys = (HKEY*)HeapAlloc(GetProcessHeap(), 0, sizeof(HKEY) * pdcm->cKeys);
302 if (!m_aKeys)
303 return E_OUTOFMEMORY;
304 memcpy(m_aKeys, pdcm->aKeys, sizeof(HKEY) * pdcm->cKeys);
305 }
306
307 m_psf->GetUIObjectOf(pdcm->hwnd, m_cidl, m_apidl, IID_NULL_PPV_ARG(IDataObject, &m_pDataObj));
308
309 if (pdcm->pidlFolder)
310 {
311 m_pidlFolder = ILClone(pdcm->pidlFolder);
312 }
313 else
314 {
315 CComPtr<IPersistFolder2> pf = NULL;
316 if (SUCCEEDED(m_psf->QueryInterface(IID_PPV_ARG(IPersistFolder2, &pf))))
317 {
318 if (FAILED(pf->GetCurFolder(&m_pidlFolder)))
319 ERR("GetCurFolder failed\n");
320 }
321 TRACE("pidlFolder %p\n", m_pidlFolder);
322 }
323
324 return S_OK;
325 }
326
_DoCallback(UINT uMsg,WPARAM wParam,LPVOID lParam)327 HRESULT CDefaultContextMenu::_DoCallback(UINT uMsg, WPARAM wParam, LPVOID lParam)
328 {
329 if (m_pmcb)
330 {
331 return m_pmcb->CallBack(m_psf, m_hwnd, m_pDataObj, uMsg, wParam, (LPARAM)lParam);
332 }
333 else if(m_pfnmcb)
334 {
335 return m_pfnmcb(m_psf, m_hwnd, m_pDataObj, uMsg, wParam, (LPARAM)lParam);
336 }
337
338 return E_FAIL;
339 }
340
AddStaticEntry(const HKEY hkeyClass,const WCHAR * szVerb,UINT uFlags)341 void CDefaultContextMenu::AddStaticEntry(const HKEY hkeyClass, const WCHAR *szVerb, UINT uFlags)
342 {
343 POSITION it = m_StaticEntries.GetHeadPosition();
344 while (it != NULL)
345 {
346 const StaticShellEntry& info = m_StaticEntries.GetNext(it);
347 if (info.Verb.CompareNoCase(szVerb) == 0)
348 {
349 /* entry already exists */
350 return;
351 }
352 }
353
354 TRACE("adding verb %s\n", debugstr_w(szVerb));
355
356 if (!_wcsicmp(szVerb, L"open") && !(uFlags & CMF_NODEFAULT))
357 {
358 /* open verb is always inserted in front */
359 m_StaticEntries.AddHead({ szVerb, hkeyClass });
360 }
361 else
362 {
363 m_StaticEntries.AddTail({ szVerb, hkeyClass });
364 }
365 }
366
AddStaticEntriesForKey(HKEY hKey,UINT uFlags)367 void CDefaultContextMenu::AddStaticEntriesForKey(HKEY hKey, UINT uFlags)
368 {
369 WCHAR wszName[VERBKEY_CCHMAX];
370 DWORD cchName, dwIndex = 0;
371 HKEY hShellKey;
372
373 LRESULT lres = RegOpenKeyExW(hKey, L"shell", 0, KEY_READ, &hShellKey);
374 if (lres != STATUS_SUCCESS)
375 return;
376
377 if (!*m_DefVerbs)
378 {
379 DWORD cb = sizeof(m_DefVerbs);
380 RegGetValueW(hShellKey, NULL, NULL, RRF_RT_REG_SZ, NULL, m_DefVerbs, &cb);
381 }
382
383 while(TRUE)
384 {
385 cchName = _countof(wszName);
386 if (RegEnumKeyExW(hShellKey, dwIndex++, wszName, &cchName, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
387 break;
388
389 AddStaticEntry(hKey, wszName, uFlags);
390 }
391
392 RegCloseKey(hShellKey);
393 }
394
395 static
396 BOOL
HasClipboardData()397 HasClipboardData()
398 {
399 BOOL bRet = FALSE;
400 CComPtr<IDataObject> pDataObj;
401
402 if (SUCCEEDED(OleGetClipboard(&pDataObj)))
403 {
404 FORMATETC formatetc;
405
406 TRACE("pDataObj=%p\n", pDataObj.p);
407
408 /* Set the FORMATETC structure*/
409 InitFormatEtc(formatetc, RegisterClipboardFormatW(CFSTR_SHELLIDLIST), TYMED_HGLOBAL);
410 bRet = SUCCEEDED(pDataObj->QueryGetData(&formatetc));
411 }
412
413 return bRet;
414 }
415
416 BOOL
IsShellExtensionAlreadyLoaded(REFCLSID clsid)417 CDefaultContextMenu::IsShellExtensionAlreadyLoaded(REFCLSID clsid)
418 {
419 POSITION it = m_DynamicEntries.GetHeadPosition();
420 while (it != NULL)
421 {
422 const DynamicShellEntry& info = m_DynamicEntries.GetNext(it);
423 if (info.ClassID == clsid)
424 return TRUE;
425 }
426
427 return FALSE;
428 }
429
430 HRESULT
LoadDynamicContextMenuHandler(HKEY hKey,REFCLSID clsid)431 CDefaultContextMenu::LoadDynamicContextMenuHandler(HKEY hKey, REFCLSID clsid)
432 {
433 HRESULT hr;
434 TRACE("LoadDynamicContextMenuHandler entered with This %p hKey %p pclsid %s\n", this, hKey, wine_dbgstr_guid(&clsid));
435
436 if (IsShellExtensionAlreadyLoaded(clsid))
437 return S_OK;
438
439 CComPtr<IContextMenu> pcm;
440 hr = SHCoCreateInstance(NULL, &clsid, NULL, IID_PPV_ARG(IContextMenu, &pcm));
441 if (FAILED(hr))
442 {
443 ERR("SHCoCreateInstance(IContextMenu) failed.clsid %s hr 0x%x\n", wine_dbgstr_guid(&clsid), hr);
444 return hr;
445 }
446
447 CComPtr<IShellExtInit> pExtInit;
448 hr = pcm->QueryInterface(IID_PPV_ARG(IShellExtInit, &pExtInit));
449 if (FAILED(hr))
450 {
451 ERR("IContextMenu->QueryInterface(IShellExtInit) failed.clsid %s hr 0x%x\n", wine_dbgstr_guid(&clsid), hr);
452 return hr;
453 }
454
455 hr = pExtInit->Initialize(m_pDataObj ? NULL : m_pidlFolder, m_pDataObj, hKey);
456 if (FAILED(hr))
457 {
458 WARN("IShellExtInit::Initialize failed.clsid %s hr 0x%x\n", wine_dbgstr_guid(&clsid), hr);
459 return hr;
460 }
461
462 if (m_site)
463 IUnknown_SetSite(pcm, m_site);
464
465 m_DynamicEntries.AddTail({ 0, 0, clsid, pcm });
466
467 return S_OK;
468 }
469
470 BOOL
EnumerateDynamicContextHandlerForKey(HKEY hRootKey)471 CDefaultContextMenu::EnumerateDynamicContextHandlerForKey(HKEY hRootKey)
472 {
473 WCHAR wszName[MAX_PATH], wszBuf[MAX_PATH], *pwszClsid;
474 DWORD cchName;
475 HRESULT hr;
476 HKEY hKey;
477
478 if (RegOpenKeyExW(hRootKey, L"shellex\\ContextMenuHandlers", 0, KEY_READ, &hKey) != ERROR_SUCCESS)
479 {
480 TRACE("RegOpenKeyExW failed\n");
481 return FALSE;
482 }
483
484 DWORD dwIndex = 0;
485 while (TRUE)
486 {
487 cchName = _countof(wszName);
488 if (RegEnumKeyExW(hKey, dwIndex++, wszName, &cchName, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
489 break;
490
491 /* Key name or key value is CLSID */
492 CLSID clsid;
493 hr = CLSIDFromString(wszName, &clsid);
494 if (hr == S_OK)
495 pwszClsid = wszName;
496 else
497 {
498 DWORD cchBuf = _countof(wszBuf);
499 if (RegGetValueW(hKey, wszName, NULL, RRF_RT_REG_SZ, NULL, wszBuf, &cchBuf) == ERROR_SUCCESS)
500 hr = CLSIDFromString(wszBuf, &clsid);
501 pwszClsid = wszBuf;
502 }
503
504 if (FAILED(hr))
505 {
506 ERR("CLSIDFromString failed for clsid %S hr 0x%x\n", pwszClsid, hr);
507 continue;
508 }
509
510 if (m_bGroupPolicyActive)
511 {
512 if (RegGetValueW(HKEY_LOCAL_MACHINE,
513 L"Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved",
514 pwszClsid,
515 RRF_RT_REG_SZ,
516 NULL,
517 NULL,
518 NULL) != ERROR_SUCCESS)
519 {
520 ERR("Shell extension %s not approved!\n", pwszClsid);
521 continue;
522 }
523 }
524
525 hr = LoadDynamicContextMenuHandler(hRootKey, clsid);
526 if (FAILED(hr))
527 WARN("Failed to get context menu entires from shell extension! clsid: %S\n", pwszClsid);
528 }
529
530 RegCloseKey(hKey);
531 return TRUE;
532 }
533
534 UINT
AddShellExtensionsToMenu(HMENU hMenu,UINT * pIndexMenu,UINT idCmdFirst,UINT idCmdLast,UINT uFlags)535 CDefaultContextMenu::AddShellExtensionsToMenu(HMENU hMenu, UINT* pIndexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
536 {
537 UINT cIds = 0;
538
539 if (m_DynamicEntries.IsEmpty())
540 return cIds;
541
542 POSITION it = m_DynamicEntries.GetHeadPosition();
543 while (it != NULL)
544 {
545 DynamicShellEntry& info = m_DynamicEntries.GetNext(it);
546
547 HRESULT hr = info.pCM->QueryContextMenu(hMenu, *pIndexMenu, idCmdFirst + cIds, idCmdLast, uFlags);
548 if (SUCCEEDED(hr))
549 {
550 info.iIdCmdFirst = cIds;
551 info.NumIds = HRESULT_CODE(hr);
552 (*pIndexMenu) += info.NumIds;
553
554 cIds += info.NumIds;
555 if (idCmdFirst + cIds >= idCmdLast)
556 break;
557 }
558 TRACE("pEntry hr %x contextmenu %p cmdfirst %x num ids %x\n", hr, info.pCM.p, info.iIdCmdFirst, info.NumIds);
559 }
560 return cIds;
561 }
562
563 UINT
AddStaticContextMenusToMenu(HMENU hMenu,UINT * pIndexMenu,UINT iIdCmdFirst,UINT iIdCmdLast,UINT uFlags)564 CDefaultContextMenu::AddStaticContextMenusToMenu(
565 HMENU hMenu,
566 UINT* pIndexMenu,
567 UINT iIdCmdFirst,
568 UINT iIdCmdLast,
569 UINT uFlags)
570 {
571 UINT ntver = RosGetProcessEffectiveVersion();
572 MENUITEMINFOW mii = { sizeof(mii) };
573 UINT idResource;
574 WCHAR wszDispVerb[80]; // The limit on XP. If the friendly string is longer, it falls back to the verb key.
575 UINT fState;
576 UINT cIds = 0, indexFirst = *pIndexMenu, indexDefault;
577 int iDefVerbIndex = -1;
578
579 mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE | MIIM_DATA;
580 mii.fType = MFT_STRING;
581
582 POSITION it = m_StaticEntries.GetHeadPosition();
583 bool first = true;
584 while (it != NULL)
585 {
586 StaticShellEntry& info = m_StaticEntries.GetNext(it);
587 BOOL forceFirstPos = FALSE;
588
589 fState = MFS_ENABLED;
590
591 /* set first entry as default */
592 if (first)
593 {
594 fState |= MFS_DEFAULT;
595 first = false;
596 }
597
598 if (info.Verb.CompareNoCase(L"open") == 0)
599 {
600 idResource = IDS_OPEN_VERB;
601 fState |= MFS_DEFAULT; /* override default when open verb is found */
602 forceFirstPos++;
603 }
604 else if (info.Verb.CompareNoCase(L"explore") == 0)
605 {
606 idResource = IDS_EXPLORE_VERB;
607 if (uFlags & CMF_EXPLORE)
608 {
609 fState |= MFS_DEFAULT;
610 forceFirstPos++;
611 }
612 }
613 else if (info.Verb.CompareNoCase(L"runas") == 0)
614 idResource = IDS_RUNAS_VERB;
615 else if (info.Verb.CompareNoCase(L"edit") == 0)
616 idResource = IDS_EDIT_VERB;
617 else if (info.Verb.CompareNoCase(L"find") == 0)
618 idResource = IDS_FIND_VERB;
619 else if (info.Verb.CompareNoCase(L"print") == 0)
620 idResource = IDS_PRINT_VERB;
621 else if (info.Verb.CompareNoCase(L"printto") == 0)
622 continue;
623 else
624 idResource = 0;
625
626 /* By default use verb for menu item name */
627 mii.dwTypeData = (LPWSTR)info.Verb.GetString();
628
629 WCHAR wszKey[sizeof("shell\\") + MAX_VERB];
630 HRESULT hr;
631 hr = StringCbPrintfW(wszKey, sizeof(wszKey), L"shell\\%s", info.Verb.GetString());
632 if (FAILED_UNEXPECTEDLY(hr))
633 {
634 continue;
635 }
636
637 UINT cmdFlags = 0;
638 bool hide = false;
639 HKEY hkVerb;
640 if (idResource > 0)
641 {
642 if (!(uFlags & CMF_OPTIMIZEFORINVOKE))
643 {
644 if (LoadStringW(shell32_hInstance, idResource, wszDispVerb, _countof(wszDispVerb)))
645 mii.dwTypeData = wszDispVerb; /* use translated verb */
646 else
647 ERR("Failed to load string\n");
648 }
649
650 if (RegOpenKeyW(info.hkClass, wszKey, &hkVerb) != ERROR_SUCCESS)
651 hkVerb = NULL;
652 }
653 else
654 {
655 if (RegOpenKeyW(info.hkClass, wszKey, &hkVerb) == ERROR_SUCCESS)
656 {
657 if (!(uFlags & CMF_OPTIMIZEFORINVOKE))
658 {
659 DWORD cbVerb = sizeof(wszDispVerb);
660 LONG res = RegLoadMUIStringW(hkVerb, L"MUIVerb", wszDispVerb, cbVerb, NULL, 0, NULL);
661 if (res || !*wszDispVerb)
662 res = RegLoadMUIStringW(hkVerb, NULL, wszDispVerb, cbVerb, NULL, 0, NULL);
663
664 if (res == ERROR_SUCCESS && *wszDispVerb)
665 {
666 /* use description for the menu entry */
667 mii.dwTypeData = wszDispVerb;
668 }
669 }
670 }
671 else
672 {
673 hkVerb = NULL;
674 }
675 }
676
677 if (hkVerb)
678 {
679 if (!(uFlags & CMF_EXTENDEDVERBS))
680 hide = RegValueExists(hkVerb, L"Extended");
681
682 if (!hide)
683 hide = RegValueExists(hkVerb, L"ProgrammaticAccessOnly");
684
685 if (!hide && !(uFlags & CMF_DISABLEDVERBS))
686 hide = RegValueExists(hkVerb, L"LegacyDisable");
687
688 if (RegValueExists(hkVerb, L"NeverDefault"))
689 fState &= ~MFS_DEFAULT;
690
691 if (RegValueExists(hkVerb, L"SeparatorBefore"))
692 cmdFlags |= ECF_SEPARATORBEFORE;
693 if (RegValueExists(hkVerb, L"SeparatorAfter"))
694 cmdFlags |= ECF_SEPARATORAFTER;
695
696 RegCloseKey(hkVerb);
697 }
698
699 if (((uFlags & CMF_NODEFAULT) && ntver >= _WIN32_WINNT_VISTA) ||
700 ((uFlags & CMF_DONOTPICKDEFAULT) && ntver >= _WIN32_WINNT_WIN7))
701 {
702 fState &= ~MFS_DEFAULT;
703 }
704
705 if (!hide)
706 {
707 if (cmdFlags & ECF_SEPARATORBEFORE)
708 {
709 if (InsertMenuItemAt(hMenu, *pIndexMenu, MF_SEPARATOR))
710 (*pIndexMenu)++;
711 }
712
713 UINT pos = *pIndexMenu;
714 int verbIndex = hkVerb ? FindVerbInDefaultVerbList(m_DefVerbs, info.Verb) : -1;
715 if (verbIndex >= 0)
716 {
717 if (verbIndex < iDefVerbIndex || iDefVerbIndex < 0)
718 {
719 iDefVerbIndex = verbIndex;
720 fState |= MFS_DEFAULT;
721 forceFirstPos = TRUE;
722 }
723 else
724 {
725 fState &= ~MFS_DEFAULT; // We have already set a better default
726 pos = indexDefault;
727 }
728 }
729 else if (iDefVerbIndex >= 0)
730 {
731 fState &= ~MFS_DEFAULT; // We have already set the default
732 if (forceFirstPos)
733 pos = indexDefault;
734 forceFirstPos = FALSE;
735 }
736
737 mii.fState = fState;
738 mii.wID = iIdCmdFirst + cIds;
739 if (InsertMenuItemW(hMenu, forceFirstPos ? indexFirst : pos, TRUE, &mii))
740 (*pIndexMenu)++;
741
742 if (cmdFlags & ECF_SEPARATORAFTER)
743 {
744 if (InsertMenuItemAt(hMenu, *pIndexMenu, MF_SEPARATOR))
745 (*pIndexMenu)++;
746 }
747
748 if (fState & MFS_DEFAULT)
749 indexDefault = *pIndexMenu; // This is where we want to insert "high priority" verbs
750 }
751 cIds++; // Always increment the id because it acts as the index into m_StaticEntries
752
753 if (mii.wID >= iIdCmdLast)
754 break;
755 }
756
757 return cIds;
758 }
759
_InsertMenuItemW(HMENU hMenu,UINT indexMenu,BOOL fByPosition,UINT wID,UINT fType,LPCWSTR dwTypeData,UINT fState)760 BOOL WINAPI _InsertMenuItemW(
761 HMENU hMenu,
762 UINT indexMenu,
763 BOOL fByPosition,
764 UINT wID,
765 UINT fType,
766 LPCWSTR dwTypeData,
767 UINT fState)
768 {
769 MENUITEMINFOW mii;
770 WCHAR wszText[100];
771
772 ZeroMemory(&mii, sizeof(mii));
773 mii.cbSize = sizeof(mii);
774 if (fType == MFT_SEPARATOR)
775 mii.fMask = MIIM_ID | MIIM_TYPE;
776 else if (fType == MFT_STRING)
777 {
778 mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE;
779 if (IS_INTRESOURCE(dwTypeData))
780 {
781 if (LoadStringW(shell32_hInstance, LOWORD((ULONG_PTR)dwTypeData), wszText, _countof(wszText)))
782 mii.dwTypeData = wszText;
783 else
784 {
785 ERR("failed to load string %p\n", dwTypeData);
786 return FALSE;
787 }
788 }
789 else
790 mii.dwTypeData = (LPWSTR)dwTypeData;
791 mii.fState = fState;
792 }
793
794 mii.wID = wID;
795 mii.fType = fType;
796 return InsertMenuItemW(hMenu, indexMenu, fByPosition, &mii);
797 }
798
799 void
TryPickDefault(HMENU hMenu,UINT idCmdFirst,UINT DfltOffset,UINT uFlags)800 CDefaultContextMenu::TryPickDefault(HMENU hMenu, UINT idCmdFirst, UINT DfltOffset, UINT uFlags)
801 {
802 // Are we allowed to pick a default?
803 if ((uFlags & CMF_NODEFAULT) ||
804 ((uFlags & CMF_DONOTPICKDEFAULT) && RosGetProcessEffectiveVersion() >= _WIN32_WINNT_WIN7))
805 {
806 return;
807 }
808
809 // Do we already have a default?
810 if ((int)GetMenuDefaultItem(hMenu, MF_BYPOSITION, 0) != -1)
811 return;
812
813 // Does the view want to pick one?
814 INT_PTR forceDfm = 0;
815 if (SUCCEEDED(_DoCallback(DFM_GETDEFSTATICID, 0, &forceDfm)) && forceDfm)
816 {
817 for (UINT i = 0; i < _countof(g_StaticInvokeCmdMap); ++i)
818 {
819 UINT menuItemId = g_StaticInvokeCmdMap[i].IntVerb + DfltOffset - DCM_FCIDM_SHVIEW_OFFSET;
820 if (g_StaticInvokeCmdMap[i].DfmCmd == forceDfm &&
821 SetMenuDefaultItem(hMenu, menuItemId, MF_BYCOMMAND))
822 {
823 return;
824 }
825 }
826 }
827
828 // Don't want to pick something like cut or delete as the default but
829 // a static or dynamic verb is a good default.
830 if (m_iIdSCMLast > m_iIdSCMFirst || m_iIdSHELast > m_iIdSHEFirst)
831 SetMenuDefaultItem(hMenu, idCmdFirst, MF_BYCOMMAND);
832 }
833
834 HRESULT
835 WINAPI
QueryContextMenu(HMENU hMenu,UINT IndexMenu,UINT idCmdFirst,UINT idCmdLast,UINT uFlags)836 CDefaultContextMenu::QueryContextMenu(
837 HMENU hMenu,
838 UINT IndexMenu,
839 UINT idCmdFirst,
840 UINT idCmdLast,
841 UINT uFlags)
842 {
843 HRESULT hr;
844 UINT idCmdNext = idCmdFirst;
845 UINT cIds = 0;
846
847 TRACE("BuildShellItemContextMenu entered\n");
848
849 /* Load static verbs and shell extensions from registry */
850 for (UINT i = 0; i < m_cKeys && !(uFlags & CMF_NOVERBS); i++)
851 {
852 AddStaticEntriesForKey(m_aKeys[i], uFlags);
853 EnumerateDynamicContextHandlerForKey(m_aKeys[i]);
854 }
855
856 /* Add static context menu handlers */
857 cIds = AddStaticContextMenusToMenu(hMenu, &IndexMenu, idCmdFirst, idCmdLast, uFlags);
858 m_iIdSCMFirst = 0; // FIXME: This should be = idCmdFirst?
859 m_iIdSCMLast = cIds;
860 idCmdNext = idCmdFirst + cIds;
861
862 /* Add dynamic context menu handlers */
863 cIds += AddShellExtensionsToMenu(hMenu, &IndexMenu, idCmdNext, idCmdLast, uFlags);
864 m_iIdSHEFirst = m_iIdSCMLast;
865 m_iIdSHELast = cIds;
866 idCmdNext = idCmdFirst + cIds;
867 TRACE("SH_LoadContextMenuHandlers first %x last %x\n", m_iIdSHEFirst, m_iIdSHELast);
868
869 /* Now let the callback add its own items */
870 QCMINFO qcminfo = {hMenu, IndexMenu, idCmdNext, idCmdLast, NULL};
871 if (SUCCEEDED(_DoCallback(DFM_MERGECONTEXTMENU, uFlags, &qcminfo)))
872 {
873 UINT added = qcminfo.idCmdFirst - idCmdNext;
874 cIds += added;
875 IndexMenu += added;
876 m_iIdCBFirst = m_iIdSHELast;
877 m_iIdCBLast = cIds;
878 idCmdNext = idCmdFirst + cIds;
879 }
880
881 //TODO: DFM_MERGECONTEXTMENU_BOTTOM
882
883 UINT idDefaultOffset = 0;
884 BOOL isBackgroundMenu = !m_cidl;
885 if (!(uFlags & CMF_VERBSONLY) && !isBackgroundMenu)
886 {
887 /* Get the attributes of the items */
888 SFGAOF rfg = SFGAO_BROWSABLE | SFGAO_CANCOPY | SFGAO_CANLINK | SFGAO_CANMOVE | SFGAO_CANDELETE | SFGAO_CANRENAME | SFGAO_HASPROPSHEET | SFGAO_FILESYSTEM | SFGAO_FOLDER;
889 hr = m_psf->GetAttributesOf(m_cidl, m_apidl, &rfg);
890 if (FAILED_UNEXPECTEDLY(hr))
891 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, cIds);
892
893 /* Add the default part of the menu */
894 HMENU hmenuDefault = LoadMenuW(_AtlBaseModule.GetResourceInstance(), L"MENU_SHV_FILE");
895
896 /* Remove uneeded entries */
897 if (!(rfg & SFGAO_CANMOVE))
898 DeleteMenu(hmenuDefault, IDM_CUT, MF_BYCOMMAND);
899 if (!(rfg & SFGAO_CANCOPY))
900 DeleteMenu(hmenuDefault, IDM_COPY, MF_BYCOMMAND);
901 if (!((rfg & SFGAO_FILESYSTEM) && HasClipboardData()))
902 DeleteMenu(hmenuDefault, IDM_INSERT, MF_BYCOMMAND);
903 if (!(rfg & SFGAO_CANLINK))
904 DeleteMenu(hmenuDefault, IDM_CREATELINK, MF_BYCOMMAND);
905 if (!(rfg & SFGAO_CANDELETE))
906 DeleteMenu(hmenuDefault, IDM_DELETE, MF_BYCOMMAND);
907 if (!(rfg & SFGAO_CANRENAME) || !(uFlags & CMF_CANRENAME))
908 DeleteMenu(hmenuDefault, IDM_RENAME, MF_BYCOMMAND);
909 if (!(rfg & SFGAO_HASPROPSHEET))
910 DeleteMenu(hmenuDefault, IDM_PROPERTIES, MF_BYCOMMAND);
911
912 idDefaultOffset = idCmdNext;
913 UINT idMax = Shell_MergeMenus(hMenu, GetSubMenu(hmenuDefault, 0), IndexMenu, idCmdNext, idCmdLast, 0);
914 m_iIdDfltFirst = cIds;
915 cIds += idMax - idCmdNext;
916 m_iIdDfltLast = cIds;
917
918 DestroyMenu(hmenuDefault);
919 }
920
921 TryPickDefault(hMenu, idCmdFirst, idDefaultOffset, uFlags);
922
923 // TODO: DFM_MERGECONTEXTMENU_TOP
924
925 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, cIds);
926 }
927
DoPaste(LPCMINVOKECOMMANDINFOEX lpcmi,BOOL bLink)928 HRESULT CDefaultContextMenu::DoPaste(LPCMINVOKECOMMANDINFOEX lpcmi, BOOL bLink)
929 {
930 HRESULT hr;
931
932 CComPtr<IDataObject> pda;
933 hr = OleGetClipboard(&pda);
934 if (FAILED_UNEXPECTEDLY(hr))
935 return hr;
936
937 FORMATETC formatetc2;
938 STGMEDIUM medium2;
939 InitFormatEtc(formatetc2, RegisterClipboardFormatW(CFSTR_PREFERREDDROPEFFECT), TYMED_HGLOBAL);
940
941 DWORD dwKey= 0;
942
943 if (SUCCEEDED(pda->GetData(&formatetc2, &medium2)))
944 {
945 DWORD * pdwFlag = (DWORD*)GlobalLock(medium2.hGlobal);
946 if (pdwFlag)
947 {
948 if (*pdwFlag == DROPEFFECT_COPY)
949 dwKey = MK_CONTROL;
950 else
951 dwKey = MK_SHIFT;
952 }
953 else
954 {
955 ERR("No drop effect obtained\n");
956 }
957 GlobalUnlock(medium2.hGlobal);
958 }
959
960 if (bLink)
961 {
962 dwKey = MK_CONTROL|MK_SHIFT;
963 }
964
965 CComPtr<IDropTarget> pdrop;
966 if (m_cidl)
967 hr = m_psf->GetUIObjectOf(NULL, 1, &m_apidl[0], IID_NULL_PPV_ARG(IDropTarget, &pdrop));
968 else
969 hr = m_psf->CreateViewObject(NULL, IID_PPV_ARG(IDropTarget, &pdrop));
970
971 if (FAILED_UNEXPECTEDLY(hr))
972 return hr;
973
974 SHSimulateDrop(pdrop, pda, dwKey, NULL, NULL);
975
976 TRACE("CP result %x\n", hr);
977 return S_OK;
978 }
979
980 HRESULT
DoOpenOrExplore(LPCMINVOKECOMMANDINFOEX lpcmi)981 CDefaultContextMenu::DoOpenOrExplore(LPCMINVOKECOMMANDINFOEX lpcmi)
982 {
983 UNIMPLEMENTED;
984 return E_FAIL;
985 }
986
DoCreateLink(LPCMINVOKECOMMANDINFOEX lpcmi)987 HRESULT CDefaultContextMenu::DoCreateLink(LPCMINVOKECOMMANDINFOEX lpcmi)
988 {
989 if (!m_cidl || !m_pDataObj)
990 return E_FAIL;
991
992 CComPtr<IDropTarget> pDT;
993 HRESULT hr = m_psf->CreateViewObject(NULL, IID_PPV_ARG(IDropTarget, &pDT));
994 if (FAILED_UNEXPECTEDLY(hr))
995 return hr;
996
997 SHSimulateDrop(pDT, m_pDataObj, MK_CONTROL|MK_SHIFT, NULL, NULL);
998
999 return S_OK;
1000 }
1001
DoDelete(LPCMINVOKECOMMANDINFOEX lpcmi)1002 HRESULT CDefaultContextMenu::DoDelete(LPCMINVOKECOMMANDINFOEX lpcmi)
1003 {
1004 if (!m_cidl || !m_pDataObj)
1005 return E_FAIL;
1006
1007 CComPtr<IDropTarget> pDT;
1008 HRESULT hr = CRecyclerDropTarget_CreateInstance(IID_PPV_ARG(IDropTarget, &pDT));
1009 if (FAILED_UNEXPECTEDLY(hr))
1010 return hr;
1011
1012 DWORD grfKeyState = (lpcmi->fMask & CMIC_MASK_SHIFT_DOWN) ? MK_SHIFT : 0;
1013 SHSimulateDrop(pDT, m_pDataObj, grfKeyState, NULL, NULL);
1014
1015 return S_OK;
1016 }
1017
DoCopyOrCut(LPCMINVOKECOMMANDINFOEX lpcmi,BOOL bCopy)1018 HRESULT CDefaultContextMenu::DoCopyOrCut(LPCMINVOKECOMMANDINFOEX lpcmi, BOOL bCopy)
1019 {
1020 if (!m_cidl || !m_pDataObj)
1021 return E_FAIL;
1022
1023 FORMATETC formatetc;
1024 InitFormatEtc(formatetc, RegisterClipboardFormatW(CFSTR_PREFERREDDROPEFFECT), TYMED_HGLOBAL);
1025 STGMEDIUM medium = {0};
1026 medium.tymed = TYMED_HGLOBAL;
1027 medium.hGlobal = GlobalAlloc(GHND, sizeof(DWORD));
1028 DWORD* pdwFlag = (DWORD*)GlobalLock(medium.hGlobal);
1029 if (pdwFlag)
1030 *pdwFlag = bCopy ? DROPEFFECT_COPY : DROPEFFECT_MOVE;
1031 GlobalUnlock(medium.hGlobal);
1032 m_pDataObj->SetData(&formatetc, &medium, TRUE);
1033
1034 CComPtr<IShellFolderView> psfv;
1035 if (SUCCEEDED(IUnknown_QueryService(m_site, SID_SFolderView, IID_PPV_ARG(IShellFolderView, &psfv))))
1036 psfv->SetPoints(m_pDataObj);
1037
1038 HRESULT hr = OleSetClipboard(m_pDataObj);
1039 if (FAILED_UNEXPECTEDLY(hr))
1040 return hr;
1041
1042 if (psfv)
1043 psfv->SetClipboard(!bCopy);
1044 return S_OK;
1045 }
1046
DoRename(LPCMINVOKECOMMANDINFOEX lpcmi)1047 HRESULT CDefaultContextMenu::DoRename(LPCMINVOKECOMMANDINFOEX lpcmi)
1048 {
1049 CComPtr<IShellBrowser> psb;
1050 HRESULT hr;
1051
1052 if (!m_site || !m_cidl)
1053 return E_FAIL;
1054
1055 /* Get a pointer to the shell browser */
1056 hr = IUnknown_QueryService(m_site, SID_IShellBrowser, IID_PPV_ARG(IShellBrowser, &psb));
1057 if (FAILED_UNEXPECTEDLY(hr))
1058 return hr;
1059
1060 CComPtr<IShellView> lpSV;
1061 hr = psb->QueryActiveShellView(&lpSV);
1062 if (FAILED_UNEXPECTEDLY(hr))
1063 return hr;
1064
1065 SVSIF selFlags = SVSI_DESELECTOTHERS | SVSI_EDIT | SVSI_ENSUREVISIBLE | SVSI_FOCUSED | SVSI_SELECT;
1066 hr = lpSV->SelectItem(m_apidl[0], selFlags);
1067 if (FAILED_UNEXPECTEDLY(hr))
1068 return hr;
1069
1070 return S_OK;
1071 }
1072
1073 HRESULT
DoProperties(LPCMINVOKECOMMANDINFOEX lpcmi)1074 CDefaultContextMenu::DoProperties(
1075 LPCMINVOKECOMMANDINFOEX lpcmi)
1076 {
1077 HRESULT hr = _DoInvokeCommandCallback(lpcmi, DFM_CMD_PROPERTIES);
1078
1079 // We are asked to run the default property sheet
1080 if (hr == S_FALSE)
1081 {
1082 return SHELL32_ShowPropertiesDialog(m_pDataObj);
1083 }
1084
1085 return hr;
1086 }
1087
1088 HRESULT
DoUndo(LPCMINVOKECOMMANDINFOEX lpcmi)1089 CDefaultContextMenu::DoUndo(LPCMINVOKECOMMANDINFOEX lpcmi)
1090 {
1091 ERR("TODO: Undo\n");
1092 return E_NOTIMPL;
1093 }
1094
1095 HRESULT
DoCopyToMoveToFolder(LPCMINVOKECOMMANDINFOEX lpici,BOOL bCopy)1096 CDefaultContextMenu::DoCopyToMoveToFolder(LPCMINVOKECOMMANDINFOEX lpici, BOOL bCopy)
1097 {
1098 HRESULT hr = E_FAIL;
1099 if (!m_pDataObj)
1100 {
1101 ERR("m_pDataObj is NULL\n");
1102 return hr;
1103 }
1104
1105 CComPtr<IContextMenu> pContextMenu;
1106 if (bCopy)
1107 hr = SHCoCreateInstance(NULL, &CLSID_CopyToMenu, NULL,
1108 IID_PPV_ARG(IContextMenu, &pContextMenu));
1109 else
1110 hr = SHCoCreateInstance(NULL, &CLSID_MoveToMenu, NULL,
1111 IID_PPV_ARG(IContextMenu, &pContextMenu));
1112 if (FAILED_UNEXPECTEDLY(hr))
1113 return hr;
1114
1115 CComPtr<IShellExtInit> pInit;
1116 hr = pContextMenu->QueryInterface(IID_PPV_ARG(IShellExtInit, &pInit));
1117 if (FAILED_UNEXPECTEDLY(hr))
1118 return hr;
1119
1120 hr = pInit->Initialize(m_pidlFolder, m_pDataObj, NULL);
1121 if (FAILED_UNEXPECTEDLY(hr))
1122 return hr;
1123
1124 if (bCopy)
1125 lpici->lpVerb = "copyto";
1126 else
1127 lpici->lpVerb = "moveto";
1128
1129 return pContextMenu->InvokeCommand((LPCMINVOKECOMMANDINFO)lpici);
1130 }
1131
1132 // This code is taken from CNewMenu and should be shared between the 2 classes
1133 HRESULT
DoCreateNewFolder(LPCMINVOKECOMMANDINFOEX lpici)1134 CDefaultContextMenu::DoCreateNewFolder(
1135 LPCMINVOKECOMMANDINFOEX lpici)
1136 {
1137 WCHAR wszPath[MAX_PATH];
1138 WCHAR wszName[MAX_PATH];
1139 WCHAR wszNewFolder[25];
1140 HRESULT hr;
1141
1142 /* Get folder path */
1143 hr = SHGetPathFromIDListW(m_pidlFolder, wszPath);
1144 if (FAILED_UNEXPECTEDLY(hr))
1145 return hr;
1146
1147 if (!LoadStringW(shell32_hInstance, IDS_NEWFOLDER, wszNewFolder, _countof(wszNewFolder)))
1148 return E_FAIL;
1149
1150 /* Create the name of the new directory */
1151 if (!PathYetAnotherMakeUniqueName(wszName, wszPath, NULL, wszNewFolder))
1152 return E_FAIL;
1153
1154 /* Create the new directory and show the appropriate dialog in case of error */
1155 if (SHCreateDirectory(lpici->hwnd, wszName) != ERROR_SUCCESS)
1156 return E_FAIL;
1157
1158 /* Show and select the new item in the def view */
1159 LPITEMIDLIST pidl;
1160 PITEMID_CHILD pidlNewItem;
1161 CComPtr<IShellView> psv;
1162
1163 /* Notify the view object about the new item */
1164 SHChangeNotify(SHCNE_MKDIR, SHCNF_PATHW | SHCNF_FLUSH, (LPCVOID)wszName, NULL);
1165
1166 if (!m_site)
1167 return S_OK;
1168
1169 /* Get a pointer to the shell view */
1170 hr = IUnknown_QueryService(m_site, SID_SFolderView, IID_PPV_ARG(IShellView, &psv));
1171 if (FAILED_UNEXPECTEDLY(hr))
1172 return S_OK;
1173
1174 /* Attempt to get the pidl of the new item */
1175 hr = SHILCreateFromPathW(wszName, &pidl, NULL);
1176 if (FAILED_UNEXPECTEDLY(hr))
1177 return hr;
1178
1179 pidlNewItem = ILFindLastID(pidl);
1180
1181 hr = psv->SelectItem(pidlNewItem, SVSI_DESELECTOTHERS | SVSI_EDIT | SVSI_ENSUREVISIBLE |
1182 SVSI_FOCUSED | SVSI_SELECT);
1183 if (FAILED_UNEXPECTEDLY(hr))
1184 return hr;
1185
1186 SHFree(pidl);
1187
1188 return S_OK;
1189 }
1190
GetDynamicEntry(UINT idCmd)1191 PDynamicShellEntry CDefaultContextMenu::GetDynamicEntry(UINT idCmd)
1192 {
1193 POSITION it = m_DynamicEntries.GetHeadPosition();
1194 while (it != NULL)
1195 {
1196 DynamicShellEntry& info = m_DynamicEntries.GetNext(it);
1197
1198 if (idCmd >= info.iIdCmdFirst + info.NumIds)
1199 continue;
1200
1201 if (idCmd < info.iIdCmdFirst || idCmd > info.iIdCmdFirst + info.NumIds)
1202 return NULL;
1203
1204 return &info;
1205 }
1206
1207 return NULL;
1208 }
1209
1210 BOOL
MapVerbToCmdId(PVOID Verb,PUINT idCmd,BOOL IsUnicode)1211 CDefaultContextMenu::MapVerbToCmdId(PVOID Verb, PUINT idCmd, BOOL IsUnicode)
1212 {
1213 WCHAR UnicodeStr[MAX_VERB];
1214
1215 /* Loop through all the static verbs looking for a match */
1216 for (UINT i = 0; i < _countof(g_StaticInvokeCmdMap); i++)
1217 {
1218 /* We can match both ANSI and unicode strings */
1219 if (IsUnicode)
1220 {
1221 /* The static verbs are ANSI, get a unicode version before doing the compare */
1222 SHAnsiToUnicode(g_StaticInvokeCmdMap[i].szStringVerb, UnicodeStr, MAX_VERB);
1223 if (!_wcsicmp(UnicodeStr, (LPWSTR)Verb))
1224 {
1225 /* Return the Corresponding Id */
1226 *idCmd = g_StaticInvokeCmdMap[i].IntVerb;
1227 return TRUE;
1228 }
1229 }
1230 else
1231 {
1232 if (!_stricmp(g_StaticInvokeCmdMap[i].szStringVerb, (LPSTR)Verb))
1233 {
1234 *idCmd = g_StaticInvokeCmdMap[i].IntVerb;
1235 return TRUE;
1236 }
1237 }
1238 }
1239
1240 for (POSITION it = m_DynamicEntries.GetHeadPosition(); it != NULL;)
1241 {
1242 DynamicShellEntry& entry = m_DynamicEntries.GetNext(it);
1243 if (!entry.NumIds)
1244 continue;
1245 HRESULT hr = ::MapVerbToCmdId(Verb, IsUnicode, entry.pCM, 0, entry.NumIds - 1);
1246 if (SUCCEEDED(hr))
1247 {
1248 *idCmd = m_iIdSHEFirst + entry.iIdCmdFirst + hr;
1249 return TRUE;
1250 }
1251 }
1252 return FALSE;
1253 }
1254
1255 HRESULT
InvokeShellExt(LPCMINVOKECOMMANDINFOEX lpcmi)1256 CDefaultContextMenu::InvokeShellExt(
1257 LPCMINVOKECOMMANDINFOEX lpcmi)
1258 {
1259 TRACE("verb %p first %x last %x\n", lpcmi->lpVerb, m_iIdSHEFirst, m_iIdSHELast);
1260
1261 UINT idCmd = LOWORD(lpcmi->lpVerb);
1262 PDynamicShellEntry pEntry = GetDynamicEntry(idCmd);
1263 if (!pEntry)
1264 return E_FAIL;
1265
1266 /* invoke the dynamic context menu */
1267 lpcmi->lpVerb = MAKEINTRESOURCEA(idCmd - pEntry->iIdCmdFirst);
1268 return pEntry->pCM->InvokeCommand((LPCMINVOKECOMMANDINFO)lpcmi);
1269 }
1270
1271 DWORD
BrowserFlagsFromVerb(LPCMINVOKECOMMANDINFOEX lpcmi,PStaticShellEntry pEntry)1272 CDefaultContextMenu::BrowserFlagsFromVerb(LPCMINVOKECOMMANDINFOEX lpcmi, PStaticShellEntry pEntry)
1273 {
1274 CComPtr<IShellBrowser> psb;
1275 HWND hwndTree;
1276 LPCWSTR FlagsName;
1277 WCHAR wszKey[sizeof("shell\\") + MAX_VERB];
1278 HRESULT hr;
1279 DWORD wFlags;
1280 DWORD cbVerb;
1281
1282 if (!m_site)
1283 return 0;
1284
1285 /* Get a pointer to the shell browser */
1286 hr = IUnknown_QueryService(m_site, SID_IShellBrowser, IID_PPV_ARG(IShellBrowser, &psb));
1287 if (FAILED(hr))
1288 return 0;
1289
1290 /* See if we are in Explore or Browse mode. If the browser's tree is present, we are in Explore mode.*/
1291 if (SUCCEEDED(psb->GetControlWindow(FCW_TREE, &hwndTree)) && hwndTree)
1292 FlagsName = L"ExplorerFlags";
1293 else
1294 FlagsName = L"BrowserFlags";
1295
1296 CComPtr<ICommDlgBrowser> pcdb;
1297 if (SUCCEEDED(psb->QueryInterface(IID_PPV_ARG(ICommDlgBrowser, &pcdb))))
1298 {
1299 if (LOBYTE(GetVersion()) < 6 || FlagsName[0] == 'E')
1300 return 0; // Don't browse in-place
1301 }
1302
1303 /* Try to get the flag from the verb */
1304 hr = StringCbPrintfW(wszKey, sizeof(wszKey), L"shell\\%s", pEntry->Verb.GetString());
1305 if (FAILED_UNEXPECTEDLY(hr))
1306 return 0;
1307
1308 cbVerb = sizeof(wFlags);
1309 if (RegGetValueW(pEntry->hkClass, wszKey, FlagsName, RRF_RT_REG_DWORD, NULL, &wFlags, &cbVerb) == ERROR_SUCCESS)
1310 {
1311 return wFlags;
1312 }
1313
1314 return 0;
1315 }
1316
1317 HRESULT
TryToBrowse(LPCMINVOKECOMMANDINFOEX lpcmi,LPCITEMIDLIST pidlChild,DWORD wFlags)1318 CDefaultContextMenu::TryToBrowse(
1319 LPCMINVOKECOMMANDINFOEX lpcmi, LPCITEMIDLIST pidlChild, DWORD wFlags)
1320 {
1321 CComPtr<IShellBrowser> psb;
1322 HRESULT hr;
1323
1324 if (!m_site)
1325 return E_FAIL;
1326
1327 /* Get a pointer to the shell browser */
1328 hr = IUnknown_QueryService(m_site, SID_IShellBrowser, IID_PPV_ARG(IShellBrowser, &psb));
1329 if (FAILED(hr))
1330 return hr;
1331
1332 PIDLIST_ABSOLUTE pidl;
1333 hr = SHILCombine(m_pidlFolder, pidlChild, &pidl);
1334 if (FAILED_UNEXPECTEDLY(hr))
1335 return hr;
1336
1337 hr = psb->BrowseObject(pidl, wFlags & ~SBSP_RELATIVE);
1338 ILFree(pidl);
1339 return hr;
1340 }
1341
1342 HRESULT
InvokePidl(LPCMINVOKECOMMANDINFOEX lpcmi,LPCITEMIDLIST pidl,PStaticShellEntry pEntry)1343 CDefaultContextMenu::InvokePidl(LPCMINVOKECOMMANDINFOEX lpcmi, LPCITEMIDLIST pidl, PStaticShellEntry pEntry)
1344 {
1345 const BOOL unicode = IsUnicode(*lpcmi);
1346
1347 LPITEMIDLIST pidlFull = ILCombine(m_pidlFolder, pidl);
1348 if (pidlFull == NULL)
1349 {
1350 return E_FAIL;
1351 }
1352
1353 WCHAR wszPath[MAX_PATH];
1354 BOOL bHasPath = SHGetPathFromIDListW(pidlFull, wszPath);
1355
1356 WCHAR wszDir[MAX_PATH];
1357
1358 SHELLEXECUTEINFOW sei = { sizeof(sei) };
1359 sei.fMask = SEE_MASK_CLASSKEY | SEE_MASK_IDLIST | (CmicFlagsToSeeFlags(lpcmi->fMask) & ~SEE_MASK_INVOKEIDLIST);
1360 sei.hwnd = lpcmi->hwnd;
1361 sei.nShow = lpcmi->nShow;
1362 sei.lpVerb = pEntry->Verb;
1363 sei.lpIDList = pidlFull;
1364 sei.hkeyClass = pEntry->hkClass;
1365 sei.dwHotKey = lpcmi->dwHotKey;
1366 sei.hIcon = lpcmi->hIcon;
1367 sei.lpDirectory = wszDir;
1368
1369 if (unicode && !StrIsNullOrEmpty(lpcmi->lpDirectoryW))
1370 {
1371 sei.lpDirectory = lpcmi->lpDirectoryW;
1372 }
1373 else if (bHasPath)
1374 {
1375 wcscpy(wszDir, wszPath);
1376 PathRemoveFileSpec(wszDir);
1377 }
1378 else
1379 {
1380 if (!SHGetPathFromIDListW(m_pidlFolder, wszDir))
1381 *wszDir = UNICODE_NULL;
1382 }
1383
1384 if (bHasPath)
1385 sei.lpFile = wszPath;
1386
1387 CComHeapPtr<WCHAR> pszParamsW;
1388 if (unicode && !StrIsNullOrEmpty(lpcmi->lpParametersW))
1389 sei.lpParameters = lpcmi->lpParametersW;
1390 else if (!StrIsNullOrEmpty(lpcmi->lpParameters) && __SHCloneStrAtoW(&pszParamsW, lpcmi->lpParameters))
1391 sei.lpParameters = pszParamsW;
1392
1393 if (!sei.lpClass && (lpcmi->fMask & (CMIC_MASK_HASLINKNAME | CMIC_MASK_HASTITLE)) && unicode)
1394 sei.lpClass = lpcmi->lpTitleW; // Forward .lnk path from CShellLink::DoOpen (for consrv STARTF_TITLEISLINKNAME)
1395
1396 ShellExecuteExW(&sei);
1397 ILFree(pidlFull);
1398
1399 return S_OK;
1400 }
1401
1402 HRESULT
InvokeRegVerb(LPCMINVOKECOMMANDINFOEX lpcmi)1403 CDefaultContextMenu::InvokeRegVerb(
1404 LPCMINVOKECOMMANDINFOEX lpcmi)
1405 {
1406 INT iCmd = LOWORD(lpcmi->lpVerb);
1407 HRESULT hr;
1408 UINT i;
1409
1410 POSITION it = m_StaticEntries.FindIndex(iCmd);
1411
1412 if (it == NULL)
1413 return E_INVALIDARG;
1414
1415 PStaticShellEntry pEntry = &m_StaticEntries.GetAt(it);
1416
1417 CRegKey VerbKey;
1418 WCHAR VerbKeyPath[sizeof("shell\\") + MAX_VERB];
1419 hr = StringCbPrintfW(VerbKeyPath, sizeof(VerbKeyPath), L"shell\\%s", pEntry->Verb.GetString());
1420 if (SUCCEEDED(hr) && m_pDataObj &&
1421 VerbKey.Open(pEntry->hkClass, VerbKeyPath, KEY_READ) == ERROR_SUCCESS)
1422 {
1423 CLSID clsid;
1424
1425 DWORD KeyState = 0;
1426 if (lpcmi->fMask & CMIC_MASK_SHIFT_DOWN)
1427 KeyState |= MK_SHIFT;
1428 if (lpcmi->fMask & CMIC_MASK_CONTROL_DOWN)
1429 KeyState |= MK_CONTROL;
1430
1431 POINTL *pPtl = NULL;
1432 C_ASSERT(sizeof(POINT) == sizeof(POINTL));
1433 if (lpcmi->fMask & CMIC_MASK_PTINVOKE)
1434 pPtl = (POINTL*)&lpcmi->ptInvoke;
1435
1436 CComPtr<IExecuteCommand> pEC;
1437 hr = SHELL_GetRegCLSID(VerbKey, L"command", L"DelegateExecute", clsid);
1438 if (SUCCEEDED(hr))
1439 hr = CoCreateInstance(clsid, NULL, CLSCTX_ALL, IID_PPV_ARG(IExecuteCommand, &pEC));
1440 if (SUCCEEDED(hr))
1441 {
1442 CComPtr<IPropertyBag> pPB;
1443 SHCreatePropertyBagOnRegKey(VerbKey, NULL, STGM_READ, IID_PPV_ARG(IPropertyBag, &pPB));
1444 return InvokeIExecuteCommandWithDataObject(pEC, pEntry->Verb.GetString(), pPB, m_pDataObj,
1445 lpcmi, static_cast<IContextMenu*>(this));
1446 }
1447
1448 CComPtr<IDropTarget> pDT;
1449 hr = SHELL_GetRegCLSID(VerbKey, L"DropTarget", L"CLSID", clsid);
1450 if (SUCCEEDED(hr))
1451 hr = CoCreateInstance(clsid, NULL, CLSCTX_ALL, IID_PPV_ARG(IDropTarget, &pDT));
1452 if (SUCCEEDED(hr))
1453 {
1454 CComPtr<IPropertyBag> pPB;
1455 SHCreatePropertyBagOnRegKey(VerbKey, NULL, STGM_READ, IID_PPV_ARG(IPropertyBag, &pPB));
1456 IUnknown_SetSite(pDT, static_cast<IContextMenu*>(this));
1457 IUnknown_InitializeCommand(pDT, pEntry->Verb.GetString(), pPB);
1458 hr = SHSimulateDrop(pDT, m_pDataObj, KeyState, pPtl, NULL);
1459 IUnknown_SetSite(pDT, NULL);
1460 return hr;
1461 }
1462 }
1463
1464 /* Get the browse flags to see if we need to browse */
1465 DWORD wFlags = BrowserFlagsFromVerb(lpcmi, pEntry);
1466
1467 for (i=0; i < m_cidl; i++)
1468 {
1469 /* Check if we need to browse */
1470 if (wFlags)
1471 {
1472 hr = TryToBrowse(lpcmi, m_apidl[i], wFlags);
1473 if (SUCCEEDED(hr))
1474 {
1475 /* In WinXP if we have browsed, we don't open any more folders.
1476 * In Win7 we browse to the first folder we find and
1477 * open new windows for each of the rest of the folders */
1478 UINT ntver = RosGetProcessEffectiveVersion();
1479 if (ntver >= _WIN32_WINNT_VISTA)
1480 wFlags = 0; // FIXME: = SBSP_NEWBROWSER | (wFlags & ~SBSP_SAMEBROWSER);
1481 else
1482 i = m_cidl;
1483
1484 continue;
1485 }
1486 }
1487
1488 InvokePidl(lpcmi, m_apidl[i], pEntry);
1489 }
1490
1491 return S_OK;
1492 }
1493
1494 HRESULT
_DoInvokeCommandCallback(LPCMINVOKECOMMANDINFOEX lpcmi,WPARAM CmdId)1495 CDefaultContextMenu::_DoInvokeCommandCallback(
1496 LPCMINVOKECOMMANDINFOEX lpcmi, WPARAM CmdId)
1497 {
1498 BOOL Unicode = IsUnicode(*lpcmi);
1499 WCHAR lParamBuf[MAX_PATH];
1500 LPARAM lParam = 0;
1501
1502 if (Unicode && lpcmi->lpParametersW)
1503 lParam = (LPARAM)lpcmi->lpParametersW;
1504 else if (lpcmi->lpParameters)
1505 lParam = SHAnsiToUnicode(lpcmi->lpParameters, lParamBuf, _countof(lParamBuf)) ? (LPARAM)lParamBuf : 0;
1506
1507 HRESULT hr;
1508 #if 0 // TODO: Try DFM_INVOKECOMMANDEX first.
1509 DFMICS dfmics = { sizeof(DFMICS), lpcmi->fMask, lParam, m_iIdSCMFirst?, m_iIdDfltLast?, (LPCMINVOKECOMMANDINFO)lpcmi, m_site };
1510 hr = _DoCallback(DFM_INVOKECOMMANDEX, CmdId, &dfmics);
1511 if (hr == E_NOTIMPL)
1512 #endif
1513 hr = _DoCallback(DFM_INVOKECOMMAND, CmdId, (void*)lParam);
1514 return hr;
1515 }
1516
1517 HRESULT
1518 WINAPI
InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)1519 CDefaultContextMenu::InvokeCommand(
1520 LPCMINVOKECOMMANDINFO lpcmi)
1521 {
1522 CMINVOKECOMMANDINFOEX LocalInvokeInfo = {};
1523 HRESULT Result;
1524 UINT CmdId;
1525
1526 /* Take a local copy of the fixed members of the
1527 struct as we might need to modify the verb */
1528 memcpy(&LocalInvokeInfo, lpcmi, min(sizeof(LocalInvokeInfo), lpcmi->cbSize));
1529
1530 /* Check if this is a string verb */
1531 if (!IS_INTRESOURCE(LocalInvokeInfo.lpVerb))
1532 {
1533 /* Get the ID which corresponds to this verb, and update our local copy */
1534 if (MapVerbToCmdId((LPVOID)LocalInvokeInfo.lpVerb, &CmdId, FALSE))
1535 LocalInvokeInfo.lpVerb = MAKEINTRESOURCEA(CmdId);
1536 else
1537 return E_INVALIDARG;
1538 }
1539
1540 CmdId = LOWORD(LocalInvokeInfo.lpVerb);
1541
1542 if (!m_DynamicEntries.IsEmpty() && CmdId >= m_iIdSHEFirst && CmdId < m_iIdSHELast)
1543 {
1544 LocalInvokeInfo.lpVerb -= m_iIdSHEFirst;
1545 Result = InvokeShellExt(&LocalInvokeInfo);
1546 return Result;
1547 }
1548
1549 if (!m_StaticEntries.IsEmpty() && CmdId >= m_iIdSCMFirst && CmdId < m_iIdSCMLast)
1550 {
1551 LocalInvokeInfo.lpVerb -= m_iIdSCMFirst;
1552 Result = InvokeRegVerb(&LocalInvokeInfo);
1553 // TODO: if (FAILED(Result) && !(lpcmi->fMask & CMIC_MASK_FLAG_NO_UI)) SHELL_ErrorBox(m_pSite, Result);
1554 return Result;
1555 }
1556
1557 if (m_iIdCBFirst != m_iIdCBLast && CmdId >= m_iIdCBFirst && CmdId < m_iIdCBLast)
1558 {
1559 Result = _DoInvokeCommandCallback(&LocalInvokeInfo, CmdId - m_iIdCBFirst);
1560 return Result;
1561 }
1562
1563 if (m_iIdDfltFirst != m_iIdDfltLast && CmdId >= m_iIdDfltFirst && CmdId < m_iIdDfltLast)
1564 {
1565 CmdId -= m_iIdDfltFirst;
1566 /* See the definitions of IDM_CUT and co to see how this works */
1567 CmdId += DCM_FCIDM_SHVIEW_OFFSET;
1568 }
1569
1570 if (LocalInvokeInfo.cbSize >= sizeof(CMINVOKECOMMANDINFOEX) && (LocalInvokeInfo.fMask & CMIC_MASK_PTINVOKE))
1571 {
1572 if (m_pDataObj && FAILED_UNEXPECTEDLY(DataObject_SetOffset(m_pDataObj, &LocalInvokeInfo.ptInvoke)))
1573 {
1574 ERR("Unable to add OFFSET to DataObject!\n");
1575 }
1576 }
1577
1578 /* Check if this is a Id */
1579 switch (CmdId)
1580 {
1581 case FCIDM_SHVIEW_INSERT:
1582 Result = DoPaste(&LocalInvokeInfo, FALSE);
1583 break;
1584 case FCIDM_SHVIEW_INSERTLINK:
1585 Result = DoPaste(&LocalInvokeInfo, TRUE);
1586 break;
1587 case FCIDM_SHVIEW_OPEN:
1588 case FCIDM_SHVIEW_EXPLORE:
1589 Result = DoOpenOrExplore(&LocalInvokeInfo);
1590 break;
1591 case FCIDM_SHVIEW_COPY:
1592 case FCIDM_SHVIEW_CUT:
1593 Result = DoCopyOrCut(&LocalInvokeInfo, CmdId == FCIDM_SHVIEW_COPY);
1594 break;
1595 case FCIDM_SHVIEW_CREATELINK:
1596 Result = DoCreateLink(&LocalInvokeInfo);
1597 break;
1598 case FCIDM_SHVIEW_DELETE:
1599 Result = DoDelete(&LocalInvokeInfo);
1600 break;
1601 case FCIDM_SHVIEW_RENAME:
1602 Result = DoRename(&LocalInvokeInfo);
1603 break;
1604 case FCIDM_SHVIEW_PROPERTIES:
1605 Result = DoProperties(&LocalInvokeInfo);
1606 break;
1607 case FCIDM_SHVIEW_NEWFOLDER:
1608 Result = DoCreateNewFolder(&LocalInvokeInfo);
1609 break;
1610 case FCIDM_SHVIEW_COPYTO:
1611 Result = DoCopyToMoveToFolder(&LocalInvokeInfo, TRUE);
1612 break;
1613 case FCIDM_SHVIEW_MOVETO:
1614 Result = DoCopyToMoveToFolder(&LocalInvokeInfo, FALSE);
1615 break;
1616 case FCIDM_SHVIEW_UNDO:
1617 Result = DoUndo(&LocalInvokeInfo);
1618 break;
1619 default:
1620 Result = E_INVALIDARG;
1621 ERR("Unhandled Verb %xl\n", LOWORD(LocalInvokeInfo.lpVerb));
1622 break;
1623 }
1624
1625 return Result;
1626 }
1627
1628 HRESULT
1629 WINAPI
GetCommandString(UINT_PTR idCommand,UINT uFlags,UINT * lpReserved,LPSTR lpszName,UINT uMaxNameLen)1630 CDefaultContextMenu::GetCommandString(
1631 UINT_PTR idCommand,
1632 UINT uFlags,
1633 UINT* lpReserved,
1634 LPSTR lpszName,
1635 UINT uMaxNameLen)
1636 {
1637 /* We don't handle the help text yet */
1638 if (uFlags == GCS_HELPTEXTA ||
1639 uFlags == GCS_HELPTEXTW ||
1640 HIWORD(idCommand) != 0)
1641 {
1642 return E_NOTIMPL;
1643 }
1644
1645 UINT CmdId = LOWORD(idCommand);
1646
1647 if (!m_DynamicEntries.IsEmpty() && CmdId >= m_iIdSHEFirst && CmdId < m_iIdSHELast)
1648 {
1649 idCommand -= m_iIdSHEFirst;
1650 PDynamicShellEntry pEntry = GetDynamicEntry(idCommand);
1651 if (!pEntry)
1652 return E_FAIL;
1653
1654 idCommand -= pEntry->iIdCmdFirst;
1655 return pEntry->pCM->GetCommandString(idCommand,
1656 uFlags,
1657 lpReserved,
1658 lpszName,
1659 uMaxNameLen);
1660 }
1661
1662 if (!m_StaticEntries.IsEmpty() && CmdId >= m_iIdSCMFirst && CmdId < m_iIdSCMLast)
1663 {
1664 /* Validation just returns S_OK on a match. The id exists. */
1665 if (uFlags == GCS_VALIDATEA || uFlags == GCS_VALIDATEW)
1666 return S_OK;
1667
1668 CmdId -= m_iIdSCMFirst;
1669
1670 POSITION it = m_StaticEntries.FindIndex(CmdId);
1671
1672 if (it == NULL)
1673 return E_INVALIDARG;
1674
1675 PStaticShellEntry pEntry = &m_StaticEntries.GetAt(it);
1676
1677 if (uFlags == GCS_VERBW)
1678 return StringCchCopyW((LPWSTR)lpszName, uMaxNameLen, pEntry->Verb);
1679
1680 if (uFlags == GCS_VERBA)
1681 {
1682 if (SHUnicodeToAnsi(pEntry->Verb, lpszName, uMaxNameLen))
1683 return S_OK;
1684 }
1685
1686 return E_INVALIDARG;
1687 }
1688
1689 //FIXME: Should we handle callbacks here?
1690 if (m_iIdDfltFirst != m_iIdDfltLast && CmdId >= m_iIdDfltFirst && CmdId < m_iIdDfltLast)
1691 {
1692 CmdId -= m_iIdDfltFirst;
1693 /* See the definitions of IDM_CUT and co to see how this works */
1694 CmdId += DCM_FCIDM_SHVIEW_OFFSET;
1695 }
1696
1697 /* Loop looking for a matching Id */
1698 for (UINT i = 0; i < _countof(g_StaticInvokeCmdMap); i++)
1699 {
1700 if (g_StaticInvokeCmdMap[i].IntVerb == CmdId)
1701 {
1702 /* Validation just returns S_OK on a match */
1703 if (uFlags == GCS_VALIDATEA || uFlags == GCS_VALIDATEW)
1704 return S_OK;
1705
1706 /* Return a copy of the ANSI verb */
1707 if (uFlags == GCS_VERBA)
1708 return StringCchCopyA(lpszName, uMaxNameLen, g_StaticInvokeCmdMap[i].szStringVerb);
1709
1710 /* Convert the ANSI verb to unicode and return that */
1711 if (uFlags == GCS_VERBW)
1712 {
1713 if (SHAnsiToUnicode(g_StaticInvokeCmdMap[i].szStringVerb, (LPWSTR)lpszName, uMaxNameLen))
1714 return S_OK;
1715 }
1716 }
1717 }
1718
1719 return E_INVALIDARG;
1720 }
1721
1722 HRESULT
1723 WINAPI
HandleMenuMsg(UINT uMsg,WPARAM wParam,LPARAM lParam)1724 CDefaultContextMenu::HandleMenuMsg(
1725 UINT uMsg,
1726 WPARAM wParam,
1727 LPARAM lParam)
1728 {
1729 /* FIXME: Should we implement this as well? */
1730 return S_OK;
1731 }
1732
SHGetMenuIdFromMenuMsg(UINT uMsg,LPARAM lParam,UINT * CmdId)1733 HRESULT SHGetMenuIdFromMenuMsg(UINT uMsg, LPARAM lParam, UINT *CmdId)
1734 {
1735 if (uMsg == WM_DRAWITEM)
1736 {
1737 DRAWITEMSTRUCT* pDrawStruct = reinterpret_cast<DRAWITEMSTRUCT*>(lParam);
1738 *CmdId = pDrawStruct->itemID;
1739 return S_OK;
1740 }
1741 else if (uMsg == WM_MEASUREITEM)
1742 {
1743 MEASUREITEMSTRUCT* pMeasureStruct = reinterpret_cast<MEASUREITEMSTRUCT*>(lParam);
1744 *CmdId = pMeasureStruct->itemID;
1745 return S_OK;
1746 }
1747
1748 return E_FAIL;
1749 }
1750
SHSetMenuIdInMenuMsg(UINT uMsg,LPARAM lParam,UINT CmdId)1751 HRESULT SHSetMenuIdInMenuMsg(UINT uMsg, LPARAM lParam, UINT CmdId)
1752 {
1753 if (uMsg == WM_DRAWITEM)
1754 {
1755 DRAWITEMSTRUCT* pDrawStruct = reinterpret_cast<DRAWITEMSTRUCT*>(lParam);
1756 pDrawStruct->itemID = CmdId;
1757 return S_OK;
1758 }
1759 else if (uMsg == WM_MEASUREITEM)
1760 {
1761 MEASUREITEMSTRUCT* pMeasureStruct = reinterpret_cast<MEASUREITEMSTRUCT*>(lParam);
1762 pMeasureStruct->itemID = CmdId;
1763 return S_OK;
1764 }
1765
1766 return E_FAIL;
1767 }
1768
1769 HRESULT
1770 WINAPI
HandleMenuMsg2(UINT uMsg,WPARAM wParam,LPARAM lParam,LRESULT * plResult)1771 CDefaultContextMenu::HandleMenuMsg2(
1772 UINT uMsg,
1773 WPARAM wParam,
1774 LPARAM lParam,
1775 LRESULT *plResult)
1776 {
1777 if (uMsg == WM_INITMENUPOPUP)
1778 {
1779 POSITION it = m_DynamicEntries.GetHeadPosition();
1780 while (it != NULL)
1781 {
1782 DynamicShellEntry& info = m_DynamicEntries.GetNext(it);
1783 SHForwardContextMenuMsg(info.pCM, uMsg, wParam, lParam, plResult, TRUE);
1784 }
1785 return S_OK;
1786 }
1787
1788 UINT CmdId;
1789 HRESULT hr = SHGetMenuIdFromMenuMsg(uMsg, lParam, &CmdId);
1790 if (FAILED(hr))
1791 return S_FALSE;
1792
1793 if (CmdId < m_iIdSHEFirst || CmdId >= m_iIdSHELast)
1794 return S_FALSE;
1795
1796 CmdId -= m_iIdSHEFirst;
1797 PDynamicShellEntry pEntry = GetDynamicEntry(CmdId);
1798 if (pEntry)
1799 {
1800 SHSetMenuIdInMenuMsg(uMsg, lParam, CmdId - pEntry->iIdCmdFirst);
1801 SHForwardContextMenuMsg(pEntry->pCM, uMsg, wParam, lParam, plResult, TRUE);
1802 }
1803
1804 return S_OK;
1805 }
1806
1807 HRESULT
1808 WINAPI
SetSite(IUnknown * pUnkSite)1809 CDefaultContextMenu::SetSite(IUnknown *pUnkSite)
1810 {
1811 m_site = pUnkSite;
1812 return S_OK;
1813 }
1814
1815 HRESULT
1816 WINAPI
GetSite(REFIID riid,void ** ppvSite)1817 CDefaultContextMenu::GetSite(REFIID riid, void **ppvSite)
1818 {
1819 if (!m_site)
1820 return E_FAIL;
1821
1822 return m_site->QueryInterface(riid, ppvSite);
1823 }
1824
1825 static
1826 HRESULT
CDefaultContextMenu_CreateInstance(const DEFCONTEXTMENU * pdcm,LPFNDFMCALLBACK lpfn,REFIID riid,void ** ppv)1827 CDefaultContextMenu_CreateInstance(const DEFCONTEXTMENU *pdcm, LPFNDFMCALLBACK lpfn, REFIID riid, void **ppv)
1828 {
1829 return ShellObjectCreatorInit<CDefaultContextMenu>(pdcm, lpfn, riid, ppv);
1830 }
1831
1832 /*************************************************************************
1833 * SHCreateDefaultContextMenu [SHELL32.325] Vista API
1834 *
1835 */
1836
1837 HRESULT
1838 WINAPI
SHCreateDefaultContextMenu(const DEFCONTEXTMENU * pdcm,REFIID riid,void ** ppv)1839 SHCreateDefaultContextMenu(const DEFCONTEXTMENU *pdcm, REFIID riid, void **ppv)
1840 {
1841 HRESULT hr;
1842
1843 if (!ppv)
1844 return E_INVALIDARG;
1845
1846 hr = CDefaultContextMenu_CreateInstance(pdcm, NULL, riid, ppv);
1847 if (FAILED_UNEXPECTEDLY(hr))
1848 return hr;
1849
1850 return S_OK;
1851 }
1852
1853 /*************************************************************************
1854 * CDefFolderMenu_Create2 [SHELL32.701]
1855 *
1856 */
1857
1858 HRESULT
1859 WINAPI
CDefFolderMenu_Create2(PCIDLIST_ABSOLUTE pidlFolder,HWND hwnd,UINT cidl,PCUITEMID_CHILD_ARRAY apidl,IShellFolder * psf,LPFNDFMCALLBACK lpfn,UINT nKeys,const HKEY * ahkeyClsKeys,IContextMenu ** ppcm)1860 CDefFolderMenu_Create2(
1861 PCIDLIST_ABSOLUTE pidlFolder,
1862 HWND hwnd,
1863 UINT cidl,
1864 PCUITEMID_CHILD_ARRAY apidl,
1865 IShellFolder *psf,
1866 LPFNDFMCALLBACK lpfn,
1867 UINT nKeys,
1868 const HKEY *ahkeyClsKeys,
1869 IContextMenu **ppcm)
1870 {
1871 DEFCONTEXTMENU dcm;
1872 dcm.hwnd = hwnd;
1873 dcm.pcmcb = NULL;
1874 dcm.pidlFolder = pidlFolder;
1875 dcm.psf = psf;
1876 dcm.cidl = cidl;
1877 dcm.apidl = apidl;
1878 dcm.punkAssociationInfo = NULL;
1879 dcm.cKeys = nKeys;
1880 dcm.aKeys = ahkeyClsKeys;
1881
1882 HRESULT hr = CDefaultContextMenu_CreateInstance(&dcm, lpfn, IID_PPV_ARG(IContextMenu, ppcm));
1883 if (FAILED_UNEXPECTEDLY(hr))
1884 return hr;
1885
1886 return S_OK;
1887 }
1888