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
QueryService(REFGUID svc,REFIID riid,void ** ppv)222 virtual HRESULT STDMETHODCALLTYPE QueryService(REFGUID svc, REFIID riid, void**ppv)
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 HRESULT hr = OleSetClipboard(m_pDataObj);
1035 if (FAILED_UNEXPECTEDLY(hr))
1036 return hr;
1037
1038 return S_OK;
1039 }
1040
DoRename(LPCMINVOKECOMMANDINFOEX lpcmi)1041 HRESULT CDefaultContextMenu::DoRename(LPCMINVOKECOMMANDINFOEX lpcmi)
1042 {
1043 CComPtr<IShellBrowser> psb;
1044 HRESULT hr;
1045
1046 if (!m_site || !m_cidl)
1047 return E_FAIL;
1048
1049 /* Get a pointer to the shell browser */
1050 hr = IUnknown_QueryService(m_site, SID_IShellBrowser, IID_PPV_ARG(IShellBrowser, &psb));
1051 if (FAILED_UNEXPECTEDLY(hr))
1052 return hr;
1053
1054 CComPtr<IShellView> lpSV;
1055 hr = psb->QueryActiveShellView(&lpSV);
1056 if (FAILED_UNEXPECTEDLY(hr))
1057 return hr;
1058
1059 SVSIF selFlags = SVSI_DESELECTOTHERS | SVSI_EDIT | SVSI_ENSUREVISIBLE | SVSI_FOCUSED | SVSI_SELECT;
1060 hr = lpSV->SelectItem(m_apidl[0], selFlags);
1061 if (FAILED_UNEXPECTEDLY(hr))
1062 return hr;
1063
1064 return S_OK;
1065 }
1066
1067 HRESULT
DoProperties(LPCMINVOKECOMMANDINFOEX lpcmi)1068 CDefaultContextMenu::DoProperties(
1069 LPCMINVOKECOMMANDINFOEX lpcmi)
1070 {
1071 HRESULT hr = _DoInvokeCommandCallback(lpcmi, DFM_CMD_PROPERTIES);
1072
1073 // We are asked to run the default property sheet
1074 if (hr == S_FALSE)
1075 {
1076 return SHELL32_ShowPropertiesDialog(m_pDataObj);
1077 }
1078
1079 return hr;
1080 }
1081
1082 HRESULT
DoUndo(LPCMINVOKECOMMANDINFOEX lpcmi)1083 CDefaultContextMenu::DoUndo(LPCMINVOKECOMMANDINFOEX lpcmi)
1084 {
1085 ERR("TODO: Undo\n");
1086 return E_NOTIMPL;
1087 }
1088
1089 HRESULT
DoCopyToMoveToFolder(LPCMINVOKECOMMANDINFOEX lpici,BOOL bCopy)1090 CDefaultContextMenu::DoCopyToMoveToFolder(LPCMINVOKECOMMANDINFOEX lpici, BOOL bCopy)
1091 {
1092 HRESULT hr = E_FAIL;
1093 if (!m_pDataObj)
1094 {
1095 ERR("m_pDataObj is NULL\n");
1096 return hr;
1097 }
1098
1099 CComPtr<IContextMenu> pContextMenu;
1100 if (bCopy)
1101 hr = SHCoCreateInstance(NULL, &CLSID_CopyToMenu, NULL,
1102 IID_PPV_ARG(IContextMenu, &pContextMenu));
1103 else
1104 hr = SHCoCreateInstance(NULL, &CLSID_MoveToMenu, NULL,
1105 IID_PPV_ARG(IContextMenu, &pContextMenu));
1106 if (FAILED_UNEXPECTEDLY(hr))
1107 return hr;
1108
1109 CComPtr<IShellExtInit> pInit;
1110 hr = pContextMenu->QueryInterface(IID_PPV_ARG(IShellExtInit, &pInit));
1111 if (FAILED_UNEXPECTEDLY(hr))
1112 return hr;
1113
1114 hr = pInit->Initialize(m_pidlFolder, m_pDataObj, NULL);
1115 if (FAILED_UNEXPECTEDLY(hr))
1116 return hr;
1117
1118 if (bCopy)
1119 lpici->lpVerb = "copyto";
1120 else
1121 lpici->lpVerb = "moveto";
1122
1123 return pContextMenu->InvokeCommand((LPCMINVOKECOMMANDINFO)lpici);
1124 }
1125
1126 // This code is taken from CNewMenu and should be shared between the 2 classes
1127 HRESULT
DoCreateNewFolder(LPCMINVOKECOMMANDINFOEX lpici)1128 CDefaultContextMenu::DoCreateNewFolder(
1129 LPCMINVOKECOMMANDINFOEX lpici)
1130 {
1131 WCHAR wszPath[MAX_PATH];
1132 WCHAR wszName[MAX_PATH];
1133 WCHAR wszNewFolder[25];
1134 HRESULT hr;
1135
1136 /* Get folder path */
1137 hr = SHGetPathFromIDListW(m_pidlFolder, wszPath);
1138 if (FAILED_UNEXPECTEDLY(hr))
1139 return hr;
1140
1141 if (!LoadStringW(shell32_hInstance, IDS_NEWFOLDER, wszNewFolder, _countof(wszNewFolder)))
1142 return E_FAIL;
1143
1144 /* Create the name of the new directory */
1145 if (!PathYetAnotherMakeUniqueName(wszName, wszPath, NULL, wszNewFolder))
1146 return E_FAIL;
1147
1148 /* Create the new directory and show the appropriate dialog in case of error */
1149 if (SHCreateDirectory(lpici->hwnd, wszName) != ERROR_SUCCESS)
1150 return E_FAIL;
1151
1152 /* Show and select the new item in the def view */
1153 LPITEMIDLIST pidl;
1154 PITEMID_CHILD pidlNewItem;
1155 CComPtr<IShellView> psv;
1156
1157 /* Notify the view object about the new item */
1158 SHChangeNotify(SHCNE_MKDIR, SHCNF_PATHW | SHCNF_FLUSH, (LPCVOID)wszName, NULL);
1159
1160 if (!m_site)
1161 return S_OK;
1162
1163 /* Get a pointer to the shell view */
1164 hr = IUnknown_QueryService(m_site, SID_IFolderView, IID_PPV_ARG(IShellView, &psv));
1165 if (FAILED_UNEXPECTEDLY(hr))
1166 return S_OK;
1167
1168 /* Attempt to get the pidl of the new item */
1169 hr = SHILCreateFromPathW(wszName, &pidl, NULL);
1170 if (FAILED_UNEXPECTEDLY(hr))
1171 return hr;
1172
1173 pidlNewItem = ILFindLastID(pidl);
1174
1175 hr = psv->SelectItem(pidlNewItem, SVSI_DESELECTOTHERS | SVSI_EDIT | SVSI_ENSUREVISIBLE |
1176 SVSI_FOCUSED | SVSI_SELECT);
1177 if (FAILED_UNEXPECTEDLY(hr))
1178 return hr;
1179
1180 SHFree(pidl);
1181
1182 return S_OK;
1183 }
1184
GetDynamicEntry(UINT idCmd)1185 PDynamicShellEntry CDefaultContextMenu::GetDynamicEntry(UINT idCmd)
1186 {
1187 POSITION it = m_DynamicEntries.GetHeadPosition();
1188 while (it != NULL)
1189 {
1190 DynamicShellEntry& info = m_DynamicEntries.GetNext(it);
1191
1192 if (idCmd >= info.iIdCmdFirst + info.NumIds)
1193 continue;
1194
1195 if (idCmd < info.iIdCmdFirst || idCmd > info.iIdCmdFirst + info.NumIds)
1196 return NULL;
1197
1198 return &info;
1199 }
1200
1201 return NULL;
1202 }
1203
1204 BOOL
MapVerbToCmdId(PVOID Verb,PUINT idCmd,BOOL IsUnicode)1205 CDefaultContextMenu::MapVerbToCmdId(PVOID Verb, PUINT idCmd, BOOL IsUnicode)
1206 {
1207 WCHAR UnicodeStr[MAX_VERB];
1208
1209 /* Loop through all the static verbs looking for a match */
1210 for (UINT i = 0; i < _countof(g_StaticInvokeCmdMap); i++)
1211 {
1212 /* We can match both ANSI and unicode strings */
1213 if (IsUnicode)
1214 {
1215 /* The static verbs are ANSI, get a unicode version before doing the compare */
1216 SHAnsiToUnicode(g_StaticInvokeCmdMap[i].szStringVerb, UnicodeStr, MAX_VERB);
1217 if (!_wcsicmp(UnicodeStr, (LPWSTR)Verb))
1218 {
1219 /* Return the Corresponding Id */
1220 *idCmd = g_StaticInvokeCmdMap[i].IntVerb;
1221 return TRUE;
1222 }
1223 }
1224 else
1225 {
1226 if (!_stricmp(g_StaticInvokeCmdMap[i].szStringVerb, (LPSTR)Verb))
1227 {
1228 *idCmd = g_StaticInvokeCmdMap[i].IntVerb;
1229 return TRUE;
1230 }
1231 }
1232 }
1233
1234 for (POSITION it = m_DynamicEntries.GetHeadPosition(); it != NULL;)
1235 {
1236 DynamicShellEntry& entry = m_DynamicEntries.GetNext(it);
1237 if (!entry.NumIds)
1238 continue;
1239 HRESULT hr = ::MapVerbToCmdId(Verb, IsUnicode, entry.pCM, 0, entry.NumIds - 1);
1240 if (SUCCEEDED(hr))
1241 {
1242 *idCmd = m_iIdSHEFirst + entry.iIdCmdFirst + hr;
1243 return TRUE;
1244 }
1245 }
1246 return FALSE;
1247 }
1248
1249 HRESULT
InvokeShellExt(LPCMINVOKECOMMANDINFOEX lpcmi)1250 CDefaultContextMenu::InvokeShellExt(
1251 LPCMINVOKECOMMANDINFOEX lpcmi)
1252 {
1253 TRACE("verb %p first %x last %x\n", lpcmi->lpVerb, m_iIdSHEFirst, m_iIdSHELast);
1254
1255 UINT idCmd = LOWORD(lpcmi->lpVerb);
1256 PDynamicShellEntry pEntry = GetDynamicEntry(idCmd);
1257 if (!pEntry)
1258 return E_FAIL;
1259
1260 /* invoke the dynamic context menu */
1261 lpcmi->lpVerb = MAKEINTRESOURCEA(idCmd - pEntry->iIdCmdFirst);
1262 return pEntry->pCM->InvokeCommand((LPCMINVOKECOMMANDINFO)lpcmi);
1263 }
1264
1265 DWORD
BrowserFlagsFromVerb(LPCMINVOKECOMMANDINFOEX lpcmi,PStaticShellEntry pEntry)1266 CDefaultContextMenu::BrowserFlagsFromVerb(LPCMINVOKECOMMANDINFOEX lpcmi, PStaticShellEntry pEntry)
1267 {
1268 CComPtr<IShellBrowser> psb;
1269 HWND hwndTree;
1270 LPCWSTR FlagsName;
1271 WCHAR wszKey[sizeof("shell\\") + MAX_VERB];
1272 HRESULT hr;
1273 DWORD wFlags;
1274 DWORD cbVerb;
1275
1276 if (!m_site)
1277 return 0;
1278
1279 /* Get a pointer to the shell browser */
1280 hr = IUnknown_QueryService(m_site, SID_IShellBrowser, IID_PPV_ARG(IShellBrowser, &psb));
1281 if (FAILED(hr))
1282 return 0;
1283
1284 /* See if we are in Explore or Browse mode. If the browser's tree is present, we are in Explore mode.*/
1285 if (SUCCEEDED(psb->GetControlWindow(FCW_TREE, &hwndTree)) && hwndTree)
1286 FlagsName = L"ExplorerFlags";
1287 else
1288 FlagsName = L"BrowserFlags";
1289
1290 CComPtr<ICommDlgBrowser> pcdb;
1291 if (SUCCEEDED(psb->QueryInterface(IID_PPV_ARG(ICommDlgBrowser, &pcdb))))
1292 {
1293 if (LOBYTE(GetVersion()) < 6 || FlagsName[0] == 'E')
1294 return 0; // Don't browse in-place
1295 }
1296
1297 /* Try to get the flag from the verb */
1298 hr = StringCbPrintfW(wszKey, sizeof(wszKey), L"shell\\%s", pEntry->Verb.GetString());
1299 if (FAILED_UNEXPECTEDLY(hr))
1300 return 0;
1301
1302 cbVerb = sizeof(wFlags);
1303 if (RegGetValueW(pEntry->hkClass, wszKey, FlagsName, RRF_RT_REG_DWORD, NULL, &wFlags, &cbVerb) == ERROR_SUCCESS)
1304 {
1305 return wFlags;
1306 }
1307
1308 return 0;
1309 }
1310
1311 HRESULT
TryToBrowse(LPCMINVOKECOMMANDINFOEX lpcmi,LPCITEMIDLIST pidlChild,DWORD wFlags)1312 CDefaultContextMenu::TryToBrowse(
1313 LPCMINVOKECOMMANDINFOEX lpcmi, LPCITEMIDLIST pidlChild, DWORD wFlags)
1314 {
1315 CComPtr<IShellBrowser> psb;
1316 HRESULT hr;
1317
1318 if (!m_site)
1319 return E_FAIL;
1320
1321 /* Get a pointer to the shell browser */
1322 hr = IUnknown_QueryService(m_site, SID_IShellBrowser, IID_PPV_ARG(IShellBrowser, &psb));
1323 if (FAILED(hr))
1324 return hr;
1325
1326 PIDLIST_ABSOLUTE pidl;
1327 hr = SHILCombine(m_pidlFolder, pidlChild, &pidl);
1328 if (FAILED_UNEXPECTEDLY(hr))
1329 return hr;
1330
1331 hr = psb->BrowseObject(pidl, wFlags & ~SBSP_RELATIVE);
1332 ILFree(pidl);
1333 return hr;
1334 }
1335
1336 HRESULT
InvokePidl(LPCMINVOKECOMMANDINFOEX lpcmi,LPCITEMIDLIST pidl,PStaticShellEntry pEntry)1337 CDefaultContextMenu::InvokePidl(LPCMINVOKECOMMANDINFOEX lpcmi, LPCITEMIDLIST pidl, PStaticShellEntry pEntry)
1338 {
1339 const BOOL unicode = IsUnicode(*lpcmi);
1340
1341 LPITEMIDLIST pidlFull = ILCombine(m_pidlFolder, pidl);
1342 if (pidlFull == NULL)
1343 {
1344 return E_FAIL;
1345 }
1346
1347 WCHAR wszPath[MAX_PATH];
1348 BOOL bHasPath = SHGetPathFromIDListW(pidlFull, wszPath);
1349
1350 WCHAR wszDir[MAX_PATH];
1351
1352 SHELLEXECUTEINFOW sei = { sizeof(sei) };
1353 sei.fMask = SEE_MASK_CLASSKEY | SEE_MASK_IDLIST | (CmicFlagsToSeeFlags(lpcmi->fMask) & ~SEE_MASK_INVOKEIDLIST);
1354 sei.hwnd = lpcmi->hwnd;
1355 sei.nShow = lpcmi->nShow;
1356 sei.lpVerb = pEntry->Verb;
1357 sei.lpIDList = pidlFull;
1358 sei.hkeyClass = pEntry->hkClass;
1359 sei.dwHotKey = lpcmi->dwHotKey;
1360 sei.hIcon = lpcmi->hIcon;
1361 sei.lpDirectory = wszDir;
1362
1363 if (unicode && !StrIsNullOrEmpty(lpcmi->lpDirectoryW))
1364 {
1365 sei.lpDirectory = lpcmi->lpDirectoryW;
1366 }
1367 else if (bHasPath)
1368 {
1369 wcscpy(wszDir, wszPath);
1370 PathRemoveFileSpec(wszDir);
1371 }
1372 else
1373 {
1374 if (!SHGetPathFromIDListW(m_pidlFolder, wszDir))
1375 *wszDir = UNICODE_NULL;
1376 }
1377
1378 if (bHasPath)
1379 sei.lpFile = wszPath;
1380
1381 CComHeapPtr<WCHAR> pszParamsW;
1382 if (unicode && !StrIsNullOrEmpty(lpcmi->lpParametersW))
1383 sei.lpParameters = lpcmi->lpParametersW;
1384 else if (!StrIsNullOrEmpty(lpcmi->lpParameters) && __SHCloneStrAtoW(&pszParamsW, lpcmi->lpParameters))
1385 sei.lpParameters = pszParamsW;
1386
1387 if (!sei.lpClass && (lpcmi->fMask & (CMIC_MASK_HASLINKNAME | CMIC_MASK_HASTITLE)) && unicode)
1388 sei.lpClass = lpcmi->lpTitleW; // Forward .lnk path from CShellLink::DoOpen (for consrv STARTF_TITLEISLINKNAME)
1389
1390 ShellExecuteExW(&sei);
1391 ILFree(pidlFull);
1392
1393 return S_OK;
1394 }
1395
1396 HRESULT
InvokeRegVerb(LPCMINVOKECOMMANDINFOEX lpcmi)1397 CDefaultContextMenu::InvokeRegVerb(
1398 LPCMINVOKECOMMANDINFOEX lpcmi)
1399 {
1400 INT iCmd = LOWORD(lpcmi->lpVerb);
1401 HRESULT hr;
1402 UINT i;
1403
1404 POSITION it = m_StaticEntries.FindIndex(iCmd);
1405
1406 if (it == NULL)
1407 return E_INVALIDARG;
1408
1409 PStaticShellEntry pEntry = &m_StaticEntries.GetAt(it);
1410
1411 CRegKey VerbKey;
1412 WCHAR VerbKeyPath[sizeof("shell\\") + MAX_VERB];
1413 hr = StringCbPrintfW(VerbKeyPath, sizeof(VerbKeyPath), L"shell\\%s", pEntry->Verb.GetString());
1414 if (SUCCEEDED(hr) && m_pDataObj &&
1415 VerbKey.Open(pEntry->hkClass, VerbKeyPath, KEY_READ) == ERROR_SUCCESS)
1416 {
1417 CLSID clsid;
1418
1419 DWORD KeyState = 0;
1420 if (lpcmi->fMask & CMIC_MASK_SHIFT_DOWN)
1421 KeyState |= MK_SHIFT;
1422 if (lpcmi->fMask & CMIC_MASK_CONTROL_DOWN)
1423 KeyState |= MK_CONTROL;
1424
1425 POINTL *pPtl = NULL;
1426 C_ASSERT(sizeof(POINT) == sizeof(POINTL));
1427 if (lpcmi->fMask & CMIC_MASK_PTINVOKE)
1428 pPtl = (POINTL*)&lpcmi->ptInvoke;
1429
1430 CComPtr<IExecuteCommand> pEC;
1431 hr = SHELL_GetRegCLSID(VerbKey, L"command", L"DelegateExecute", clsid);
1432 if (SUCCEEDED(hr))
1433 hr = CoCreateInstance(clsid, NULL, CLSCTX_ALL, IID_PPV_ARG(IExecuteCommand, &pEC));
1434 if (SUCCEEDED(hr))
1435 {
1436 CComPtr<IPropertyBag> pPB;
1437 SHCreatePropertyBagOnRegKey(VerbKey, NULL, STGM_READ, IID_PPV_ARG(IPropertyBag, &pPB));
1438 return InvokeIExecuteCommandWithDataObject(pEC, pEntry->Verb.GetString(), pPB, m_pDataObj,
1439 lpcmi, static_cast<IContextMenu*>(this));
1440 }
1441
1442 CComPtr<IDropTarget> pDT;
1443 hr = SHELL_GetRegCLSID(VerbKey, L"DropTarget", L"CLSID", clsid);
1444 if (SUCCEEDED(hr))
1445 hr = CoCreateInstance(clsid, NULL, CLSCTX_ALL, IID_PPV_ARG(IDropTarget, &pDT));
1446 if (SUCCEEDED(hr))
1447 {
1448 CComPtr<IPropertyBag> pPB;
1449 SHCreatePropertyBagOnRegKey(VerbKey, NULL, STGM_READ, IID_PPV_ARG(IPropertyBag, &pPB));
1450 IUnknown_SetSite(pDT, static_cast<IContextMenu*>(this));
1451 IUnknown_InitializeCommand(pDT, pEntry->Verb.GetString(), pPB);
1452 hr = SHSimulateDrop(pDT, m_pDataObj, KeyState, pPtl, NULL);
1453 IUnknown_SetSite(pDT, NULL);
1454 return hr;
1455 }
1456 }
1457
1458 /* Get the browse flags to see if we need to browse */
1459 DWORD wFlags = BrowserFlagsFromVerb(lpcmi, pEntry);
1460
1461 for (i=0; i < m_cidl; i++)
1462 {
1463 /* Check if we need to browse */
1464 if (wFlags)
1465 {
1466 hr = TryToBrowse(lpcmi, m_apidl[i], wFlags);
1467 if (SUCCEEDED(hr))
1468 {
1469 /* In WinXP if we have browsed, we don't open any more folders.
1470 * In Win7 we browse to the first folder we find and
1471 * open new windows for each of the rest of the folders */
1472 UINT ntver = RosGetProcessEffectiveVersion();
1473 if (ntver >= _WIN32_WINNT_VISTA)
1474 wFlags = 0; // FIXME: = SBSP_NEWBROWSER | (wFlags & ~SBSP_SAMEBROWSER);
1475 else
1476 i = m_cidl;
1477
1478 continue;
1479 }
1480 }
1481
1482 InvokePidl(lpcmi, m_apidl[i], pEntry);
1483 }
1484
1485 return S_OK;
1486 }
1487
1488 HRESULT
_DoInvokeCommandCallback(LPCMINVOKECOMMANDINFOEX lpcmi,WPARAM CmdId)1489 CDefaultContextMenu::_DoInvokeCommandCallback(
1490 LPCMINVOKECOMMANDINFOEX lpcmi, WPARAM CmdId)
1491 {
1492 BOOL Unicode = IsUnicode(*lpcmi);
1493 WCHAR lParamBuf[MAX_PATH];
1494 LPARAM lParam = 0;
1495
1496 if (Unicode && lpcmi->lpParametersW)
1497 lParam = (LPARAM)lpcmi->lpParametersW;
1498 else if (lpcmi->lpParameters)
1499 lParam = SHAnsiToUnicode(lpcmi->lpParameters, lParamBuf, _countof(lParamBuf)) ? (LPARAM)lParamBuf : 0;
1500
1501 HRESULT hr;
1502 #if 0 // TODO: Try DFM_INVOKECOMMANDEX first.
1503 DFMICS dfmics = { sizeof(DFMICS), lpcmi->fMask, lParam, m_iIdSCMFirst?, m_iIdDfltLast?, (LPCMINVOKECOMMANDINFO)lpcmi, m_site };
1504 hr = _DoCallback(DFM_INVOKECOMMANDEX, CmdId, &dfmics);
1505 if (hr == E_NOTIMPL)
1506 #endif
1507 hr = _DoCallback(DFM_INVOKECOMMAND, CmdId, (void*)lParam);
1508 return hr;
1509 }
1510
1511 HRESULT
1512 WINAPI
InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)1513 CDefaultContextMenu::InvokeCommand(
1514 LPCMINVOKECOMMANDINFO lpcmi)
1515 {
1516 CMINVOKECOMMANDINFOEX LocalInvokeInfo = {};
1517 HRESULT Result;
1518 UINT CmdId;
1519
1520 /* Take a local copy of the fixed members of the
1521 struct as we might need to modify the verb */
1522 memcpy(&LocalInvokeInfo, lpcmi, min(sizeof(LocalInvokeInfo), lpcmi->cbSize));
1523
1524 /* Check if this is a string verb */
1525 if (!IS_INTRESOURCE(LocalInvokeInfo.lpVerb))
1526 {
1527 /* Get the ID which corresponds to this verb, and update our local copy */
1528 if (MapVerbToCmdId((LPVOID)LocalInvokeInfo.lpVerb, &CmdId, FALSE))
1529 LocalInvokeInfo.lpVerb = MAKEINTRESOURCEA(CmdId);
1530 else
1531 return E_INVALIDARG;
1532 }
1533
1534 CmdId = LOWORD(LocalInvokeInfo.lpVerb);
1535
1536 if (!m_DynamicEntries.IsEmpty() && CmdId >= m_iIdSHEFirst && CmdId < m_iIdSHELast)
1537 {
1538 LocalInvokeInfo.lpVerb -= m_iIdSHEFirst;
1539 Result = InvokeShellExt(&LocalInvokeInfo);
1540 return Result;
1541 }
1542
1543 if (!m_StaticEntries.IsEmpty() && CmdId >= m_iIdSCMFirst && CmdId < m_iIdSCMLast)
1544 {
1545 LocalInvokeInfo.lpVerb -= m_iIdSCMFirst;
1546 Result = InvokeRegVerb(&LocalInvokeInfo);
1547 // TODO: if (FAILED(Result) && !(lpcmi->fMask & CMIC_MASK_FLAG_NO_UI)) SHELL_ErrorBox(m_pSite, Result);
1548 return Result;
1549 }
1550
1551 if (m_iIdCBFirst != m_iIdCBLast && CmdId >= m_iIdCBFirst && CmdId < m_iIdCBLast)
1552 {
1553 Result = _DoInvokeCommandCallback(&LocalInvokeInfo, CmdId - m_iIdCBFirst);
1554 return Result;
1555 }
1556
1557 if (m_iIdDfltFirst != m_iIdDfltLast && CmdId >= m_iIdDfltFirst && CmdId < m_iIdDfltLast)
1558 {
1559 CmdId -= m_iIdDfltFirst;
1560 /* See the definitions of IDM_CUT and co to see how this works */
1561 CmdId += DCM_FCIDM_SHVIEW_OFFSET;
1562 }
1563
1564 if (LocalInvokeInfo.cbSize >= sizeof(CMINVOKECOMMANDINFOEX) && (LocalInvokeInfo.fMask & CMIC_MASK_PTINVOKE))
1565 {
1566 if (m_pDataObj && FAILED_UNEXPECTEDLY(DataObject_SetOffset(m_pDataObj, &LocalInvokeInfo.ptInvoke)))
1567 {
1568 ERR("Unable to add OFFSET to DataObject!\n");
1569 }
1570 }
1571
1572 /* Check if this is a Id */
1573 switch (CmdId)
1574 {
1575 case FCIDM_SHVIEW_INSERT:
1576 Result = DoPaste(&LocalInvokeInfo, FALSE);
1577 break;
1578 case FCIDM_SHVIEW_INSERTLINK:
1579 Result = DoPaste(&LocalInvokeInfo, TRUE);
1580 break;
1581 case FCIDM_SHVIEW_OPEN:
1582 case FCIDM_SHVIEW_EXPLORE:
1583 Result = DoOpenOrExplore(&LocalInvokeInfo);
1584 break;
1585 case FCIDM_SHVIEW_COPY:
1586 case FCIDM_SHVIEW_CUT:
1587 Result = DoCopyOrCut(&LocalInvokeInfo, CmdId == FCIDM_SHVIEW_COPY);
1588 break;
1589 case FCIDM_SHVIEW_CREATELINK:
1590 Result = DoCreateLink(&LocalInvokeInfo);
1591 break;
1592 case FCIDM_SHVIEW_DELETE:
1593 Result = DoDelete(&LocalInvokeInfo);
1594 break;
1595 case FCIDM_SHVIEW_RENAME:
1596 Result = DoRename(&LocalInvokeInfo);
1597 break;
1598 case FCIDM_SHVIEW_PROPERTIES:
1599 Result = DoProperties(&LocalInvokeInfo);
1600 break;
1601 case FCIDM_SHVIEW_NEWFOLDER:
1602 Result = DoCreateNewFolder(&LocalInvokeInfo);
1603 break;
1604 case FCIDM_SHVIEW_COPYTO:
1605 Result = DoCopyToMoveToFolder(&LocalInvokeInfo, TRUE);
1606 break;
1607 case FCIDM_SHVIEW_MOVETO:
1608 Result = DoCopyToMoveToFolder(&LocalInvokeInfo, FALSE);
1609 break;
1610 case FCIDM_SHVIEW_UNDO:
1611 Result = DoUndo(&LocalInvokeInfo);
1612 break;
1613 default:
1614 Result = E_INVALIDARG;
1615 ERR("Unhandled Verb %xl\n", LOWORD(LocalInvokeInfo.lpVerb));
1616 break;
1617 }
1618
1619 return Result;
1620 }
1621
1622 HRESULT
1623 WINAPI
GetCommandString(UINT_PTR idCommand,UINT uFlags,UINT * lpReserved,LPSTR lpszName,UINT uMaxNameLen)1624 CDefaultContextMenu::GetCommandString(
1625 UINT_PTR idCommand,
1626 UINT uFlags,
1627 UINT* lpReserved,
1628 LPSTR lpszName,
1629 UINT uMaxNameLen)
1630 {
1631 /* We don't handle the help text yet */
1632 if (uFlags == GCS_HELPTEXTA ||
1633 uFlags == GCS_HELPTEXTW ||
1634 HIWORD(idCommand) != 0)
1635 {
1636 return E_NOTIMPL;
1637 }
1638
1639 UINT CmdId = LOWORD(idCommand);
1640
1641 if (!m_DynamicEntries.IsEmpty() && CmdId >= m_iIdSHEFirst && CmdId < m_iIdSHELast)
1642 {
1643 idCommand -= m_iIdSHEFirst;
1644 PDynamicShellEntry pEntry = GetDynamicEntry(idCommand);
1645 if (!pEntry)
1646 return E_FAIL;
1647
1648 idCommand -= pEntry->iIdCmdFirst;
1649 return pEntry->pCM->GetCommandString(idCommand,
1650 uFlags,
1651 lpReserved,
1652 lpszName,
1653 uMaxNameLen);
1654 }
1655
1656 if (!m_StaticEntries.IsEmpty() && CmdId >= m_iIdSCMFirst && CmdId < m_iIdSCMLast)
1657 {
1658 /* Validation just returns S_OK on a match. The id exists. */
1659 if (uFlags == GCS_VALIDATEA || uFlags == GCS_VALIDATEW)
1660 return S_OK;
1661
1662 CmdId -= m_iIdSCMFirst;
1663
1664 POSITION it = m_StaticEntries.FindIndex(CmdId);
1665
1666 if (it == NULL)
1667 return E_INVALIDARG;
1668
1669 PStaticShellEntry pEntry = &m_StaticEntries.GetAt(it);
1670
1671 if (uFlags == GCS_VERBW)
1672 return StringCchCopyW((LPWSTR)lpszName, uMaxNameLen, pEntry->Verb);
1673
1674 if (uFlags == GCS_VERBA)
1675 {
1676 if (SHUnicodeToAnsi(pEntry->Verb, lpszName, uMaxNameLen))
1677 return S_OK;
1678 }
1679
1680 return E_INVALIDARG;
1681 }
1682
1683 //FIXME: Should we handle callbacks here?
1684 if (m_iIdDfltFirst != m_iIdDfltLast && CmdId >= m_iIdDfltFirst && CmdId < m_iIdDfltLast)
1685 {
1686 CmdId -= m_iIdDfltFirst;
1687 /* See the definitions of IDM_CUT and co to see how this works */
1688 CmdId += DCM_FCIDM_SHVIEW_OFFSET;
1689 }
1690
1691 /* Loop looking for a matching Id */
1692 for (UINT i = 0; i < _countof(g_StaticInvokeCmdMap); i++)
1693 {
1694 if (g_StaticInvokeCmdMap[i].IntVerb == CmdId)
1695 {
1696 /* Validation just returns S_OK on a match */
1697 if (uFlags == GCS_VALIDATEA || uFlags == GCS_VALIDATEW)
1698 return S_OK;
1699
1700 /* Return a copy of the ANSI verb */
1701 if (uFlags == GCS_VERBA)
1702 return StringCchCopyA(lpszName, uMaxNameLen, g_StaticInvokeCmdMap[i].szStringVerb);
1703
1704 /* Convert the ANSI verb to unicode and return that */
1705 if (uFlags == GCS_VERBW)
1706 {
1707 if (SHAnsiToUnicode(g_StaticInvokeCmdMap[i].szStringVerb, (LPWSTR)lpszName, uMaxNameLen))
1708 return S_OK;
1709 }
1710 }
1711 }
1712
1713 return E_INVALIDARG;
1714 }
1715
1716 HRESULT
1717 WINAPI
HandleMenuMsg(UINT uMsg,WPARAM wParam,LPARAM lParam)1718 CDefaultContextMenu::HandleMenuMsg(
1719 UINT uMsg,
1720 WPARAM wParam,
1721 LPARAM lParam)
1722 {
1723 /* FIXME: Should we implement this as well? */
1724 return S_OK;
1725 }
1726
SHGetMenuIdFromMenuMsg(UINT uMsg,LPARAM lParam,UINT * CmdId)1727 HRESULT SHGetMenuIdFromMenuMsg(UINT uMsg, LPARAM lParam, UINT *CmdId)
1728 {
1729 if (uMsg == WM_DRAWITEM)
1730 {
1731 DRAWITEMSTRUCT* pDrawStruct = reinterpret_cast<DRAWITEMSTRUCT*>(lParam);
1732 *CmdId = pDrawStruct->itemID;
1733 return S_OK;
1734 }
1735 else if (uMsg == WM_MEASUREITEM)
1736 {
1737 MEASUREITEMSTRUCT* pMeasureStruct = reinterpret_cast<MEASUREITEMSTRUCT*>(lParam);
1738 *CmdId = pMeasureStruct->itemID;
1739 return S_OK;
1740 }
1741
1742 return E_FAIL;
1743 }
1744
SHSetMenuIdInMenuMsg(UINT uMsg,LPARAM lParam,UINT CmdId)1745 HRESULT SHSetMenuIdInMenuMsg(UINT uMsg, LPARAM lParam, UINT CmdId)
1746 {
1747 if (uMsg == WM_DRAWITEM)
1748 {
1749 DRAWITEMSTRUCT* pDrawStruct = reinterpret_cast<DRAWITEMSTRUCT*>(lParam);
1750 pDrawStruct->itemID = CmdId;
1751 return S_OK;
1752 }
1753 else if (uMsg == WM_MEASUREITEM)
1754 {
1755 MEASUREITEMSTRUCT* pMeasureStruct = reinterpret_cast<MEASUREITEMSTRUCT*>(lParam);
1756 pMeasureStruct->itemID = CmdId;
1757 return S_OK;
1758 }
1759
1760 return E_FAIL;
1761 }
1762
1763 HRESULT
1764 WINAPI
HandleMenuMsg2(UINT uMsg,WPARAM wParam,LPARAM lParam,LRESULT * plResult)1765 CDefaultContextMenu::HandleMenuMsg2(
1766 UINT uMsg,
1767 WPARAM wParam,
1768 LPARAM lParam,
1769 LRESULT *plResult)
1770 {
1771 if (uMsg == WM_INITMENUPOPUP)
1772 {
1773 POSITION it = m_DynamicEntries.GetHeadPosition();
1774 while (it != NULL)
1775 {
1776 DynamicShellEntry& info = m_DynamicEntries.GetNext(it);
1777 SHForwardContextMenuMsg(info.pCM, uMsg, wParam, lParam, plResult, TRUE);
1778 }
1779 return S_OK;
1780 }
1781
1782 UINT CmdId;
1783 HRESULT hr = SHGetMenuIdFromMenuMsg(uMsg, lParam, &CmdId);
1784 if (FAILED(hr))
1785 return S_FALSE;
1786
1787 if (CmdId < m_iIdSHEFirst || CmdId >= m_iIdSHELast)
1788 return S_FALSE;
1789
1790 CmdId -= m_iIdSHEFirst;
1791 PDynamicShellEntry pEntry = GetDynamicEntry(CmdId);
1792 if (pEntry)
1793 {
1794 SHSetMenuIdInMenuMsg(uMsg, lParam, CmdId - pEntry->iIdCmdFirst);
1795 SHForwardContextMenuMsg(pEntry->pCM, uMsg, wParam, lParam, plResult, TRUE);
1796 }
1797
1798 return S_OK;
1799 }
1800
1801 HRESULT
1802 WINAPI
SetSite(IUnknown * pUnkSite)1803 CDefaultContextMenu::SetSite(IUnknown *pUnkSite)
1804 {
1805 m_site = pUnkSite;
1806 return S_OK;
1807 }
1808
1809 HRESULT
1810 WINAPI
GetSite(REFIID riid,void ** ppvSite)1811 CDefaultContextMenu::GetSite(REFIID riid, void **ppvSite)
1812 {
1813 if (!m_site)
1814 return E_FAIL;
1815
1816 return m_site->QueryInterface(riid, ppvSite);
1817 }
1818
1819 static
1820 HRESULT
CDefaultContextMenu_CreateInstance(const DEFCONTEXTMENU * pdcm,LPFNDFMCALLBACK lpfn,REFIID riid,void ** ppv)1821 CDefaultContextMenu_CreateInstance(const DEFCONTEXTMENU *pdcm, LPFNDFMCALLBACK lpfn, REFIID riid, void **ppv)
1822 {
1823 return ShellObjectCreatorInit<CDefaultContextMenu>(pdcm, lpfn, riid, ppv);
1824 }
1825
1826 /*************************************************************************
1827 * SHCreateDefaultContextMenu [SHELL32.325] Vista API
1828 *
1829 */
1830
1831 HRESULT
1832 WINAPI
SHCreateDefaultContextMenu(const DEFCONTEXTMENU * pdcm,REFIID riid,void ** ppv)1833 SHCreateDefaultContextMenu(const DEFCONTEXTMENU *pdcm, REFIID riid, void **ppv)
1834 {
1835 HRESULT hr;
1836
1837 if (!ppv)
1838 return E_INVALIDARG;
1839
1840 hr = CDefaultContextMenu_CreateInstance(pdcm, NULL, riid, ppv);
1841 if (FAILED_UNEXPECTEDLY(hr))
1842 return hr;
1843
1844 return S_OK;
1845 }
1846
1847 /*************************************************************************
1848 * CDefFolderMenu_Create2 [SHELL32.701]
1849 *
1850 */
1851
1852 HRESULT
1853 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)1854 CDefFolderMenu_Create2(
1855 PCIDLIST_ABSOLUTE pidlFolder,
1856 HWND hwnd,
1857 UINT cidl,
1858 PCUITEMID_CHILD_ARRAY apidl,
1859 IShellFolder *psf,
1860 LPFNDFMCALLBACK lpfn,
1861 UINT nKeys,
1862 const HKEY *ahkeyClsKeys,
1863 IContextMenu **ppcm)
1864 {
1865 DEFCONTEXTMENU dcm;
1866 dcm.hwnd = hwnd;
1867 dcm.pcmcb = NULL;
1868 dcm.pidlFolder = pidlFolder;
1869 dcm.psf = psf;
1870 dcm.cidl = cidl;
1871 dcm.apidl = apidl;
1872 dcm.punkAssociationInfo = NULL;
1873 dcm.cKeys = nKeys;
1874 dcm.aKeys = ahkeyClsKeys;
1875
1876 HRESULT hr = CDefaultContextMenu_CreateInstance(&dcm, lpfn, IID_PPV_ARG(IContextMenu, ppcm));
1877 if (FAILED_UNEXPECTEDLY(hr))
1878 return hr;
1879
1880 return S_OK;
1881 }
1882