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