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