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