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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 981 CDefaultContextMenu::DoOpenOrExplore(LPCMINVOKECOMMANDINFOEX lpcmi) 982 { 983 UNIMPLEMENTED; 984 return E_FAIL; 985 } 986 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 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 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 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 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 1083 CDefaultContextMenu::DoUndo(LPCMINVOKECOMMANDINFOEX lpcmi) 1084 { 1085 ERR("TODO: Undo\n"); 1086 return E_NOTIMPL; 1087 } 1088 1089 HRESULT 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 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 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 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 (!wcscmp(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 (!strcmp(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 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 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 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 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 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 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 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 } 1531 1532 CmdId = LOWORD(LocalInvokeInfo.lpVerb); 1533 1534 if (!m_DynamicEntries.IsEmpty() && CmdId >= m_iIdSHEFirst && CmdId < m_iIdSHELast) 1535 { 1536 LocalInvokeInfo.lpVerb -= m_iIdSHEFirst; 1537 Result = InvokeShellExt(&LocalInvokeInfo); 1538 return Result; 1539 } 1540 1541 if (!m_StaticEntries.IsEmpty() && CmdId >= m_iIdSCMFirst && CmdId < m_iIdSCMLast) 1542 { 1543 LocalInvokeInfo.lpVerb -= m_iIdSCMFirst; 1544 Result = InvokeRegVerb(&LocalInvokeInfo); 1545 // TODO: if (FAILED(Result) && !(lpcmi->fMask & CMIC_MASK_FLAG_NO_UI)) SHELL_ErrorBox(m_pSite, Result); 1546 return Result; 1547 } 1548 1549 if (m_iIdCBFirst != m_iIdCBLast && CmdId >= m_iIdCBFirst && CmdId < m_iIdCBLast) 1550 { 1551 Result = _DoInvokeCommandCallback(&LocalInvokeInfo, CmdId - m_iIdCBFirst); 1552 return Result; 1553 } 1554 1555 if (m_iIdDfltFirst != m_iIdDfltLast && CmdId >= m_iIdDfltFirst && CmdId < m_iIdDfltLast) 1556 { 1557 CmdId -= m_iIdDfltFirst; 1558 /* See the definitions of IDM_CUT and co to see how this works */ 1559 CmdId += DCM_FCIDM_SHVIEW_OFFSET; 1560 } 1561 1562 if (LocalInvokeInfo.cbSize >= sizeof(CMINVOKECOMMANDINFOEX) && (LocalInvokeInfo.fMask & CMIC_MASK_PTINVOKE)) 1563 { 1564 if (m_pDataObj && FAILED_UNEXPECTEDLY(DataObject_SetOffset(m_pDataObj, &LocalInvokeInfo.ptInvoke))) 1565 { 1566 ERR("Unable to add OFFSET to DataObject!\n"); 1567 } 1568 } 1569 1570 /* Check if this is a Id */ 1571 switch (CmdId) 1572 { 1573 case FCIDM_SHVIEW_INSERT: 1574 Result = DoPaste(&LocalInvokeInfo, FALSE); 1575 break; 1576 case FCIDM_SHVIEW_INSERTLINK: 1577 Result = DoPaste(&LocalInvokeInfo, TRUE); 1578 break; 1579 case FCIDM_SHVIEW_OPEN: 1580 case FCIDM_SHVIEW_EXPLORE: 1581 Result = DoOpenOrExplore(&LocalInvokeInfo); 1582 break; 1583 case FCIDM_SHVIEW_COPY: 1584 case FCIDM_SHVIEW_CUT: 1585 Result = DoCopyOrCut(&LocalInvokeInfo, CmdId == FCIDM_SHVIEW_COPY); 1586 break; 1587 case FCIDM_SHVIEW_CREATELINK: 1588 Result = DoCreateLink(&LocalInvokeInfo); 1589 break; 1590 case FCIDM_SHVIEW_DELETE: 1591 Result = DoDelete(&LocalInvokeInfo); 1592 break; 1593 case FCIDM_SHVIEW_RENAME: 1594 Result = DoRename(&LocalInvokeInfo); 1595 break; 1596 case FCIDM_SHVIEW_PROPERTIES: 1597 Result = DoProperties(&LocalInvokeInfo); 1598 break; 1599 case FCIDM_SHVIEW_NEWFOLDER: 1600 Result = DoCreateNewFolder(&LocalInvokeInfo); 1601 break; 1602 case FCIDM_SHVIEW_COPYTO: 1603 Result = DoCopyToMoveToFolder(&LocalInvokeInfo, TRUE); 1604 break; 1605 case FCIDM_SHVIEW_MOVETO: 1606 Result = DoCopyToMoveToFolder(&LocalInvokeInfo, FALSE); 1607 break; 1608 case FCIDM_SHVIEW_UNDO: 1609 Result = DoUndo(&LocalInvokeInfo); 1610 break; 1611 default: 1612 Result = E_INVALIDARG; 1613 ERR("Unhandled Verb %xl\n", LOWORD(LocalInvokeInfo.lpVerb)); 1614 break; 1615 } 1616 1617 return Result; 1618 } 1619 1620 HRESULT 1621 WINAPI 1622 CDefaultContextMenu::GetCommandString( 1623 UINT_PTR idCommand, 1624 UINT uFlags, 1625 UINT* lpReserved, 1626 LPSTR lpszName, 1627 UINT uMaxNameLen) 1628 { 1629 /* We don't handle the help text yet */ 1630 if (uFlags == GCS_HELPTEXTA || 1631 uFlags == GCS_HELPTEXTW || 1632 HIWORD(idCommand) != 0) 1633 { 1634 return E_NOTIMPL; 1635 } 1636 1637 UINT CmdId = LOWORD(idCommand); 1638 1639 if (!m_DynamicEntries.IsEmpty() && CmdId >= m_iIdSHEFirst && CmdId < m_iIdSHELast) 1640 { 1641 idCommand -= m_iIdSHEFirst; 1642 PDynamicShellEntry pEntry = GetDynamicEntry(idCommand); 1643 if (!pEntry) 1644 return E_FAIL; 1645 1646 idCommand -= pEntry->iIdCmdFirst; 1647 return pEntry->pCM->GetCommandString(idCommand, 1648 uFlags, 1649 lpReserved, 1650 lpszName, 1651 uMaxNameLen); 1652 } 1653 1654 if (!m_StaticEntries.IsEmpty() && CmdId >= m_iIdSCMFirst && CmdId < m_iIdSCMLast) 1655 { 1656 /* Validation just returns S_OK on a match. The id exists. */ 1657 if (uFlags == GCS_VALIDATEA || uFlags == GCS_VALIDATEW) 1658 return S_OK; 1659 1660 CmdId -= m_iIdSCMFirst; 1661 1662 POSITION it = m_StaticEntries.FindIndex(CmdId); 1663 1664 if (it == NULL) 1665 return E_INVALIDARG; 1666 1667 PStaticShellEntry pEntry = &m_StaticEntries.GetAt(it); 1668 1669 if (uFlags == GCS_VERBW) 1670 return StringCchCopyW((LPWSTR)lpszName, uMaxNameLen, pEntry->Verb); 1671 1672 if (uFlags == GCS_VERBA) 1673 { 1674 if (SHUnicodeToAnsi(pEntry->Verb, lpszName, uMaxNameLen)) 1675 return S_OK; 1676 } 1677 1678 return E_INVALIDARG; 1679 } 1680 1681 //FIXME: Should we handle callbacks here? 1682 if (m_iIdDfltFirst != m_iIdDfltLast && CmdId >= m_iIdDfltFirst && CmdId < m_iIdDfltLast) 1683 { 1684 CmdId -= m_iIdDfltFirst; 1685 /* See the definitions of IDM_CUT and co to see how this works */ 1686 CmdId += DCM_FCIDM_SHVIEW_OFFSET; 1687 } 1688 1689 /* Loop looking for a matching Id */ 1690 for (UINT i = 0; i < _countof(g_StaticInvokeCmdMap); i++) 1691 { 1692 if (g_StaticInvokeCmdMap[i].IntVerb == CmdId) 1693 { 1694 /* Validation just returns S_OK on a match */ 1695 if (uFlags == GCS_VALIDATEA || uFlags == GCS_VALIDATEW) 1696 return S_OK; 1697 1698 /* Return a copy of the ANSI verb */ 1699 if (uFlags == GCS_VERBA) 1700 return StringCchCopyA(lpszName, uMaxNameLen, g_StaticInvokeCmdMap[i].szStringVerb); 1701 1702 /* Convert the ANSI verb to unicode and return that */ 1703 if (uFlags == GCS_VERBW) 1704 { 1705 if (SHAnsiToUnicode(g_StaticInvokeCmdMap[i].szStringVerb, (LPWSTR)lpszName, uMaxNameLen)) 1706 return S_OK; 1707 } 1708 } 1709 } 1710 1711 return E_INVALIDARG; 1712 } 1713 1714 HRESULT 1715 WINAPI 1716 CDefaultContextMenu::HandleMenuMsg( 1717 UINT uMsg, 1718 WPARAM wParam, 1719 LPARAM lParam) 1720 { 1721 /* FIXME: Should we implement this as well? */ 1722 return S_OK; 1723 } 1724 1725 HRESULT SHGetMenuIdFromMenuMsg(UINT uMsg, LPARAM lParam, UINT *CmdId) 1726 { 1727 if (uMsg == WM_DRAWITEM) 1728 { 1729 DRAWITEMSTRUCT* pDrawStruct = reinterpret_cast<DRAWITEMSTRUCT*>(lParam); 1730 *CmdId = pDrawStruct->itemID; 1731 return S_OK; 1732 } 1733 else if (uMsg == WM_MEASUREITEM) 1734 { 1735 MEASUREITEMSTRUCT* pMeasureStruct = reinterpret_cast<MEASUREITEMSTRUCT*>(lParam); 1736 *CmdId = pMeasureStruct->itemID; 1737 return S_OK; 1738 } 1739 1740 return E_FAIL; 1741 } 1742 1743 HRESULT SHSetMenuIdInMenuMsg(UINT uMsg, LPARAM lParam, UINT CmdId) 1744 { 1745 if (uMsg == WM_DRAWITEM) 1746 { 1747 DRAWITEMSTRUCT* pDrawStruct = reinterpret_cast<DRAWITEMSTRUCT*>(lParam); 1748 pDrawStruct->itemID = CmdId; 1749 return S_OK; 1750 } 1751 else if (uMsg == WM_MEASUREITEM) 1752 { 1753 MEASUREITEMSTRUCT* pMeasureStruct = reinterpret_cast<MEASUREITEMSTRUCT*>(lParam); 1754 pMeasureStruct->itemID = CmdId; 1755 return S_OK; 1756 } 1757 1758 return E_FAIL; 1759 } 1760 1761 HRESULT 1762 WINAPI 1763 CDefaultContextMenu::HandleMenuMsg2( 1764 UINT uMsg, 1765 WPARAM wParam, 1766 LPARAM lParam, 1767 LRESULT *plResult) 1768 { 1769 if (uMsg == WM_INITMENUPOPUP) 1770 { 1771 POSITION it = m_DynamicEntries.GetHeadPosition(); 1772 while (it != NULL) 1773 { 1774 DynamicShellEntry& info = m_DynamicEntries.GetNext(it); 1775 SHForwardContextMenuMsg(info.pCM, uMsg, wParam, lParam, plResult, TRUE); 1776 } 1777 return S_OK; 1778 } 1779 1780 UINT CmdId; 1781 HRESULT hr = SHGetMenuIdFromMenuMsg(uMsg, lParam, &CmdId); 1782 if (FAILED(hr)) 1783 return S_FALSE; 1784 1785 if (CmdId < m_iIdSHEFirst || CmdId >= m_iIdSHELast) 1786 return S_FALSE; 1787 1788 CmdId -= m_iIdSHEFirst; 1789 PDynamicShellEntry pEntry = GetDynamicEntry(CmdId); 1790 if (pEntry) 1791 { 1792 SHSetMenuIdInMenuMsg(uMsg, lParam, CmdId - pEntry->iIdCmdFirst); 1793 SHForwardContextMenuMsg(pEntry->pCM, uMsg, wParam, lParam, plResult, TRUE); 1794 } 1795 1796 return S_OK; 1797 } 1798 1799 HRESULT 1800 WINAPI 1801 CDefaultContextMenu::SetSite(IUnknown *pUnkSite) 1802 { 1803 m_site = pUnkSite; 1804 return S_OK; 1805 } 1806 1807 HRESULT 1808 WINAPI 1809 CDefaultContextMenu::GetSite(REFIID riid, void **ppvSite) 1810 { 1811 if (!m_site) 1812 return E_FAIL; 1813 1814 return m_site->QueryInterface(riid, ppvSite); 1815 } 1816 1817 static 1818 HRESULT 1819 CDefaultContextMenu_CreateInstance(const DEFCONTEXTMENU *pdcm, LPFNDFMCALLBACK lpfn, REFIID riid, void **ppv) 1820 { 1821 return ShellObjectCreatorInit<CDefaultContextMenu>(pdcm, lpfn, riid, ppv); 1822 } 1823 1824 /************************************************************************* 1825 * SHCreateDefaultContextMenu [SHELL32.325] Vista API 1826 * 1827 */ 1828 1829 HRESULT 1830 WINAPI 1831 SHCreateDefaultContextMenu(const DEFCONTEXTMENU *pdcm, REFIID riid, void **ppv) 1832 { 1833 HRESULT hr; 1834 1835 if (!ppv) 1836 return E_INVALIDARG; 1837 1838 hr = CDefaultContextMenu_CreateInstance(pdcm, NULL, riid, ppv); 1839 if (FAILED_UNEXPECTEDLY(hr)) 1840 return hr; 1841 1842 return S_OK; 1843 } 1844 1845 /************************************************************************* 1846 * CDefFolderMenu_Create2 [SHELL32.701] 1847 * 1848 */ 1849 1850 HRESULT 1851 WINAPI 1852 CDefFolderMenu_Create2( 1853 PCIDLIST_ABSOLUTE pidlFolder, 1854 HWND hwnd, 1855 UINT cidl, 1856 PCUITEMID_CHILD_ARRAY apidl, 1857 IShellFolder *psf, 1858 LPFNDFMCALLBACK lpfn, 1859 UINT nKeys, 1860 const HKEY *ahkeyClsKeys, 1861 IContextMenu **ppcm) 1862 { 1863 DEFCONTEXTMENU dcm; 1864 dcm.hwnd = hwnd; 1865 dcm.pcmcb = NULL; 1866 dcm.pidlFolder = pidlFolder; 1867 dcm.psf = psf; 1868 dcm.cidl = cidl; 1869 dcm.apidl = apidl; 1870 dcm.punkAssociationInfo = NULL; 1871 dcm.cKeys = nKeys; 1872 dcm.aKeys = ahkeyClsKeys; 1873 1874 HRESULT hr = CDefaultContextMenu_CreateInstance(&dcm, lpfn, IID_PPV_ARG(IContextMenu, ppcm)); 1875 if (FAILED_UNEXPECTEDLY(hr)) 1876 return hr; 1877 1878 return S_OK; 1879 } 1880