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