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