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(reinterpret_cast<LPITEMIDLIST*>(&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 STGMEDIUM medium; 329 FORMATETC formatetc; 330 331 TRACE("pDataObj=%p\n", pDataObj.p); 332 333 /* Set the FORMATETC structure*/ 334 InitFormatEtc(formatetc, RegisterClipboardFormatW(CFSTR_SHELLIDLIST), TYMED_HGLOBAL); 335 if (SUCCEEDED(pDataObj->GetData(&formatetc, &medium))) 336 { 337 bRet = TRUE; 338 ReleaseStgMedium(&medium); 339 } 340 } 341 342 return bRet; 343 } 344 345 BOOL 346 CDefaultContextMenu::IsShellExtensionAlreadyLoaded(const CLSID *pclsid) 347 { 348 PDynamicShellEntry pEntry = m_pDynamicEntries; 349 350 while (pEntry) 351 { 352 if (!memcmp(&pEntry->ClassID, pclsid, sizeof(CLSID))) 353 return TRUE; 354 pEntry = pEntry->pNext; 355 } 356 357 return FALSE; 358 } 359 360 HRESULT 361 CDefaultContextMenu::LoadDynamicContextMenuHandler(HKEY hKey, const CLSID *pclsid) 362 { 363 HRESULT hr; 364 365 TRACE("LoadDynamicContextMenuHandler entered with This %p hKey %p pclsid %s\n", this, hKey, wine_dbgstr_guid(pclsid)); 366 367 if (IsShellExtensionAlreadyLoaded(pclsid)) 368 return S_OK; 369 370 CComPtr<IContextMenu> pcm; 371 hr = SHCoCreateInstance(NULL, pclsid, NULL, IID_PPV_ARG(IContextMenu, &pcm)); 372 if (FAILED(hr)) 373 { 374 ERR("SHCoCreateInstance(IContextMenu) failed.clsid %s hr 0x%x\n", wine_dbgstr_guid(pclsid), hr); 375 return hr; 376 } 377 378 CComPtr<IShellExtInit> pExtInit; 379 hr = pcm->QueryInterface(IID_PPV_ARG(IShellExtInit, &pExtInit)); 380 if (FAILED(hr)) 381 { 382 ERR("IContextMenu->QueryInterface(IShellExtInit) failed.clsid %s hr 0x%x\n", wine_dbgstr_guid(pclsid), hr); 383 return hr; 384 } 385 386 hr = pExtInit->Initialize(m_pidlFolder, m_pDataObj, hKey); 387 if (FAILED(hr)) 388 { 389 ERR("IShellExtInit::Initialize failed.clsid %s hr 0x%x\n", wine_dbgstr_guid(pclsid), hr); 390 return hr; 391 } 392 393 if (m_site) 394 IUnknown_SetSite(pcm, m_site); 395 396 PDynamicShellEntry pEntry = (DynamicShellEntry *)HeapAlloc(GetProcessHeap(), 0, sizeof(DynamicShellEntry)); 397 if (!pEntry) 398 return E_OUTOFMEMORY; 399 400 pEntry->iIdCmdFirst = 0; 401 pEntry->pNext = NULL; 402 pEntry->NumIds = 0; 403 pEntry->pCM = pcm.Detach(); 404 memcpy(&pEntry->ClassID, pclsid, sizeof(CLSID)); 405 406 if (m_pDynamicEntries) 407 { 408 PDynamicShellEntry pLastEntry = m_pDynamicEntries; 409 410 while (pLastEntry->pNext) 411 pLastEntry = pLastEntry->pNext; 412 413 pLastEntry->pNext = pEntry; 414 } 415 else 416 m_pDynamicEntries = pEntry; 417 418 return S_OK; 419 } 420 421 BOOL 422 CDefaultContextMenu::EnumerateDynamicContextHandlerForKey(HKEY hRootKey) 423 { 424 WCHAR wszName[MAX_PATH], wszBuf[MAX_PATH], *pwszClsid; 425 DWORD cchName; 426 HRESULT hr; 427 HKEY hKey; 428 429 if (RegOpenKeyExW(hRootKey, L"shellex\\ContextMenuHandlers", 0, KEY_READ, &hKey) != ERROR_SUCCESS) 430 { 431 TRACE("RegOpenKeyExW failed\n"); 432 return FALSE; 433 } 434 435 DWORD dwIndex = 0; 436 while (TRUE) 437 { 438 cchName = _countof(wszName); 439 if (RegEnumKeyExW(hKey, dwIndex++, wszName, &cchName, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) 440 break; 441 442 /* Key name or key value is CLSID */ 443 CLSID clsid; 444 hr = CLSIDFromString(wszName, &clsid); 445 if (hr == S_OK) 446 pwszClsid = wszName; 447 else 448 { 449 DWORD cchBuf = _countof(wszBuf); 450 if (RegGetValueW(hKey, wszName, NULL, RRF_RT_REG_SZ, NULL, wszBuf, &cchBuf) == ERROR_SUCCESS) 451 hr = CLSIDFromString(wszBuf, &clsid); 452 pwszClsid = wszBuf; 453 } 454 455 if (FAILED(hr)) 456 { 457 ERR("CLSIDFromString failed for clsid %S hr 0x%x\n", pwszClsid, hr); 458 continue; 459 } 460 461 if (m_bGroupPolicyActive) 462 { 463 if (RegGetValueW(HKEY_LOCAL_MACHINE, 464 L"Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved", 465 pwszClsid, 466 RRF_RT_REG_SZ, 467 NULL, 468 NULL, 469 NULL) != ERROR_SUCCESS) 470 { 471 ERR("Shell extension %s not approved!\n", pwszClsid); 472 continue; 473 } 474 } 475 476 hr = LoadDynamicContextMenuHandler(hKey, &clsid); 477 if (FAILED(hr)) 478 WARN("Failed to get context menu entires from shell extension! clsid: %S\n", pwszClsid); 479 } 480 481 RegCloseKey(hKey); 482 return TRUE; 483 } 484 485 UINT 486 CDefaultContextMenu::AddShellExtensionsToMenu(HMENU hMenu, UINT* pIndexMenu, UINT idCmdFirst, UINT idCmdLast) 487 { 488 UINT cIds = 0; 489 490 if (!m_pDynamicEntries) 491 return cIds; 492 493 PDynamicShellEntry pEntry = m_pDynamicEntries; 494 do 495 { 496 HRESULT hr = pEntry->pCM->QueryContextMenu(hMenu, *pIndexMenu, idCmdFirst + cIds, idCmdLast, CMF_NORMAL); 497 if (SUCCEEDED(hr)) 498 { 499 pEntry->iIdCmdFirst = cIds; 500 pEntry->NumIds = LOWORD(hr); 501 (*pIndexMenu) += pEntry->NumIds; 502 503 cIds += pEntry->NumIds; 504 if(idCmdFirst + cIds >= idCmdLast) 505 break; 506 } 507 TRACE("pEntry %p hr %x contextmenu %p cmdfirst %x num ids %x\n", pEntry, hr, pEntry->pCM, pEntry->iIdCmdFirst, pEntry->NumIds); 508 pEntry = pEntry->pNext; 509 } while (pEntry); 510 511 return cIds; 512 } 513 514 UINT 515 CDefaultContextMenu::AddStaticContextMenusToMenu( 516 HMENU hMenu, 517 UINT* pIndexMenu, 518 UINT iIdCmdFirst, 519 UINT iIdCmdLast) 520 { 521 MENUITEMINFOW mii; 522 UINT idResource; 523 WCHAR wszVerb[40]; 524 UINT fState; 525 UINT cIds = 0; 526 527 mii.cbSize = sizeof(mii); 528 mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE | MIIM_DATA; 529 mii.fType = MFT_STRING; 530 mii.dwTypeData = NULL; 531 532 PStaticShellEntry pEntry = m_pStaticEntries; 533 534 while (pEntry) 535 { 536 fState = MFS_ENABLED; 537 mii.dwTypeData = NULL; 538 539 /* set first entry as default */ 540 if (pEntry == m_pStaticEntries) 541 fState |= MFS_DEFAULT; 542 543 if (!wcsicmp(pEntry->szVerb, L"open")) 544 { 545 /* override default when open verb is found */ 546 fState |= MFS_DEFAULT; 547 idResource = IDS_OPEN_VERB; 548 } 549 else if (!wcsicmp(pEntry->szVerb, L"explore")) 550 idResource = IDS_EXPLORE_VERB; 551 else if (!wcsicmp(pEntry->szVerb, L"runas")) 552 idResource = IDS_RUNAS_VERB; 553 else if (!wcsicmp(pEntry->szVerb, L"edit")) 554 idResource = IDS_EDIT_VERB; 555 else if (!wcsicmp(pEntry->szVerb, L"find")) 556 idResource = IDS_FIND_VERB; 557 else if (!wcsicmp(pEntry->szVerb, L"print")) 558 idResource = IDS_PRINT_VERB; 559 else if (!wcsicmp(pEntry->szVerb, L"printto")) 560 { 561 pEntry = pEntry->pNext; 562 continue; 563 } 564 else 565 idResource = 0; 566 567 /* By default use verb for menu item name */ 568 mii.dwTypeData = pEntry->szVerb; 569 570 if (idResource > 0) 571 { 572 if (LoadStringW(shell32_hInstance, idResource, wszVerb, _countof(wszVerb))) 573 mii.dwTypeData = wszVerb; /* use translated verb */ 574 else 575 ERR("Failed to load string\n"); 576 } 577 else 578 { 579 WCHAR wszKey[256]; 580 HRESULT hr = StringCbPrintfW(wszKey, sizeof(wszKey), L"shell\\%s", pEntry->szVerb); 581 582 if (SUCCEEDED(hr)) 583 { 584 HKEY hkVerb; 585 DWORD cbVerb = sizeof(wszVerb); 586 LONG res = RegOpenKeyW(pEntry->hkClass, wszKey, &hkVerb); 587 if (res == ERROR_SUCCESS) 588 { 589 res = RegLoadMUIStringW(hkVerb, 590 NULL, 591 wszVerb, 592 cbVerb, 593 NULL, 594 0, 595 NULL); 596 if (res == ERROR_SUCCESS) 597 { 598 /* use description for the menu entry */ 599 mii.dwTypeData = wszVerb; 600 } 601 602 RegCloseKey(hkVerb); 603 } 604 } 605 } 606 607 mii.cch = wcslen(mii.dwTypeData); 608 mii.fState = fState; 609 mii.wID = iIdCmdFirst + cIds; 610 InsertMenuItemW(hMenu, *pIndexMenu, TRUE, &mii); 611 (*pIndexMenu)++; 612 cIds++; 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 if (!bCopy) 844 { 845 FORMATETC formatetc; 846 STGMEDIUM medium; 847 InitFormatEtc(formatetc, RegisterClipboardFormatW(CFSTR_PREFERREDDROPEFFECT), TYMED_HGLOBAL); 848 m_pDataObj->GetData(&formatetc, &medium); 849 DWORD * pdwFlag = (DWORD*)GlobalLock(medium.hGlobal); 850 if (pdwFlag) 851 *pdwFlag = DROPEFFECT_MOVE; 852 GlobalUnlock(medium.hGlobal); 853 m_pDataObj->SetData(&formatetc, &medium, TRUE); 854 } 855 856 HRESULT hr = OleSetClipboard(m_pDataObj); 857 if (FAILED_UNEXPECTEDLY(hr)) 858 return hr; 859 860 return S_OK; 861 } 862 863 HRESULT CDefaultContextMenu::DoRename(LPCMINVOKECOMMANDINFO lpcmi) 864 { 865 CComPtr<IShellBrowser> psb; 866 HRESULT hr; 867 868 if (!m_site || !m_cidl) 869 return E_FAIL; 870 871 /* Get a pointer to the shell browser */ 872 hr = IUnknown_QueryService(m_site, SID_IShellBrowser, IID_PPV_ARG(IShellBrowser, &psb)); 873 if (FAILED_UNEXPECTEDLY(hr)) 874 return hr; 875 876 CComPtr<IShellView> lpSV; 877 hr = psb->QueryActiveShellView(&lpSV); 878 if (FAILED_UNEXPECTEDLY(hr)) 879 return hr; 880 881 SVSIF selFlags = SVSI_DESELECTOTHERS | SVSI_EDIT | SVSI_ENSUREVISIBLE | SVSI_FOCUSED | SVSI_SELECT; 882 hr = lpSV->SelectItem(m_apidl[0], selFlags); 883 if (FAILED_UNEXPECTEDLY(hr)) 884 return hr; 885 886 return S_OK; 887 } 888 889 HRESULT 890 CDefaultContextMenu::DoProperties( 891 LPCMINVOKECOMMANDINFO lpcmi) 892 { 893 _DoCallback(DFM_INVOKECOMMAND, DFM_CMD_PROPERTIES, NULL); 894 895 return S_OK; 896 } 897 898 // This code is taken from CNewMenu and should be shared between the 2 classes 899 HRESULT 900 CDefaultContextMenu::DoCreateNewFolder( 901 LPCMINVOKECOMMANDINFO lpici) 902 { 903 WCHAR wszPath[MAX_PATH]; 904 WCHAR wszName[MAX_PATH]; 905 WCHAR wszNewFolder[25]; 906 HRESULT hr; 907 908 /* Get folder path */ 909 hr = SHGetPathFromIDListW(m_pidlFolder, wszPath); 910 if (FAILED_UNEXPECTEDLY(hr)) 911 return hr; 912 913 if (!LoadStringW(shell32_hInstance, IDS_NEWFOLDER, wszNewFolder, _countof(wszNewFolder))) 914 return E_FAIL; 915 916 /* Create the name of the new directory */ 917 if (!PathYetAnotherMakeUniqueName(wszName, wszPath, NULL, wszNewFolder)) 918 return E_FAIL; 919 920 /* Create the new directory and show the appropriate dialog in case of error */ 921 if (SHCreateDirectory(lpici->hwnd, wszName) != ERROR_SUCCESS) 922 return E_FAIL; 923 924 /* Show and select the new item in the def view */ 925 LPITEMIDLIST pidl; 926 PITEMID_CHILD pidlNewItem; 927 CComPtr<IShellView> psv; 928 929 /* Notify the view object about the new item */ 930 SHChangeNotify(SHCNE_MKDIR, SHCNF_PATHW, (LPCVOID)wszName, NULL); 931 932 if (!m_site) 933 return S_OK; 934 935 /* Get a pointer to the shell view */ 936 hr = IUnknown_QueryService(m_site, SID_IFolderView, IID_PPV_ARG(IShellView, &psv)); 937 if (FAILED_UNEXPECTEDLY(hr)) 938 return S_OK; 939 940 /* Attempt to get the pidl of the new item */ 941 hr = SHILCreateFromPathW(wszName, &pidl, NULL); 942 if (FAILED_UNEXPECTEDLY(hr)) 943 return hr; 944 945 pidlNewItem = ILFindLastID(pidl); 946 947 hr = psv->SelectItem(pidlNewItem, SVSI_DESELECTOTHERS | SVSI_EDIT | SVSI_ENSUREVISIBLE | 948 SVSI_FOCUSED | SVSI_SELECT); 949 if (FAILED_UNEXPECTEDLY(hr)) 950 return hr; 951 952 SHFree(pidl); 953 954 return S_OK; 955 } 956 957 PDynamicShellEntry CDefaultContextMenu::GetDynamicEntry(UINT idCmd) 958 { 959 PDynamicShellEntry pEntry = m_pDynamicEntries; 960 961 while(pEntry && idCmd >= pEntry->iIdCmdFirst + pEntry->NumIds) 962 pEntry = pEntry->pNext; 963 964 if (!pEntry) 965 return NULL; 966 967 if (idCmd < pEntry->iIdCmdFirst || idCmd > pEntry->iIdCmdFirst + pEntry->NumIds) 968 return NULL; 969 970 return pEntry; 971 } 972 973 // FIXME: 260 is correct, but should this be part of the SDK or just MAX_PATH? 974 #define MAX_VERB 260 975 976 BOOL 977 CDefaultContextMenu::MapVerbToCmdId(PVOID Verb, PUINT idCmd, BOOL IsUnicode) 978 { 979 WCHAR UnicodeStr[MAX_VERB]; 980 981 /* Loop through all the static verbs looking for a match */ 982 for (UINT i = 0; i < _countof(g_StaticInvokeCmdMap); i++) 983 { 984 /* We can match both ANSI and unicode strings */ 985 if (IsUnicode) 986 { 987 /* The static verbs are ANSI, get a unicode version before doing the compare */ 988 SHAnsiToUnicode(g_StaticInvokeCmdMap[i].szStringVerb, UnicodeStr, MAX_VERB); 989 if (!wcscmp(UnicodeStr, (LPWSTR)Verb)) 990 { 991 /* Return the Corresponding Id */ 992 *idCmd = g_StaticInvokeCmdMap[i].IntVerb; 993 return TRUE; 994 } 995 } 996 else 997 { 998 if (!strcmp(g_StaticInvokeCmdMap[i].szStringVerb, (LPSTR)Verb)) 999 { 1000 *idCmd = g_StaticInvokeCmdMap[i].IntVerb; 1001 return TRUE; 1002 } 1003 } 1004 } 1005 1006 return FALSE; 1007 } 1008 1009 HRESULT 1010 CDefaultContextMenu::InvokeShellExt( 1011 LPCMINVOKECOMMANDINFO lpcmi) 1012 { 1013 TRACE("verb %p first %x last %x\n", lpcmi->lpVerb, m_iIdSHEFirst, m_iIdSHELast); 1014 1015 UINT idCmd = LOWORD(lpcmi->lpVerb); 1016 PDynamicShellEntry pEntry = GetDynamicEntry(idCmd); 1017 if (!pEntry) 1018 return E_FAIL; 1019 1020 /* invoke the dynamic context menu */ 1021 lpcmi->lpVerb = MAKEINTRESOURCEA(idCmd - pEntry->iIdCmdFirst); 1022 return pEntry->pCM->InvokeCommand(lpcmi); 1023 } 1024 1025 DWORD 1026 CDefaultContextMenu::BrowserFlagsFromVerb(LPCMINVOKECOMMANDINFO lpcmi, PStaticShellEntry pEntry) 1027 { 1028 CComPtr<IShellBrowser> psb; 1029 HWND hwndTree; 1030 LPCWSTR FlagsName; 1031 WCHAR wszKey[256]; 1032 HRESULT hr; 1033 DWORD wFlags; 1034 DWORD cbVerb; 1035 1036 if (!m_site) 1037 return 0; 1038 1039 /* Get a pointer to the shell browser */ 1040 hr = IUnknown_QueryService(m_site, SID_IShellBrowser, IID_PPV_ARG(IShellBrowser, &psb)); 1041 if (FAILED_UNEXPECTEDLY(hr)) 1042 return 0; 1043 1044 /* See if we are in Explore or Browse mode. If the browser's tree is present, we are in Explore mode.*/ 1045 if (SUCCEEDED(psb->GetControlWindow(FCW_TREE, &hwndTree)) && hwndTree) 1046 FlagsName = L"ExplorerFlags"; 1047 else 1048 FlagsName = L"BrowserFlags"; 1049 1050 /* Try to get the flag from the verb */ 1051 hr = StringCbPrintfW(wszKey, sizeof(wszKey), L"shell\\%s", pEntry->szVerb); 1052 if (FAILED_UNEXPECTEDLY(hr)) 1053 return 0; 1054 1055 cbVerb = sizeof(wFlags); 1056 if (RegGetValueW(pEntry->hkClass, wszKey, FlagsName, RRF_RT_REG_DWORD, NULL, &wFlags, &cbVerb) == ERROR_SUCCESS) 1057 { 1058 return wFlags; 1059 } 1060 1061 return 0; 1062 } 1063 1064 HRESULT 1065 CDefaultContextMenu::TryToBrowse( 1066 LPCMINVOKECOMMANDINFO lpcmi, LPCITEMIDLIST pidl, DWORD wFlags) 1067 { 1068 CComPtr<IShellBrowser> psb; 1069 HRESULT hr; 1070 1071 if (!m_site) 1072 return E_FAIL; 1073 1074 /* Get a pointer to the shell browser */ 1075 hr = IUnknown_QueryService(m_site, SID_IShellBrowser, IID_PPV_ARG(IShellBrowser, &psb)); 1076 if (FAILED_UNEXPECTEDLY(hr)) 1077 return 0; 1078 1079 return psb->BrowseObject(ILCombine(m_pidlFolder, pidl), wFlags); 1080 } 1081 1082 HRESULT 1083 CDefaultContextMenu::InvokePidl(LPCMINVOKECOMMANDINFO lpcmi, LPCITEMIDLIST pidl, PStaticShellEntry pEntry) 1084 { 1085 LPITEMIDLIST pidlFull = ILCombine(m_pidlFolder, pidl); 1086 if (pidlFull == NULL) 1087 { 1088 return E_FAIL; 1089 } 1090 1091 WCHAR wszPath[MAX_PATH]; 1092 BOOL bHasPath = SHGetPathFromIDListW(pidlFull, wszPath); 1093 1094 WCHAR wszDir[MAX_PATH]; 1095 if (bHasPath) 1096 { 1097 wcscpy(wszDir, wszPath); 1098 PathRemoveFileSpec(wszDir); 1099 } 1100 else 1101 { 1102 SHGetPathFromIDListW(m_pidlFolder, wszDir); 1103 } 1104 1105 SHELLEXECUTEINFOW sei; 1106 ZeroMemory(&sei, sizeof(sei)); 1107 sei.cbSize = sizeof(sei); 1108 sei.hwnd = lpcmi->hwnd; 1109 sei.nShow = SW_SHOWNORMAL; 1110 sei.lpVerb = pEntry->szVerb; 1111 sei.lpDirectory = wszDir; 1112 sei.lpIDList = pidlFull; 1113 sei.hkeyClass = pEntry->hkClass; 1114 sei.fMask = SEE_MASK_CLASSKEY | SEE_MASK_IDLIST; 1115 if (bHasPath) 1116 { 1117 sei.lpFile = wszPath; 1118 } 1119 1120 ShellExecuteExW(&sei); 1121 1122 ILFree(pidlFull); 1123 1124 return S_OK; 1125 } 1126 1127 HRESULT 1128 CDefaultContextMenu::InvokeRegVerb( 1129 LPCMINVOKECOMMANDINFO lpcmi) 1130 { 1131 PStaticShellEntry pEntry = m_pStaticEntries; 1132 INT iCmd = LOWORD(lpcmi->lpVerb) - m_iIdSCMFirst; 1133 HRESULT hr; 1134 UINT i; 1135 1136 while (pEntry && (iCmd--) > 0) 1137 pEntry = pEntry->pNext; 1138 1139 if (iCmd > 0) 1140 return E_FAIL; 1141 1142 /* Get the browse flags to see if we need to browse */ 1143 DWORD wFlags = BrowserFlagsFromVerb(lpcmi, pEntry); 1144 BOOL bBrowsed = FALSE; 1145 1146 for (i=0; i < m_cidl; i++) 1147 { 1148 /* Check if we need to browse */ 1149 if (wFlags > 0) 1150 { 1151 /* In xp if we have browsed, we don't open any more folders. 1152 * In win7 we browse to the first folder we find and 1153 * open new windows for each of the rest of the folders */ 1154 if (bBrowsed) 1155 continue; 1156 1157 hr = TryToBrowse(lpcmi, m_apidl[i], wFlags); 1158 if (SUCCEEDED(hr)) 1159 { 1160 bBrowsed = TRUE; 1161 continue; 1162 } 1163 } 1164 1165 InvokePidl(lpcmi, m_apidl[i], pEntry); 1166 } 1167 1168 return S_OK; 1169 } 1170 1171 HRESULT 1172 WINAPI 1173 CDefaultContextMenu::InvokeCommand( 1174 LPCMINVOKECOMMANDINFO lpcmi) 1175 { 1176 CMINVOKECOMMANDINFO LocalInvokeInfo; 1177 HRESULT Result; 1178 UINT CmdId; 1179 1180 /* Take a local copy of the fixed members of the 1181 struct as we might need to modify the verb */ 1182 LocalInvokeInfo = *lpcmi; 1183 1184 /* Check if this is a string verb */ 1185 if (HIWORD(LocalInvokeInfo.lpVerb)) 1186 { 1187 /* Get the ID which corresponds to this verb, and update our local copy */ 1188 if (MapVerbToCmdId((LPVOID)LocalInvokeInfo.lpVerb, &CmdId, FALSE)) 1189 LocalInvokeInfo.lpVerb = MAKEINTRESOURCEA(CmdId); 1190 } 1191 1192 CmdId = LOWORD(LocalInvokeInfo.lpVerb); 1193 1194 if (m_pDynamicEntries && CmdId >= m_iIdSHEFirst && CmdId < m_iIdSHELast) 1195 { 1196 LocalInvokeInfo.lpVerb -= m_iIdSHEFirst; 1197 Result = InvokeShellExt(&LocalInvokeInfo); 1198 return Result; 1199 } 1200 1201 if (m_pStaticEntries && CmdId >= m_iIdSCMFirst && CmdId < m_iIdSCMLast) 1202 { 1203 LocalInvokeInfo.lpVerb -= m_iIdSCMFirst; 1204 Result = InvokeRegVerb(&LocalInvokeInfo); 1205 return Result; 1206 } 1207 1208 if (m_iIdCBFirst != m_iIdCBLast && CmdId >= m_iIdCBFirst && CmdId < m_iIdCBLast) 1209 { 1210 Result = _DoCallback(DFM_INVOKECOMMAND, CmdId - m_iIdCBFirst, NULL); 1211 return Result; 1212 } 1213 1214 if (m_iIdDfltFirst != m_iIdDfltLast && CmdId >= m_iIdDfltFirst && CmdId < m_iIdDfltLast) 1215 { 1216 CmdId -= m_iIdDfltFirst; 1217 /* See the definitions of IDM_CUT and co to see how this works */ 1218 CmdId += 0x7000; 1219 } 1220 1221 /* Check if this is a Id */ 1222 switch (CmdId) 1223 { 1224 case FCIDM_SHVIEW_INSERT: 1225 Result = DoPaste(&LocalInvokeInfo, FALSE); 1226 break; 1227 case FCIDM_SHVIEW_INSERTLINK: 1228 Result = DoPaste(&LocalInvokeInfo, TRUE); 1229 break; 1230 case FCIDM_SHVIEW_OPEN: 1231 case FCIDM_SHVIEW_EXPLORE: 1232 Result = DoOpenOrExplore(&LocalInvokeInfo); 1233 break; 1234 case FCIDM_SHVIEW_COPY: 1235 case FCIDM_SHVIEW_CUT: 1236 Result = DoCopyOrCut(&LocalInvokeInfo, CmdId == FCIDM_SHVIEW_COPY); 1237 break; 1238 case FCIDM_SHVIEW_CREATELINK: 1239 Result = DoCreateLink(&LocalInvokeInfo); 1240 break; 1241 case FCIDM_SHVIEW_DELETE: 1242 Result = DoDelete(&LocalInvokeInfo); 1243 break; 1244 case FCIDM_SHVIEW_RENAME: 1245 Result = DoRename(&LocalInvokeInfo); 1246 break; 1247 case FCIDM_SHVIEW_PROPERTIES: 1248 Result = DoProperties(&LocalInvokeInfo); 1249 break; 1250 case FCIDM_SHVIEW_NEWFOLDER: 1251 Result = DoCreateNewFolder(&LocalInvokeInfo); 1252 break; 1253 default: 1254 Result = E_INVALIDARG; 1255 ERR("Unhandled Verb %xl\n", LOWORD(LocalInvokeInfo.lpVerb)); 1256 break; 1257 } 1258 1259 return Result; 1260 } 1261 1262 HRESULT 1263 WINAPI 1264 CDefaultContextMenu::GetCommandString( 1265 UINT_PTR idCommand, 1266 UINT uFlags, 1267 UINT* lpReserved, 1268 LPSTR lpszName, 1269 UINT uMaxNameLen) 1270 { 1271 /* We don't handle the help text yet */ 1272 if (uFlags == GCS_HELPTEXTA || 1273 uFlags == GCS_HELPTEXTW || 1274 HIWORD(idCommand) != 0) 1275 { 1276 return E_NOTIMPL; 1277 } 1278 1279 UINT CmdId = LOWORD(idCommand); 1280 1281 if (m_pDynamicEntries && CmdId >= m_iIdSHEFirst && CmdId < m_iIdSHELast) 1282 { 1283 idCommand -= m_iIdSHEFirst; 1284 PDynamicShellEntry pEntry = GetDynamicEntry(idCommand); 1285 if (!pEntry) 1286 return E_FAIL; 1287 1288 idCommand -= pEntry->iIdCmdFirst; 1289 return pEntry->pCM->GetCommandString(idCommand, 1290 uFlags, 1291 lpReserved, 1292 lpszName, 1293 uMaxNameLen); 1294 } 1295 1296 if (m_pStaticEntries && CmdId >= m_iIdSCMFirst && CmdId < m_iIdSCMLast) 1297 { 1298 /* Validation just returns S_OK on a match. The id exists. */ 1299 if (uFlags == GCS_VALIDATEA || uFlags == GCS_VALIDATEW) 1300 return S_OK; 1301 1302 CmdId -= m_iIdSCMFirst; 1303 1304 PStaticShellEntry pEntry = m_pStaticEntries; 1305 while (pEntry && (CmdId--) > 0) 1306 pEntry = pEntry->pNext; 1307 1308 if (!pEntry) 1309 return E_INVALIDARG; 1310 1311 if (uFlags == GCS_VERBW) 1312 return StringCchCopyW((LPWSTR)lpszName, uMaxNameLen, pEntry->szVerb); 1313 1314 if (uFlags == GCS_VERBA) 1315 { 1316 if (SHUnicodeToAnsi(pEntry->szVerb, lpszName, uMaxNameLen)) 1317 return S_OK; 1318 } 1319 1320 return E_INVALIDARG; 1321 } 1322 1323 //FIXME: Should we handle callbacks here? 1324 if (m_iIdDfltFirst != m_iIdDfltLast && CmdId >= m_iIdDfltFirst && CmdId < m_iIdDfltLast) 1325 { 1326 CmdId -= m_iIdDfltFirst; 1327 /* See the definitions of IDM_CUT and co to see how this works */ 1328 CmdId += 0x7000; 1329 } 1330 1331 /* Loop looking for a matching Id */ 1332 for (UINT i = 0; i < _countof(g_StaticInvokeCmdMap); i++) 1333 { 1334 if (g_StaticInvokeCmdMap[i].IntVerb == CmdId) 1335 { 1336 /* Validation just returns S_OK on a match */ 1337 if (uFlags == GCS_VALIDATEA || uFlags == GCS_VALIDATEW) 1338 return S_OK; 1339 1340 /* Return a copy of the ANSI verb */ 1341 if (uFlags == GCS_VERBA) 1342 return StringCchCopyA(lpszName, uMaxNameLen, g_StaticInvokeCmdMap[i].szStringVerb); 1343 1344 /* Convert the ANSI verb to unicode and return that */ 1345 if (uFlags == GCS_VERBW) 1346 { 1347 if (SHAnsiToUnicode(g_StaticInvokeCmdMap[i].szStringVerb, (LPWSTR)lpszName, uMaxNameLen)) 1348 return S_OK; 1349 } 1350 } 1351 } 1352 1353 return E_INVALIDARG; 1354 } 1355 1356 HRESULT 1357 WINAPI 1358 CDefaultContextMenu::HandleMenuMsg( 1359 UINT uMsg, 1360 WPARAM wParam, 1361 LPARAM lParam) 1362 { 1363 /* FIXME: Should we implement this as well? */ 1364 return S_OK; 1365 } 1366 1367 HRESULT SHGetMenuIdFromMenuMsg(UINT uMsg, LPARAM lParam, UINT *CmdId) 1368 { 1369 if (uMsg == WM_DRAWITEM) 1370 { 1371 DRAWITEMSTRUCT* pDrawStruct = reinterpret_cast<DRAWITEMSTRUCT*>(lParam); 1372 *CmdId = pDrawStruct->itemID; 1373 return S_OK; 1374 } 1375 else if (uMsg == WM_MEASUREITEM) 1376 { 1377 MEASUREITEMSTRUCT* pMeasureStruct = reinterpret_cast<MEASUREITEMSTRUCT*>(lParam); 1378 *CmdId = pMeasureStruct->itemID; 1379 return S_OK; 1380 } 1381 1382 return E_FAIL; 1383 } 1384 1385 HRESULT SHSetMenuIdInMenuMsg(UINT uMsg, LPARAM lParam, UINT CmdId) 1386 { 1387 if (uMsg == WM_DRAWITEM) 1388 { 1389 DRAWITEMSTRUCT* pDrawStruct = reinterpret_cast<DRAWITEMSTRUCT*>(lParam); 1390 pDrawStruct->itemID = CmdId; 1391 return S_OK; 1392 } 1393 else if (uMsg == WM_MEASUREITEM) 1394 { 1395 MEASUREITEMSTRUCT* pMeasureStruct = reinterpret_cast<MEASUREITEMSTRUCT*>(lParam); 1396 pMeasureStruct->itemID = CmdId; 1397 return S_OK; 1398 } 1399 1400 return E_FAIL; 1401 } 1402 1403 HRESULT 1404 WINAPI 1405 CDefaultContextMenu::HandleMenuMsg2( 1406 UINT uMsg, 1407 WPARAM wParam, 1408 LPARAM lParam, 1409 LRESULT *plResult) 1410 { 1411 if (uMsg == WM_INITMENUPOPUP) 1412 { 1413 PDynamicShellEntry pEntry = m_pDynamicEntries; 1414 while (pEntry) 1415 { 1416 SHForwardContextMenuMsg(pEntry->pCM, uMsg, wParam, lParam, plResult, TRUE); 1417 pEntry = pEntry->pNext; 1418 } 1419 return S_OK; 1420 } 1421 1422 UINT CmdId; 1423 HRESULT hr = SHGetMenuIdFromMenuMsg(uMsg, lParam, &CmdId); 1424 if (FAILED(hr)) 1425 return S_FALSE; 1426 1427 if (CmdId < m_iIdSHEFirst || CmdId >= m_iIdSHELast) 1428 return S_FALSE; 1429 1430 CmdId -= m_iIdSHEFirst; 1431 PDynamicShellEntry pEntry = GetDynamicEntry(CmdId); 1432 if (pEntry) 1433 { 1434 SHSetMenuIdInMenuMsg(uMsg, lParam, CmdId - pEntry->iIdCmdFirst); 1435 SHForwardContextMenuMsg(pEntry->pCM, uMsg, wParam, lParam, plResult, TRUE); 1436 } 1437 1438 return S_OK; 1439 } 1440 1441 HRESULT 1442 WINAPI 1443 CDefaultContextMenu::SetSite(IUnknown *pUnkSite) 1444 { 1445 m_site = pUnkSite; 1446 return S_OK; 1447 } 1448 1449 HRESULT 1450 WINAPI 1451 CDefaultContextMenu::GetSite(REFIID riid, void **ppvSite) 1452 { 1453 if (!m_site) 1454 return E_FAIL; 1455 1456 return m_site->QueryInterface(riid, ppvSite); 1457 } 1458 1459 static 1460 HRESULT 1461 CDefaultContextMenu_CreateInstance(const DEFCONTEXTMENU *pdcm, LPFNDFMCALLBACK lpfn, REFIID riid, void **ppv) 1462 { 1463 return ShellObjectCreatorInit<CDefaultContextMenu>(pdcm, lpfn, riid, ppv); 1464 } 1465 1466 /************************************************************************* 1467 * SHCreateDefaultContextMenu [SHELL32.325] Vista API 1468 * 1469 */ 1470 1471 HRESULT 1472 WINAPI 1473 SHCreateDefaultContextMenu(const DEFCONTEXTMENU *pdcm, REFIID riid, void **ppv) 1474 { 1475 HRESULT hr; 1476 1477 if (!ppv) 1478 return E_INVALIDARG; 1479 1480 hr = CDefaultContextMenu_CreateInstance(pdcm, NULL, riid, ppv); 1481 if (FAILED_UNEXPECTEDLY(hr)) 1482 return hr; 1483 1484 return S_OK; 1485 } 1486 1487 /************************************************************************* 1488 * CDefFolderMenu_Create2 [SHELL32.701] 1489 * 1490 */ 1491 1492 HRESULT 1493 WINAPI 1494 CDefFolderMenu_Create2( 1495 PCIDLIST_ABSOLUTE pidlFolder, 1496 HWND hwnd, 1497 UINT cidl, 1498 PCUITEMID_CHILD_ARRAY apidl, 1499 IShellFolder *psf, 1500 LPFNDFMCALLBACK lpfn, 1501 UINT nKeys, 1502 const HKEY *ahkeyClsKeys, 1503 IContextMenu **ppcm) 1504 { 1505 DEFCONTEXTMENU dcm; 1506 dcm.hwnd = hwnd; 1507 dcm.pcmcb = NULL; 1508 dcm.pidlFolder = pidlFolder; 1509 dcm.psf = psf; 1510 dcm.cidl = cidl; 1511 dcm.apidl = apidl; 1512 dcm.punkAssociationInfo = NULL; 1513 dcm.cKeys = nKeys; 1514 dcm.aKeys = ahkeyClsKeys; 1515 1516 HRESULT hr = CDefaultContextMenu_CreateInstance(&dcm, lpfn, IID_PPV_ARG(IContextMenu, ppcm)); 1517 if (FAILED_UNEXPECTEDLY(hr)) 1518 return hr; 1519 1520 return S_OK; 1521 } 1522