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