Reactos
at master 2008 lines 62 kB view raw
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#include <compat_undoc.h> 11 12WINE_DEFAULT_DEBUG_CHANNEL(dmenu); 13 14// FIXME: 260 is correct, but should this be part of the SDK or just MAX_PATH? 15#define MAX_VERB 260 16 17static HRESULT 18SHELL_GetRegCLSID(HKEY hKey, LPCWSTR SubKey, LPCWSTR Value, CLSID &clsid) 19{ 20 WCHAR buf[42]; 21 DWORD cb = sizeof(buf); 22 DWORD err = RegGetValueW(hKey, SubKey, Value, RRF_RT_REG_SZ, NULL, buf, &cb); 23 return !err ? CLSIDFromString(buf, &clsid) : HRESULT_FROM_WIN32(err); 24} 25 26static BOOL InsertMenuItemAt(HMENU hMenu, UINT Pos, UINT Flags) 27{ 28 MENUITEMINFOW mii; 29 mii.cbSize = FIELD_OFFSET(MENUITEMINFOW, hbmpItem); // USER32 version agnostic 30 mii.fMask = MIIM_TYPE; 31 mii.fType = Flags; 32 return InsertMenuItemW(hMenu, Pos, TRUE, &mii); 33} 34 35static void DCMA_DestroyEntry(DCMENTRY &dcme) 36{ 37 if (!dcme.pCM) 38 return; 39 IUnknown_SetSite(dcme.pCM, NULL); 40 dcme.pCM->Release(); 41 dcme.pCM = NULL; 42} 43 44void DCMA_Destroy(HDCMA hDCMA) 45{ 46 UINT i = 0; 47 for (DCMENTRY *p; (p = DCMA_GetEntry(hDCMA, i)) != NULL; ++i) 48 DCMA_DestroyEntry(*p); 49 DSA_Destroy(hDCMA); 50} 51 52UINT DCMA_InsertMenuItems( 53 _In_ HDCMA hDCMA, 54 _In_ HDCIA hDCIA, 55 _In_opt_ LPCITEMIDLIST pidlFolder, 56 _In_opt_ IDataObject *pDO, 57 _In_opt_ HKEY *pKeys, 58 _In_opt_ UINT nKeys, 59 _In_ QCMINFO *pQCMI, 60 _In_opt_ UINT fCmf, 61 _In_opt_ IUnknown *pUnkSite) 62{ 63 UINT idCmdBase = pQCMI->idCmdFirst, idCmdFirst = idCmdBase; 64 UINT nOffset = 0; 65 66 // Insert in reverse order 67 for (int iCls = DCIA_GetCount(hDCIA) - 1; iCls >= 0; --iCls) 68 { 69 REFCLSID clsid = *DCIA_GetEntry(hDCIA, iCls); 70 if (fCmf & CMF_DEFAULTONLY) 71 { 72 WCHAR szKey[MAX_PATH]; 73 wcscpy(szKey, L"CLSID\\"); 74 StringFromGUID2(clsid, szKey + _countof(L"CLSID\\") - 1, CHARS_IN_GUID); 75 wcscpy(szKey + _countof(L"CLSID\\") - 1 + CHARS_IN_GUID - 1, L"\\shellex\\MayChangeDefaultMenu"); 76 if (!RegKeyExists(HKEY_CLASSES_ROOT, szKey)) 77 continue; 78 } 79 80 for (UINT iKey = 0; iKey < nKeys; ++iKey) 81 { 82 CComPtr<IShellExtInit> pInit; 83 HRESULT hr = SHExtCoCreateInstance(NULL, &clsid, NULL, IID_PPV_ARG(IShellExtInit, &pInit)); 84 if (FAILED(hr)) 85 break; 86 if (FAILED(hr = pInit->Initialize(pidlFolder, pDO, pKeys[iKey]))) 87 continue; 88 89 IContextMenu *pCM; 90 if (FAILED(hr = pInit->QueryInterface(IID_PPV_ARG(IContextMenu, &pCM)))) 91 break; 92 IUnknown_SetSite(pCM, pUnkSite); 93 94 hr = pCM->QueryContextMenu(pQCMI->hmenu, pQCMI->indexMenu + nOffset, idCmdFirst, pQCMI->idCmdLast, fCmf); 95 const UINT nCount = HRESULT_CODE(hr); 96 const UINT idThisFirst = idCmdFirst - idCmdBase; 97 DCMENTRY dcme = { pCM, idThisFirst, idThisFirst + nCount - 1 }; 98 if (hr > 0) 99 { 100 idCmdFirst += nCount; 101 if (DSA_AppendItem(hDCMA, &dcme) >= 0) 102 { 103 if (nOffset == 0 && GetMenuDefaultItem(pQCMI->hmenu, TRUE, 0) == 0) 104 nOffset++; // Insert new items below the default 105 break; 106 } 107 } 108 DCMA_DestroyEntry(dcme); 109 } 110 } 111 return idCmdFirst; 112} 113 114HRESULT DCMA_InvokeCommand(HDCMA hDCMA, CMINVOKECOMMANDINFO *pICI) 115{ 116 HRESULT hr = S_FALSE; 117 for (UINT i = 0;; ++i) 118 { 119 DCMENTRY *p = DCMA_GetEntry(hDCMA, i); 120 if (!p) 121 return hr; 122 123 UINT id = LOWORD(pICI->lpVerb); 124 if (!IS_INTRESOURCE(pICI->lpVerb)) 125 { 126 if (SUCCEEDED(hr = p->pCM->InvokeCommand(pICI))) 127 return hr; 128 } 129 else if (id >= p->idCmdFirst && id <= p->idCmdLast) 130 { 131 CMINVOKECOMMANDINFOEX ici; 132 CopyMemory(&ici, pICI, min(sizeof(ici), pICI->cbSize)); 133 ici.cbSize = min(sizeof(ici), pICI->cbSize); 134 ici.lpVerb = MAKEINTRESOURCEA(id - p->idCmdFirst); 135 ici.lpVerbW = (PWSTR)ici.lpVerb; 136 return p->pCM->InvokeCommand((CMINVOKECOMMANDINFO*)&ici); 137 } 138 } 139} 140 141typedef struct _DynamicShellEntry_ 142{ 143 UINT iIdCmdFirst; 144 UINT NumIds; 145 CLSID ClassID; 146 CComPtr<IContextMenu> pCM; 147} DynamicShellEntry, *PDynamicShellEntry; 148 149typedef struct _StaticShellEntry_ 150{ 151 CStringW Verb; 152 HKEY hkClass; 153} StaticShellEntry, *PStaticShellEntry; 154 155#define DCM_FCIDM_SHVIEW_OFFSET 0x7000 // Offset from the menu ids in the menu resource to FCIDM_SHVIEW_* 156 157// 158// verbs for InvokeCommandInfo 159// 160static const struct _StaticInvokeCommandMap_ 161{ 162 LPCSTR szStringVerb; 163 WORD IntVerb; 164 SHORT DfmCmd; 165} g_StaticInvokeCmdMap[] = 166{ 167 { "runas", 0 }, // Unimplemented 168 { "print", 0 }, // Unimplemented 169 { "preview", 0 }, // Unimplemented 170 { "open", FCIDM_SHVIEW_OPEN }, 171 { CMDSTR_NEWFOLDERA, FCIDM_SHVIEW_NEWFOLDER, (SHORT)DFM_CMD_NEWFOLDER }, 172 { "cut", FCIDM_SHVIEW_CUT, /* ? */ }, 173 { "copy", FCIDM_SHVIEW_COPY, (SHORT)DFM_CMD_COPY }, 174 { "paste", FCIDM_SHVIEW_INSERT, (SHORT)DFM_CMD_PASTE }, 175 { "link", FCIDM_SHVIEW_CREATELINK, (SHORT)DFM_CMD_LINK }, 176 { "delete", FCIDM_SHVIEW_DELETE, (SHORT)DFM_CMD_DELETE }, 177 { "properties", FCIDM_SHVIEW_PROPERTIES, (SHORT)DFM_CMD_PROPERTIES }, 178 { "rename", FCIDM_SHVIEW_RENAME, (SHORT)DFM_CMD_RENAME }, 179 { "copyto", FCIDM_SHVIEW_COPYTO }, 180 { "moveto", FCIDM_SHVIEW_MOVETO }, 181}; 182 183PCSTR MapFcidmCmdToVerb(_In_ UINT_PTR CmdId) 184{ 185 for (SIZE_T i = 0; i < _countof(g_StaticInvokeCmdMap); ++i) 186 { 187 if (g_StaticInvokeCmdMap[i].IntVerb == CmdId && CmdId) 188 return g_StaticInvokeCmdMap[i].szStringVerb; 189 } 190 return NULL; 191} 192 193UINT MapVerbToDfmCmd(_In_ LPCSTR verba) 194{ 195 for (UINT i = 0; i < _countof(g_StaticInvokeCmdMap); ++i) 196 { 197 if (!lstrcmpiA(g_StaticInvokeCmdMap[i].szStringVerb, verba)) 198 return (int)g_StaticInvokeCmdMap[i].DfmCmd; 199 } 200 return 0; 201} 202 203static HRESULT 204MapVerbToCmdId(PVOID Verb, BOOL IsUnicode, IContextMenu *pCM, UINT idFirst, UINT idLast) 205{ 206 const UINT gcs = IsUnicode ? GCS_VERBW : GCS_VERBA; 207 for (UINT id = idFirst; id <= idLast; ++id) 208 { 209 WCHAR buf[MAX_PATH]; 210 *buf = UNICODE_NULL; 211 HRESULT hr = pCM->GetCommandString(id, gcs, NULL, (LPSTR)buf, _countof(buf)); 212 if (FAILED(hr) || !*buf) 213 continue; 214 else if (IsUnicode && !_wcsicmp((LPWSTR)Verb, buf)) 215 return id; 216 else if (!IsUnicode && !lstrcmpiA((LPSTR)Verb, (LPSTR)buf)) 217 return id; 218 } 219 return HRESULT_FROM_WIN32(ERROR_NOT_FOUND); 220} 221 222static inline bool IsVerbListSeparator(WCHAR Ch) 223{ 224 return Ch == L' ' || Ch == L','; // learn.microsoft.com/en-us/windows/win32/shell/context-menu-handlers 225} 226 227static int FindVerbInDefaultVerbList(LPCWSTR List, LPCWSTR Verb) 228{ 229 for (UINT index = 0; *List; ++index) 230 { 231 while (IsVerbListSeparator(*List)) 232 List++; 233 LPCWSTR Start = List; 234 while (*List && !IsVerbListSeparator(*List)) 235 List++; 236 // "List > Start" to verify that the list item is non-empty to avoid the edge case where Verb is "" and the list contains ",," 237 if (!_wcsnicmp(Verb, Start, List - Start) && List > Start) 238 return index; 239 } 240 return -1; 241} 242 243EXTERN_C HRESULT SHELL32_EnumDefaultVerbList(LPCWSTR List, UINT Index, LPWSTR Verb, SIZE_T cchMax) 244{ 245 for (UINT i = 0; *List; ++i) 246 { 247 while (IsVerbListSeparator(*List)) 248 List++; 249 LPCWSTR Start = List; 250 while (*List && !IsVerbListSeparator(*List)) 251 List++; 252 if (List > Start && i == Index) 253 return StringCchCopyNW(Verb, cchMax, Start, List - Start); 254 } 255 return HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS); 256} 257 258static HRESULT GetFriendlyVerb(_In_ PCWSTR pszVerb, _Out_ PWSTR pszBuf, _In_ SIZE_T cchMax) 259{ 260 static const struct { PCWSTR pszVerb; WORD iResId; } map[] = 261 { 262 // { L"open", IDS_OPEN_VERB }, These two have already been handled 263 // { L"explore", IDS_EXPLORE_VERB }, 264 { L"edit", IDS_EDIT_VERB }, 265 { L"print", IDS_PRINT_VERB }, 266 { L"runas", IDS_RUNAS_VERB }, 267 { L"openas", IDS_OPEN_VERB }, 268 { L"find", IDS_FIND_VERB }, 269 }; 270 for (SIZE_T i = 0; i < _countof(map); ++i) 271 { 272 if (!_wcsicmp(pszVerb, map[i].pszVerb) && 273 LoadStringW(shell32_hInstance, map[i].iResId, pszBuf, cchMax)) 274 { 275 return S_OK; 276 } 277 } 278 279 // Try to make a friendly verb based on the verb subkey 280 if (pszVerb[0] < 127 && !StrChrW(pszVerb, '&') && SUCCEEDED(StringCchCopyW(pszBuf + 1, --cchMax, pszVerb))) 281 { 282 *pszBuf = L'&'; 283 return S_OK; // This can be changed to S_FALSE if the caller needs to know we faked it 284 } 285 return E_FAIL; 286} 287 288class CDefaultContextMenu : 289 public CComObjectRootEx<CComMultiThreadModelNoCS>, 290 public IContextMenu3, 291 public IObjectWithSite, 292 public IServiceProvider 293{ 294 private: 295 CComPtr<IUnknown> m_site; 296 CComPtr<IShellFolder> m_psf; 297 CComPtr<IContextMenuCB> m_pmcb; 298 LPFNDFMCALLBACK m_pfnmcb; 299 UINT m_cidl; 300 PCUITEMID_CHILD_ARRAY m_apidl; 301 CComPtr<IDataObject> m_pDataObj; 302 HKEY m_aKeys[16]; // This limit is documented for both the old API and for DEFCONTEXTMENU 303 UINT m_cKeys; 304 PIDLIST_ABSOLUTE m_pidlFolder; 305 DWORD m_bGroupPolicyActive; 306 UINT m_iIdQCMFirst; /* The first id passed to us in QueryContextMenu */ 307 CAtlList<DynamicShellEntry> m_DynamicEntries; 308 UINT m_iIdSHEFirst; /* first used id */ 309 UINT m_iIdSHELast; /* last used id */ 310 CAtlList<StaticShellEntry> m_StaticEntries; 311 UINT m_iIdSCMFirst; /* first static used id */ 312 UINT m_iIdSCMLast; /* last static used id */ 313 UINT m_iIdCBFirst; /* first callback used id */ 314 UINT m_iIdCBLast; /* last callback used id */ 315 UINT m_iIdDfltFirst; /* first default part id */ 316 UINT m_iIdDfltLast; /* last default part id */ 317 HWND m_hwnd; /* window passed to callback */ 318 WCHAR m_DefVerbs[MAX_PATH]; 319 320 HRESULT _DoCallback(UINT uMsg, WPARAM wParam, LPVOID lParam); 321 HRESULT _DoInvokeCommandCallback(LPCMINVOKECOMMANDINFOEX lpcmi, WPARAM CmdId); 322 void AddStaticEntry(const HKEY hkeyClass, const WCHAR *szVerb, UINT uFlags); 323 void AddStaticEntriesForKey(HKEY hKey, UINT uFlags); 324 void TryPickDefault(HMENU hMenu, UINT idCmdFirst, UINT DfltOffset, UINT uFlags); 325 BOOL IsShellExtensionAlreadyLoaded(REFCLSID clsid); 326 HRESULT LoadDynamicContextMenuHandler(HKEY hKey, REFCLSID clsid); 327 BOOL EnumerateDynamicContextHandlerForKey(HKEY hRootKey); 328 UINT AddShellExtensionsToMenu(HMENU hMenu, UINT* pIndexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags); 329 UINT AddStaticContextMenusToMenu(HMENU hMenu, UINT* IndexMenu, UINT iIdCmdFirst, UINT iIdCmdLast, UINT uFlags); 330 HRESULT DoPaste(LPCMINVOKECOMMANDINFOEX lpcmi, BOOL bLink); 331 HRESULT DoOpenOrExplore(LPCMINVOKECOMMANDINFOEX lpcmi); 332 HRESULT DoCreateLink(LPCMINVOKECOMMANDINFOEX lpcmi); 333 HRESULT DoDelete(LPCMINVOKECOMMANDINFOEX lpcmi); 334 HRESULT DoCopyOrCut(LPCMINVOKECOMMANDINFOEX lpcmi, BOOL bCopy); 335 HRESULT DoRename(LPCMINVOKECOMMANDINFOEX lpcmi); 336 HRESULT DoProperties(LPCMINVOKECOMMANDINFOEX lpcmi); 337 HRESULT DoUndo(LPCMINVOKECOMMANDINFOEX lpcmi); 338 HRESULT DoCreateNewFolder(LPCMINVOKECOMMANDINFOEX lpici); 339 HRESULT DoCopyToMoveToFolder(LPCMINVOKECOMMANDINFOEX lpici, BOOL bCopy); 340 HRESULT InvokeShellExt(LPCMINVOKECOMMANDINFOEX lpcmi); 341 HRESULT InvokeRegVerb(LPCMINVOKECOMMANDINFOEX lpcmi); 342 DWORD BrowserFlagsFromVerb(LPCMINVOKECOMMANDINFOEX lpcmi, PStaticShellEntry pEntry); 343 HRESULT TryToBrowse(LPCMINVOKECOMMANDINFOEX lpcmi, LPCITEMIDLIST pidl, DWORD wFlags); 344 HRESULT InvokePidl(LPCMINVOKECOMMANDINFOEX lpcmi, LPCITEMIDLIST pidl, PStaticShellEntry pEntry); 345 PDynamicShellEntry GetDynamicEntry(UINT idCmd); 346 BOOL MapVerbToCmdId(PVOID Verb, PUINT idCmd, BOOL IsUnicode); 347 348 public: 349 CDefaultContextMenu(); 350 ~CDefaultContextMenu(); 351 HRESULT WINAPI Initialize(const DEFCONTEXTMENU *pdcm, LPFNDFMCALLBACK lpfn); 352 353 // IContextMenu 354 STDMETHOD(QueryContextMenu)(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) override; 355 STDMETHOD(InvokeCommand)(LPCMINVOKECOMMANDINFO lpcmi) override; 356 STDMETHOD(GetCommandString)(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen) override; 357 358 // IContextMenu2 359 STDMETHOD(HandleMenuMsg)(UINT uMsg, WPARAM wParam, LPARAM lParam) override; 360 361 // IContextMenu3 362 STDMETHOD(HandleMenuMsg2)(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plResult) override; 363 364 // IObjectWithSite 365 STDMETHOD(SetSite)(IUnknown *pUnkSite) override; 366 STDMETHOD(GetSite)(REFIID riid, void **ppvSite) override; 367 368 // IServiceProvider 369 STDMETHOD(QueryService)(REFGUID svc, REFIID riid, void**ppv) override 370 { 371 return IUnknown_QueryService(m_site, svc, riid, ppv); 372 } 373 374 BEGIN_COM_MAP(CDefaultContextMenu) 375 COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu) 376 COM_INTERFACE_ENTRY_IID(IID_IContextMenu2, IContextMenu2) 377 COM_INTERFACE_ENTRY_IID(IID_IContextMenu3, IContextMenu3) 378 COM_INTERFACE_ENTRY_IID(IID_IObjectWithSite, IObjectWithSite) 379 COM_INTERFACE_ENTRY_IID(IID_IServiceProvider, IServiceProvider) 380 END_COM_MAP() 381}; 382 383CDefaultContextMenu::CDefaultContextMenu() : 384 m_psf(NULL), 385 m_pmcb(NULL), 386 m_pfnmcb(NULL), 387 m_cidl(0), 388 m_apidl(NULL), 389 m_pDataObj(NULL), 390 m_cKeys(0), 391 m_pidlFolder(NULL), 392 m_bGroupPolicyActive(0), 393 m_iIdQCMFirst(0), 394 m_iIdSHEFirst(0), 395 m_iIdSHELast(0), 396 m_iIdSCMFirst(0), 397 m_iIdSCMLast(0), 398 m_iIdCBFirst(0), 399 m_iIdCBLast(0), 400 m_iIdDfltFirst(0), 401 m_iIdDfltLast(0), 402 m_hwnd(NULL) 403{ 404 *m_DefVerbs = UNICODE_NULL; 405} 406 407CDefaultContextMenu::~CDefaultContextMenu() 408{ 409 for (POSITION it = m_DynamicEntries.GetHeadPosition(); it != NULL;) 410 { 411 const DynamicShellEntry& info = m_DynamicEntries.GetNext(it); 412 IUnknown_SetSite(info.pCM.p, NULL); 413 } 414 m_DynamicEntries.RemoveAll(); 415 m_StaticEntries.RemoveAll(); 416 417 for (UINT i = 0; i < m_cKeys; i++) 418 RegCloseKey(m_aKeys[i]); 419 420 if (m_pidlFolder) 421 CoTaskMemFree(m_pidlFolder); 422 _ILFreeaPidl(const_cast<PITEMID_CHILD *>(m_apidl), m_cidl); 423} 424 425HRESULT WINAPI CDefaultContextMenu::Initialize(const DEFCONTEXTMENU *pdcm, LPFNDFMCALLBACK lpfn) 426{ 427 TRACE("cidl %u\n", pdcm->cidl); 428 429 HRESULT hr = S_OK; 430 if (!pdcm->pcmcb && !lpfn) 431 { 432 ERR("CDefaultContextMenu needs a callback!\n"); 433 return E_INVALIDARG; 434 } 435 436 m_cidl = pdcm->cidl; 437 m_apidl = const_cast<PCUITEMID_CHILD_ARRAY>(_ILCopyaPidl(pdcm->apidl, m_cidl)); 438 if (m_cidl && !m_apidl) 439 return E_OUTOFMEMORY; 440 m_psf = pdcm->psf; 441 m_pmcb = pdcm->pcmcb; 442 m_pfnmcb = lpfn; 443 m_hwnd = pdcm->hwnd; 444 445 for (UINT i = 0; i < pdcm->cKeys; ++i) 446 { 447 if (i >= _countof(m_aKeys)) 448 hr = E_INVALIDARG; 449 else if ((m_aKeys[i] = SHRegDuplicateHKey(pdcm->aKeys[i])) != NULL) 450 m_cKeys++; 451 else 452 hr = E_OUTOFMEMORY; 453 } 454 455 m_psf->GetUIObjectOf(pdcm->hwnd, m_cidl, m_apidl, IID_NULL_PPV_ARG(IDataObject, &m_pDataObj)); 456 457 if (pdcm->pidlFolder) 458 { 459 m_pidlFolder = ILClone(pdcm->pidlFolder); 460 } 461 else 462 { 463 CComPtr<IPersistFolder2> pf = NULL; 464 if (SUCCEEDED(m_psf->QueryInterface(IID_PPV_ARG(IPersistFolder2, &pf)))) 465 { 466 if (FAILED(pf->GetCurFolder(&m_pidlFolder))) 467 ERR("GetCurFolder failed\n"); 468 } 469 TRACE("pidlFolder %p\n", m_pidlFolder); 470 } 471 472 return hr; 473} 474 475HRESULT CDefaultContextMenu::_DoCallback(UINT uMsg, WPARAM wParam, LPVOID lParam) 476{ 477 if (m_pmcb) 478 { 479 return m_pmcb->CallBack(m_psf, m_hwnd, m_pDataObj, uMsg, wParam, (LPARAM)lParam); 480 } 481 else if(m_pfnmcb) 482 { 483 return m_pfnmcb(m_psf, m_hwnd, m_pDataObj, uMsg, wParam, (LPARAM)lParam); 484 } 485 486 return E_FAIL; 487} 488 489void CDefaultContextMenu::AddStaticEntry(const HKEY hkeyClass, const WCHAR *szVerb, UINT uFlags) 490{ 491 POSITION it = m_StaticEntries.GetHeadPosition(); 492 while (it != NULL) 493 { 494 const StaticShellEntry& info = m_StaticEntries.GetNext(it); 495 if (info.Verb.CompareNoCase(szVerb) == 0) 496 { 497 /* entry already exists */ 498 return; 499 } 500 } 501 502 TRACE("adding verb %s\n", debugstr_w(szVerb)); 503 504 if (!_wcsicmp(szVerb, L"open") && !(uFlags & CMF_NODEFAULT)) 505 { 506 /* open verb is always inserted in front */ 507 m_StaticEntries.AddHead({ szVerb, hkeyClass }); 508 } 509 else 510 { 511 m_StaticEntries.AddTail({ szVerb, hkeyClass }); 512 } 513} 514 515void CDefaultContextMenu::AddStaticEntriesForKey(HKEY hKey, UINT uFlags) 516{ 517 WCHAR wszName[VERBKEY_CCHMAX]; 518 DWORD cchName, dwIndex = 0; 519 HKEY hShellKey; 520 521 LRESULT lres = RegOpenKeyExW(hKey, L"shell", 0, KEY_READ, &hShellKey); 522 if (lres != STATUS_SUCCESS) 523 return; 524 525 if (!*m_DefVerbs) 526 { 527 DWORD cb = sizeof(m_DefVerbs); 528 RegGetValueW(hShellKey, NULL, NULL, RRF_RT_REG_SZ, NULL, m_DefVerbs, &cb); 529 } 530 531 while(TRUE) 532 { 533 cchName = _countof(wszName); 534 if (RegEnumKeyExW(hShellKey, dwIndex++, wszName, &cchName, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) 535 break; 536 537 AddStaticEntry(hKey, wszName, uFlags); 538 } 539 540 RegCloseKey(hShellKey); 541} 542 543static 544BOOL 545HasClipboardData() 546{ 547 BOOL bRet = FALSE; 548 CComPtr<IDataObject> pDataObj; 549 550 if (SUCCEEDED(OleGetClipboard(&pDataObj))) 551 { 552 FORMATETC formatetc; 553 554 TRACE("pDataObj=%p\n", pDataObj.p); 555 556 /* Set the FORMATETC structure*/ 557 InitFormatEtc(formatetc, RegisterClipboardFormatW(CFSTR_SHELLIDLIST), TYMED_HGLOBAL); 558 bRet = SUCCEEDED(pDataObj->QueryGetData(&formatetc)); 559 } 560 561 return bRet; 562} 563 564BOOL 565CDefaultContextMenu::IsShellExtensionAlreadyLoaded(REFCLSID clsid) 566{ 567 POSITION it = m_DynamicEntries.GetHeadPosition(); 568 while (it != NULL) 569 { 570 const DynamicShellEntry& info = m_DynamicEntries.GetNext(it); 571 if (info.ClassID == clsid) 572 return TRUE; 573 } 574 575 return FALSE; 576} 577 578HRESULT 579CDefaultContextMenu::LoadDynamicContextMenuHandler(HKEY hKey, REFCLSID clsid) 580{ 581 HRESULT hr; 582 TRACE("LoadDynamicContextMenuHandler entered with This %p hKey %p pclsid %s\n", this, hKey, wine_dbgstr_guid(&clsid)); 583 584 if (IsShellExtensionAlreadyLoaded(clsid)) 585 return S_OK; 586 587 CComPtr<IContextMenu> pcm; 588 hr = SHCoCreateInstance(NULL, &clsid, NULL, IID_PPV_ARG(IContextMenu, &pcm)); 589 if (FAILED(hr)) 590 { 591 ERR("SHCoCreateInstance(IContextMenu) failed.clsid %s hr 0x%x\n", wine_dbgstr_guid(&clsid), hr); 592 return hr; 593 } 594 595 CComPtr<IShellExtInit> pExtInit; 596 hr = pcm->QueryInterface(IID_PPV_ARG(IShellExtInit, &pExtInit)); 597 if (FAILED(hr)) 598 { 599 ERR("IContextMenu->QueryInterface(IShellExtInit) failed.clsid %s hr 0x%x\n", wine_dbgstr_guid(&clsid), hr); 600 return hr; 601 } 602 603 hr = pExtInit->Initialize(m_pDataObj ? NULL : m_pidlFolder, m_pDataObj, hKey); 604 if (FAILED(hr)) 605 { 606 WARN("IShellExtInit::Initialize failed.clsid %s hr 0x%x\n", wine_dbgstr_guid(&clsid), hr); 607 return hr; 608 } 609 610 if (m_site) 611 IUnknown_SetSite(pcm, m_site); 612 613 m_DynamicEntries.AddTail({ 0, 0, clsid, pcm }); 614 615 return S_OK; 616} 617 618BOOL 619CDefaultContextMenu::EnumerateDynamicContextHandlerForKey(HKEY hRootKey) 620{ 621 WCHAR wszName[MAX_PATH], wszBuf[MAX_PATH], *pwszClsid; 622 DWORD cchName; 623 HRESULT hr; 624 HKEY hKey; 625 626 if (RegOpenKeyExW(hRootKey, L"shellex\\ContextMenuHandlers", 0, KEY_READ, &hKey) != ERROR_SUCCESS) 627 { 628 TRACE("RegOpenKeyExW failed\n"); 629 return FALSE; 630 } 631 632 DWORD dwIndex = 0; 633 while (TRUE) 634 { 635 cchName = _countof(wszName); 636 if (RegEnumKeyExW(hKey, dwIndex++, wszName, &cchName, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) 637 break; 638 639 /* Key name or key value is CLSID */ 640 CLSID clsid; 641 hr = CLSIDFromString(wszName, &clsid); 642 if (hr == S_OK) 643 pwszClsid = wszName; 644 else 645 { 646 DWORD cchBuf = _countof(wszBuf); 647 if (RegGetValueW(hKey, wszName, NULL, RRF_RT_REG_SZ, NULL, wszBuf, &cchBuf) == ERROR_SUCCESS) 648 hr = CLSIDFromString(wszBuf, &clsid); 649 pwszClsid = wszBuf; 650 } 651 652 if (FAILED(hr)) 653 { 654 ERR("CLSIDFromString failed for clsid %S hr 0x%x\n", pwszClsid, hr); 655 continue; 656 } 657 658 if (m_bGroupPolicyActive) 659 { 660 if (RegGetValueW(HKEY_LOCAL_MACHINE, 661 L"Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved", 662 pwszClsid, 663 RRF_RT_REG_SZ, 664 NULL, 665 NULL, 666 NULL) != ERROR_SUCCESS) 667 { 668 ERR("Shell extension %s not approved!\n", pwszClsid); 669 continue; 670 } 671 } 672 673 hr = LoadDynamicContextMenuHandler(hRootKey, clsid); 674 if (FAILED(hr)) 675 WARN("Failed to get context menu entires from shell extension! clsid: %S\n", pwszClsid); 676 } 677 678 RegCloseKey(hKey); 679 return TRUE; 680} 681 682UINT 683CDefaultContextMenu::AddShellExtensionsToMenu(HMENU hMenu, UINT* pIndexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) 684{ 685 UINT cIds = 0; 686 687 if (m_DynamicEntries.IsEmpty()) 688 return cIds; 689 690 POSITION it = m_DynamicEntries.GetHeadPosition(); 691 while (it != NULL) 692 { 693 DynamicShellEntry& info = m_DynamicEntries.GetNext(it); 694 695 HRESULT hr = info.pCM->QueryContextMenu(hMenu, *pIndexMenu, idCmdFirst + cIds, idCmdLast, uFlags); 696 if (SUCCEEDED(hr)) 697 { 698 info.iIdCmdFirst = cIds; 699 info.NumIds = HRESULT_CODE(hr); 700 (*pIndexMenu) += info.NumIds; 701 702 cIds += info.NumIds; 703 if (idCmdFirst + cIds >= idCmdLast) 704 break; 705 } 706 TRACE("pEntry hr %x contextmenu %p cmdfirst %x num ids %x\n", hr, info.pCM.p, info.iIdCmdFirst, info.NumIds); 707 } 708 return cIds; 709} 710 711UINT 712CDefaultContextMenu::AddStaticContextMenusToMenu( 713 HMENU hMenu, 714 UINT* pIndexMenu, 715 UINT iIdCmdFirst, 716 UINT iIdCmdLast, 717 UINT uFlags) 718{ 719 UINT ntver = RosGetProcessEffectiveVersion(); 720 MENUITEMINFOW mii = { sizeof(mii) }; 721 WCHAR wszDispVerb[80]; // The limit on XP. If the friendly string is longer, it falls back to the verb key. 722 UINT fState, idVerbRes; 723 UINT cIds = 0, indexFirst = *pIndexMenu, indexDefault; 724 int iDefVerbIndex = -1; 725 726 mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE | MIIM_DATA; 727 mii.fType = MFT_STRING; 728 729 POSITION it = m_StaticEntries.GetHeadPosition(); 730 bool first = true; 731 while (it != NULL) 732 { 733 StaticShellEntry& info = m_StaticEntries.GetNext(it); 734 BOOL forceFirstPos = FALSE; 735 bool hide = false; 736 737 fState = MFS_ENABLED; 738 739 /* set first entry as default */ 740 if (first) 741 { 742 fState |= MFS_DEFAULT; 743 first = false; 744 } 745 746 if (info.Verb.CompareNoCase(L"open") == 0) 747 { 748 idVerbRes = IDS_OPEN_VERB; 749 fState |= MFS_DEFAULT; /* override default when open verb is found */ 750 forceFirstPos++; 751 } 752 else if (info.Verb.CompareNoCase(L"explore") == 0) 753 { 754 idVerbRes = IDS_EXPLORE_VERB; 755 if (uFlags & CMF_EXPLORE) 756 { 757 fState |= MFS_DEFAULT; 758 forceFirstPos++; 759 } 760 } 761 else if (info.Verb.CompareNoCase(L"printto") == 0) 762 hide = true; 763 else 764 idVerbRes = 0; 765 766 /* By default use verb for menu item name */ 767 mii.dwTypeData = (LPWSTR)info.Verb.GetString(); 768 769 WCHAR wszKey[sizeof("shell\\") + MAX_VERB]; 770 HRESULT hr = StringCbPrintfW(wszKey, sizeof(wszKey), L"shell\\%s", info.Verb.GetString()); 771 if (FAILED_UNEXPECTEDLY(hr)) 772 hide = true; 773 774 UINT cmdFlags = 0; 775 HKEY hkVerb; 776 if (RegOpenKeyExW(info.hkClass, wszKey, 0, KEY_READ, &hkVerb) == ERROR_SUCCESS) 777 { 778 if (!(uFlags & CMF_OPTIMIZEFORINVOKE)) 779 { 780 DWORD cbVerb = sizeof(wszDispVerb); 781 LONG res = RegLoadMUIStringW(hkVerb, L"MUIVerb", wszDispVerb, cbVerb, NULL, 0, NULL); 782 if (res || !*wszDispVerb) 783 res = RegLoadMUIStringW(hkVerb, NULL, wszDispVerb, cbVerb, NULL, 0, NULL); 784 785 if ((res == ERROR_SUCCESS && *wszDispVerb) || 786 (idVerbRes && LoadStringW(shell32_hInstance, idVerbRes, wszDispVerb, _countof(wszDispVerb))) || 787 SUCCEEDED(GetFriendlyVerb(info.Verb, wszDispVerb, _countof(wszDispVerb)))) 788 { 789 mii.dwTypeData = wszDispVerb; 790 } 791 } 792 } 793 else 794 { 795 hkVerb = NULL; 796 } 797 798 if (hkVerb) 799 { 800 if (!hide && !(uFlags & CMF_EXTENDEDVERBS)) 801 hide = RegValueExists(hkVerb, L"Extended"); 802 803 if (!hide) 804 hide = RegValueExists(hkVerb, L"ProgrammaticAccessOnly"); 805 806 if (!hide && !(uFlags & CMF_DISABLEDVERBS)) 807 hide = RegValueExists(hkVerb, L"LegacyDisable"); 808 809 if (DWORD dwRest = (hide ? 0 : RegGetDword(hkVerb, NULL, L"SuppressionPolicy", 0))) 810 hide = SHRestricted((RESTRICTIONS)dwRest); 811 812 if (RegValueExists(hkVerb, L"NeverDefault")) 813 fState &= ~MFS_DEFAULT; 814 815 if (RegValueExists(hkVerb, L"SeparatorBefore")) 816 cmdFlags |= ECF_SEPARATORBEFORE; 817 if (RegValueExists(hkVerb, L"SeparatorAfter")) 818 cmdFlags |= ECF_SEPARATORAFTER; 819 820 RegCloseKey(hkVerb); 821 } 822 823 if (((uFlags & CMF_NODEFAULT) && ntver >= _WIN32_WINNT_VISTA) || 824 ((uFlags & CMF_DONOTPICKDEFAULT) && ntver >= _WIN32_WINNT_WIN7)) 825 { 826 fState &= ~MFS_DEFAULT; 827 } 828 829 if (!hide) 830 { 831 if (cmdFlags & ECF_SEPARATORBEFORE) 832 { 833 if (InsertMenuItemAt(hMenu, *pIndexMenu, MF_SEPARATOR)) 834 (*pIndexMenu)++; 835 } 836 837 UINT pos = *pIndexMenu; 838 int verbIndex = hkVerb ? FindVerbInDefaultVerbList(m_DefVerbs, info.Verb) : -1; 839 if (verbIndex >= 0) 840 { 841 if (verbIndex < iDefVerbIndex || iDefVerbIndex < 0) 842 { 843 iDefVerbIndex = verbIndex; 844 fState |= MFS_DEFAULT; 845 forceFirstPos = TRUE; 846 } 847 else 848 { 849 fState &= ~MFS_DEFAULT; // We have already set a better default 850 pos = indexDefault; 851 } 852 } 853 else if (iDefVerbIndex >= 0) 854 { 855 fState &= ~MFS_DEFAULT; // We have already set the default 856 if (forceFirstPos) 857 pos = indexDefault; 858 forceFirstPos = FALSE; 859 } 860 861 mii.fState = fState; 862 mii.wID = iIdCmdFirst + cIds; 863 if (InsertMenuItemW(hMenu, forceFirstPos ? indexFirst : pos, TRUE, &mii)) 864 (*pIndexMenu)++; 865 866 if (cmdFlags & ECF_SEPARATORAFTER) 867 { 868 if (InsertMenuItemAt(hMenu, *pIndexMenu, MF_SEPARATOR)) 869 (*pIndexMenu)++; 870 } 871 872 if (fState & MFS_DEFAULT) 873 indexDefault = *pIndexMenu; // This is where we want to insert "high priority" verbs 874 } 875 cIds++; // Always increment the id because it acts as the index into m_StaticEntries 876 877 if (mii.wID >= iIdCmdLast) 878 break; 879 } 880 881 return cIds; 882} 883 884BOOL WINAPI _InsertMenuItemW( 885 HMENU hMenu, 886 UINT indexMenu, 887 BOOL fByPosition, 888 UINT wID, 889 UINT fType, 890 LPCWSTR dwTypeData, 891 UINT fState) 892{ 893 MENUITEMINFOW mii; 894 WCHAR wszText[100]; 895 896 ZeroMemory(&mii, sizeof(mii)); 897 mii.cbSize = sizeof(mii); 898 if (fType == MFT_SEPARATOR) 899 mii.fMask = MIIM_ID | MIIM_TYPE; 900 else if (fType == MFT_STRING) 901 { 902 mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE; 903 if (IS_INTRESOURCE(dwTypeData)) 904 { 905 if (LoadStringW(shell32_hInstance, LOWORD((ULONG_PTR)dwTypeData), wszText, _countof(wszText))) 906 mii.dwTypeData = wszText; 907 else 908 { 909 ERR("failed to load string %p\n", dwTypeData); 910 return FALSE; 911 } 912 } 913 else 914 mii.dwTypeData = (LPWSTR)dwTypeData; 915 mii.fState = fState; 916 } 917 918 mii.wID = wID; 919 mii.fType = fType; 920 return InsertMenuItemW(hMenu, indexMenu, fByPosition, &mii); 921} 922 923void 924CDefaultContextMenu::TryPickDefault(HMENU hMenu, UINT idCmdFirst, UINT DfltOffset, UINT uFlags) 925{ 926 // Are we allowed to pick a default? 927 if ((uFlags & CMF_NODEFAULT) || 928 ((uFlags & CMF_DONOTPICKDEFAULT) && RosGetProcessEffectiveVersion() >= _WIN32_WINNT_WIN7)) 929 { 930 return; 931 } 932 933 // Do we already have a default? 934 if ((int)GetMenuDefaultItem(hMenu, MF_BYPOSITION, 0) != -1) 935 return; 936 937 // Does the view want to pick one? 938 INT_PTR forceDfm = 0; 939 if (SUCCEEDED(_DoCallback(DFM_GETDEFSTATICID, 0, &forceDfm)) && forceDfm) 940 { 941 for (UINT i = 0; i < _countof(g_StaticInvokeCmdMap); ++i) 942 { 943 UINT menuItemId = g_StaticInvokeCmdMap[i].IntVerb + DfltOffset - DCM_FCIDM_SHVIEW_OFFSET; 944 if (g_StaticInvokeCmdMap[i].DfmCmd == forceDfm && 945 SetMenuDefaultItem(hMenu, menuItemId, MF_BYCOMMAND)) 946 { 947 return; 948 } 949 } 950 } 951 952 // Don't want to pick something like cut or delete as the default but 953 // a static or dynamic verb is a good default. 954 if (m_iIdSCMLast > m_iIdSCMFirst || m_iIdSHELast > m_iIdSHEFirst) 955 SetMenuDefaultItem(hMenu, idCmdFirst, MF_BYCOMMAND); 956} 957 958HRESULT 959WINAPI 960CDefaultContextMenu::QueryContextMenu( 961 HMENU hMenu, 962 UINT IndexMenu, 963 UINT idCmdFirst, 964 UINT idCmdLast, 965 UINT uFlags) 966{ 967 HRESULT hr; 968 UINT idCmdNext = m_iIdQCMFirst = idCmdFirst; 969 UINT cIds = 0; 970 971 TRACE("BuildShellItemContextMenu entered\n"); 972 973 /* Load static verbs and shell extensions from registry */ 974 for (UINT i = 0; i < m_cKeys && !(uFlags & CMF_NOVERBS); i++) 975 { 976 AddStaticEntriesForKey(m_aKeys[i], uFlags); 977 EnumerateDynamicContextHandlerForKey(m_aKeys[i]); 978 } 979 980 /* Add static context menu handlers */ 981 cIds = AddStaticContextMenusToMenu(hMenu, &IndexMenu, idCmdFirst, idCmdLast, uFlags); 982 m_iIdSCMFirst = 0; // FIXME: This should be = idCmdFirst? 983 m_iIdSCMLast = cIds; 984 idCmdNext = idCmdFirst + cIds; 985 986 /* Add dynamic context menu handlers */ 987 cIds += AddShellExtensionsToMenu(hMenu, &IndexMenu, idCmdNext, idCmdLast, uFlags); 988 m_iIdSHEFirst = m_iIdSCMLast; 989 m_iIdSHELast = cIds; 990 idCmdNext = idCmdFirst + cIds; 991 TRACE("SH_LoadContextMenuHandlers first %x last %x\n", m_iIdSHEFirst, m_iIdSHELast); 992 993 /* Now let the callback add its own items */ 994 QCMINFO qcminfo = {hMenu, IndexMenu, idCmdNext, idCmdLast, NULL}; 995 if (SUCCEEDED(_DoCallback(DFM_MERGECONTEXTMENU, uFlags, &qcminfo))) 996 { 997 UINT added = qcminfo.idCmdFirst - idCmdNext; 998 cIds += added; 999 IndexMenu += added; 1000 m_iIdCBFirst = m_iIdSHELast; 1001 m_iIdCBLast = cIds; 1002 idCmdNext = idCmdFirst + cIds; 1003 } 1004 1005 //TODO: DFM_MERGECONTEXTMENU_BOTTOM 1006 1007 UINT idDefaultOffset = 0; 1008 BOOL isBackgroundMenu = !m_cidl; 1009 if (!(uFlags & CMF_VERBSONLY) && !isBackgroundMenu) 1010 { 1011 /* Get the attributes of the items */ 1012 SFGAOF rfg = SFGAO_BROWSABLE | SFGAO_CANCOPY | SFGAO_CANLINK | SFGAO_CANMOVE | SFGAO_CANDELETE | SFGAO_CANRENAME | SFGAO_HASPROPSHEET | SFGAO_FILESYSTEM | SFGAO_FOLDER; 1013 hr = m_psf->GetAttributesOf(m_cidl, m_apidl, &rfg); 1014 if (FAILED_UNEXPECTEDLY(hr)) 1015 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, cIds); 1016 1017 /* Add the default part of the menu */ 1018 HMENU hmenuDefault = LoadMenuW(_AtlBaseModule.GetResourceInstance(), MAKEINTRESOURCEW(MENU_SHV_FILE)); 1019 1020 /* Remove uneeded entries */ 1021 if (!(rfg & SFGAO_CANMOVE)) 1022 DeleteMenu(hmenuDefault, IDM_CUT, MF_BYCOMMAND); 1023 if (!(rfg & SFGAO_CANCOPY)) 1024 DeleteMenu(hmenuDefault, IDM_COPY, MF_BYCOMMAND); 1025 if (!((rfg & SFGAO_FILESYSTEM) && HasClipboardData())) 1026 DeleteMenu(hmenuDefault, IDM_INSERT, MF_BYCOMMAND); 1027 if (!(rfg & SFGAO_CANLINK)) 1028 DeleteMenu(hmenuDefault, IDM_CREATELINK, MF_BYCOMMAND); 1029 if (!(rfg & SFGAO_CANDELETE)) 1030 DeleteMenu(hmenuDefault, IDM_DELETE, MF_BYCOMMAND); 1031 if (!(rfg & SFGAO_CANRENAME) || !(uFlags & CMF_CANRENAME)) 1032 DeleteMenu(hmenuDefault, IDM_RENAME, MF_BYCOMMAND); 1033 if (!(rfg & SFGAO_HASPROPSHEET)) 1034 DeleteMenu(hmenuDefault, IDM_PROPERTIES, MF_BYCOMMAND); 1035 1036 idDefaultOffset = idCmdNext; 1037 UINT idMax = Shell_MergeMenus(hMenu, GetSubMenu(hmenuDefault, 0), IndexMenu, idCmdNext, idCmdLast, 0); 1038 m_iIdDfltFirst = cIds; 1039 cIds += idMax - idCmdNext; 1040 m_iIdDfltLast = cIds; 1041 1042 DestroyMenu(hmenuDefault); 1043 } 1044 1045 TryPickDefault(hMenu, idCmdFirst, idDefaultOffset, uFlags); 1046 1047 // TODO: DFM_MERGECONTEXTMENU_TOP 1048 1049 // TODO: Remove duplicate verbs. This will be easier when the static items handling 1050 // has been moved to CLSID_ShellFileDefExt so we only have to deal with ShellEx. 1051 // This is a Windows XP+ feature. On an unknown file type, Windows 2000 will 1052 // display both "Open" (openas from Unknown) and "Open with..." (openas from *). 1053 1054 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, cIds); 1055} 1056 1057HRESULT CDefaultContextMenu::DoPaste(LPCMINVOKECOMMANDINFOEX lpcmi, BOOL bLink) 1058{ 1059 HRESULT hr = _DoInvokeCommandCallback(lpcmi, DFM_CMD_PASTE); 1060 if (hr == S_OK) 1061 return hr; 1062 1063 CComPtr<IDataObject> pda; 1064 hr = OleGetClipboard(&pda); 1065 if (FAILED_UNEXPECTEDLY(hr)) 1066 return hr; 1067 1068 FORMATETC formatetc2; 1069 STGMEDIUM medium2; 1070 InitFormatEtc(formatetc2, RegisterClipboardFormatW(CFSTR_PREFERREDDROPEFFECT), TYMED_HGLOBAL); 1071 1072 DWORD dwKey= 0; 1073 1074 if (SUCCEEDED(pda->GetData(&formatetc2, &medium2))) 1075 { 1076 DWORD * pdwFlag = (DWORD*)GlobalLock(medium2.hGlobal); 1077 if (pdwFlag) 1078 { 1079 if (*pdwFlag == DROPEFFECT_COPY) 1080 dwKey = MK_CONTROL; 1081 else 1082 dwKey = MK_SHIFT; 1083 } 1084 else 1085 { 1086 ERR("No drop effect obtained\n"); 1087 } 1088 GlobalUnlock(medium2.hGlobal); 1089 } 1090 1091 if (bLink) 1092 { 1093 dwKey = MK_CONTROL|MK_SHIFT; 1094 } 1095 1096 CComPtr<IDropTarget> pdrop; 1097 if (m_cidl) 1098 hr = m_psf->GetUIObjectOf(NULL, 1, &m_apidl[0], IID_NULL_PPV_ARG(IDropTarget, &pdrop)); 1099 else 1100 hr = m_psf->CreateViewObject(NULL, IID_PPV_ARG(IDropTarget, &pdrop)); 1101 1102 if (FAILED_UNEXPECTEDLY(hr)) 1103 return hr; 1104 1105 SHSimulateDrop(pdrop, pda, dwKey, NULL, NULL); 1106 1107 TRACE("CP result %x\n", hr); 1108 return S_OK; 1109} 1110 1111HRESULT 1112CDefaultContextMenu::DoOpenOrExplore(LPCMINVOKECOMMANDINFOEX lpcmi) 1113{ 1114 UNIMPLEMENTED; 1115 return E_FAIL; 1116} 1117 1118HRESULT CDefaultContextMenu::DoCreateLink(LPCMINVOKECOMMANDINFOEX lpcmi) 1119{ 1120 HRESULT hr = _DoInvokeCommandCallback(lpcmi, DFM_CMD_LINK); 1121 if (hr == S_OK) 1122 return hr; 1123 1124 if (!m_cidl || !m_pDataObj) 1125 return E_FAIL; 1126 1127 CComPtr<IDropTarget> pDT; 1128 hr = m_psf->CreateViewObject(NULL, IID_PPV_ARG(IDropTarget, &pDT)); 1129 if (FAILED_UNEXPECTEDLY(hr)) 1130 return hr; 1131 1132 SHSimulateDrop(pDT, m_pDataObj, MK_CONTROL|MK_SHIFT, NULL, NULL); 1133 1134 return S_OK; 1135} 1136 1137HRESULT CDefaultContextMenu::DoDelete(LPCMINVOKECOMMANDINFOEX lpcmi) 1138{ 1139 HRESULT hr = _DoInvokeCommandCallback(lpcmi, DFM_CMD_DELETE); 1140 if (hr == S_OK) 1141 return hr; 1142 1143 if (!m_cidl || !m_pDataObj) 1144 return E_FAIL; 1145 1146 CComPtr<IDropTarget> pDT; 1147 hr = CRecyclerDropTarget_CreateInstance(IID_PPV_ARG(IDropTarget, &pDT)); 1148 if (FAILED_UNEXPECTEDLY(hr)) 1149 return hr; 1150 1151 DWORD grfKeyState = (lpcmi->fMask & CMIC_MASK_SHIFT_DOWN) ? MK_SHIFT : 0; 1152 SHSimulateDrop(pDT, m_pDataObj, grfKeyState, NULL, NULL); 1153 1154 return S_OK; 1155} 1156 1157HRESULT CDefaultContextMenu::DoCopyOrCut(LPCMINVOKECOMMANDINFOEX lpcmi, BOOL bCopy) 1158{ 1159 if (!m_cidl || !m_pDataObj) 1160 return E_FAIL; 1161 1162 HRESULT hr = _DoInvokeCommandCallback(lpcmi, bCopy ? DFM_CMD_COPY : DFM_CMD_MOVE); 1163 if (hr == S_OK) 1164 return hr; 1165 1166 FORMATETC formatetc; 1167 InitFormatEtc(formatetc, RegisterClipboardFormatW(CFSTR_PREFERREDDROPEFFECT), TYMED_HGLOBAL); 1168 STGMEDIUM medium = {0}; 1169 medium.tymed = TYMED_HGLOBAL; 1170 medium.hGlobal = GlobalAlloc(GHND, sizeof(DWORD)); 1171 DWORD* pdwFlag = (DWORD*)GlobalLock(medium.hGlobal); 1172 if (pdwFlag) 1173 *pdwFlag = bCopy ? DROPEFFECT_COPY : DROPEFFECT_MOVE; 1174 GlobalUnlock(medium.hGlobal); 1175 m_pDataObj->SetData(&formatetc, &medium, TRUE); 1176 1177 CComPtr<IShellFolderView> psfv; 1178 if (SUCCEEDED(IUnknown_QueryService(m_site, SID_SFolderView, IID_PPV_ARG(IShellFolderView, &psfv)))) 1179 psfv->SetPoints(m_pDataObj); 1180 1181 hr = OleSetClipboard(m_pDataObj); 1182 if (FAILED_UNEXPECTEDLY(hr)) 1183 return hr; 1184 1185 if (psfv) 1186 psfv->SetClipboard(!bCopy); 1187 return S_OK; 1188} 1189 1190HRESULT CDefaultContextMenu::DoRename(LPCMINVOKECOMMANDINFOEX lpcmi) 1191{ 1192 CComPtr<IShellBrowser> psb; 1193 HRESULT hr; 1194 1195 hr = _DoInvokeCommandCallback(lpcmi, DFM_CMD_RENAME); 1196 if (hr == S_OK) 1197 return hr; 1198 1199 if (!m_site || !m_cidl) 1200 return E_FAIL; 1201 1202 /* Get a pointer to the shell browser */ 1203 hr = IUnknown_QueryService(m_site, SID_IShellBrowser, IID_PPV_ARG(IShellBrowser, &psb)); 1204 if (FAILED_UNEXPECTEDLY(hr)) 1205 return hr; 1206 1207 CComPtr<IShellView> lpSV; 1208 hr = psb->QueryActiveShellView(&lpSV); 1209 if (FAILED_UNEXPECTEDLY(hr)) 1210 return hr; 1211 1212 SVSIF selFlags = SVSI_DESELECTOTHERS | SVSI_EDIT | SVSI_ENSUREVISIBLE | SVSI_FOCUSED | SVSI_SELECT; 1213 hr = lpSV->SelectItem(m_apidl[0], selFlags); 1214 if (FAILED_UNEXPECTEDLY(hr)) 1215 return hr; 1216 1217 return S_OK; 1218} 1219 1220HRESULT 1221CDefaultContextMenu::DoProperties( 1222 LPCMINVOKECOMMANDINFOEX lpcmi) 1223{ 1224 HRESULT hr = _DoInvokeCommandCallback(lpcmi, DFM_CMD_PROPERTIES); 1225 1226 // We are asked to run the default property sheet 1227 if (hr == S_FALSE) 1228 { 1229 return SHELL32_ShowPropertiesDialog(m_pDataObj); 1230 } 1231 1232 return hr; 1233} 1234 1235HRESULT 1236CDefaultContextMenu::DoUndo(LPCMINVOKECOMMANDINFOEX lpcmi) 1237{ 1238 ERR("TODO: Undo\n"); 1239 return E_NOTIMPL; 1240} 1241 1242HRESULT 1243CDefaultContextMenu::DoCopyToMoveToFolder(LPCMINVOKECOMMANDINFOEX lpici, BOOL bCopy) 1244{ 1245 HRESULT hr = E_FAIL; 1246 if (!m_pDataObj) 1247 { 1248 ERR("m_pDataObj is NULL\n"); 1249 return hr; 1250 } 1251 1252 CComPtr<IContextMenu> pContextMenu; 1253 if (bCopy) 1254 hr = SHCoCreateInstance(NULL, &CLSID_CopyToMenu, NULL, 1255 IID_PPV_ARG(IContextMenu, &pContextMenu)); 1256 else 1257 hr = SHCoCreateInstance(NULL, &CLSID_MoveToMenu, NULL, 1258 IID_PPV_ARG(IContextMenu, &pContextMenu)); 1259 if (FAILED_UNEXPECTEDLY(hr)) 1260 return hr; 1261 1262 CComPtr<IShellExtInit> pInit; 1263 hr = pContextMenu->QueryInterface(IID_PPV_ARG(IShellExtInit, &pInit)); 1264 if (FAILED_UNEXPECTEDLY(hr)) 1265 return hr; 1266 1267 hr = pInit->Initialize(m_pidlFolder, m_pDataObj, NULL); 1268 if (FAILED_UNEXPECTEDLY(hr)) 1269 return hr; 1270 1271 if (bCopy) 1272 lpici->lpVerb = "copyto"; 1273 else 1274 lpici->lpVerb = "moveto"; 1275 1276 return pContextMenu->InvokeCommand((LPCMINVOKECOMMANDINFO)lpici); 1277} 1278 1279// This code is taken from CNewMenu and should be shared between the 2 classes 1280HRESULT 1281CDefaultContextMenu::DoCreateNewFolder( 1282 LPCMINVOKECOMMANDINFOEX lpici) 1283{ 1284 WCHAR wszPath[MAX_PATH]; 1285 WCHAR wszName[MAX_PATH]; 1286 WCHAR wszNewFolder[25]; 1287 HRESULT hr; 1288 1289 /* Get folder path */ 1290 hr = SHGetPathFromIDListW(m_pidlFolder, wszPath); 1291 if (FAILED_UNEXPECTEDLY(hr)) 1292 return hr; 1293 1294 if (!LoadStringW(shell32_hInstance, IDS_NEWFOLDER, wszNewFolder, _countof(wszNewFolder))) 1295 return E_FAIL; 1296 1297 /* Create the name of the new directory */ 1298 if (!PathYetAnotherMakeUniqueName(wszName, wszPath, NULL, wszNewFolder)) 1299 return E_FAIL; 1300 1301 /* Create the new directory and show the appropriate dialog in case of error */ 1302 if (SHCreateDirectory(lpici->hwnd, wszName) != ERROR_SUCCESS) 1303 return E_FAIL; 1304 1305 /* Show and select the new item in the def view */ 1306 LPITEMIDLIST pidl; 1307 PITEMID_CHILD pidlNewItem; 1308 CComPtr<IShellView> psv; 1309 1310 /* Notify the view object about the new item */ 1311 SHChangeNotify(SHCNE_MKDIR, SHCNF_PATHW | SHCNF_FLUSH, (LPCVOID)wszName, NULL); 1312 1313 if (!m_site) 1314 return S_OK; 1315 1316 /* Get a pointer to the shell view */ 1317 hr = IUnknown_QueryService(m_site, SID_SFolderView, IID_PPV_ARG(IShellView, &psv)); 1318 if (FAILED_UNEXPECTEDLY(hr)) 1319 return S_OK; 1320 1321 /* Attempt to get the pidl of the new item */ 1322 hr = SHILCreateFromPathW(wszName, &pidl, NULL); 1323 if (FAILED_UNEXPECTEDLY(hr)) 1324 return hr; 1325 1326 pidlNewItem = ILFindLastID(pidl); 1327 1328 hr = psv->SelectItem(pidlNewItem, SVSI_DESELECTOTHERS | SVSI_EDIT | SVSI_ENSUREVISIBLE | 1329 SVSI_FOCUSED | SVSI_SELECT); 1330 if (FAILED_UNEXPECTEDLY(hr)) 1331 return hr; 1332 1333 SHFree(pidl); 1334 1335 return S_OK; 1336} 1337 1338PDynamicShellEntry CDefaultContextMenu::GetDynamicEntry(UINT idCmd) 1339{ 1340 POSITION it = m_DynamicEntries.GetHeadPosition(); 1341 while (it != NULL) 1342 { 1343 DynamicShellEntry& info = m_DynamicEntries.GetNext(it); 1344 1345 if (idCmd >= info.iIdCmdFirst + info.NumIds) 1346 continue; 1347 1348 if (idCmd < info.iIdCmdFirst || idCmd > info.iIdCmdFirst + info.NumIds) 1349 return NULL; 1350 1351 return &info; 1352 } 1353 1354 return NULL; 1355} 1356 1357BOOL 1358CDefaultContextMenu::MapVerbToCmdId(PVOID Verb, PUINT idCmd, BOOL IsUnicode) 1359{ 1360 WCHAR UnicodeStr[MAX_VERB]; 1361 1362 /* Loop through all the static verbs looking for a match */ 1363 for (UINT i = 0; i < _countof(g_StaticInvokeCmdMap); i++) 1364 { 1365 /* We can match both ANSI and unicode strings */ 1366 if (IsUnicode) 1367 { 1368 /* The static verbs are ANSI, get a unicode version before doing the compare */ 1369 SHAnsiToUnicode(g_StaticInvokeCmdMap[i].szStringVerb, UnicodeStr, MAX_VERB); 1370 if (!_wcsicmp(UnicodeStr, (LPWSTR)Verb)) 1371 { 1372 /* Return the Corresponding Id */ 1373 *idCmd = g_StaticInvokeCmdMap[i].IntVerb; 1374 return TRUE; 1375 } 1376 } 1377 else 1378 { 1379 if (!_stricmp(g_StaticInvokeCmdMap[i].szStringVerb, (LPSTR)Verb)) 1380 { 1381 *idCmd = g_StaticInvokeCmdMap[i].IntVerb; 1382 return TRUE; 1383 } 1384 } 1385 } 1386 1387 for (POSITION it = m_DynamicEntries.GetHeadPosition(); it != NULL;) 1388 { 1389 DynamicShellEntry& entry = m_DynamicEntries.GetNext(it); 1390 if (!entry.NumIds) 1391 continue; 1392 HRESULT hr = ::MapVerbToCmdId(Verb, IsUnicode, entry.pCM, 0, entry.NumIds - 1); 1393 if (SUCCEEDED(hr)) 1394 { 1395 *idCmd = m_iIdSHEFirst + entry.iIdCmdFirst + hr; 1396 return TRUE; 1397 } 1398 } 1399 return FALSE; 1400} 1401 1402HRESULT 1403CDefaultContextMenu::InvokeShellExt( 1404 LPCMINVOKECOMMANDINFOEX lpcmi) 1405{ 1406 TRACE("verb %p first %x last %x\n", lpcmi->lpVerb, m_iIdSHEFirst, m_iIdSHELast); 1407 1408 UINT idCmd = LOWORD(lpcmi->lpVerb); 1409 PDynamicShellEntry pEntry = GetDynamicEntry(idCmd); 1410 if (!pEntry) 1411 return E_FAIL; 1412 1413 /* invoke the dynamic context menu */ 1414 lpcmi->lpVerb = MAKEINTRESOURCEA(idCmd - pEntry->iIdCmdFirst); 1415 return pEntry->pCM->InvokeCommand((LPCMINVOKECOMMANDINFO)lpcmi); 1416} 1417 1418DWORD 1419CDefaultContextMenu::BrowserFlagsFromVerb(LPCMINVOKECOMMANDINFOEX lpcmi, PStaticShellEntry pEntry) 1420{ 1421 CComPtr<IShellBrowser> psb; 1422 HWND hwndTree; 1423 LPCWSTR FlagsName; 1424 WCHAR wszKey[sizeof("shell\\") + MAX_VERB]; 1425 HRESULT hr; 1426 1427 if (!m_site) 1428 return 0; 1429 1430 /* Get a pointer to the shell browser */ 1431 hr = IUnknown_QueryService(m_site, SID_IShellBrowser, IID_PPV_ARG(IShellBrowser, &psb)); 1432 if (FAILED(hr)) 1433 return 0; 1434 1435 /* See if we are in Explore or Browse mode. If the browser's tree is present, we are in Explore mode.*/ 1436 if (SUCCEEDED(psb->GetControlWindow(FCW_TREE, &hwndTree)) && hwndTree) 1437 FlagsName = L"ExplorerFlags"; 1438 else 1439 FlagsName = L"BrowserFlags"; 1440 1441 CComPtr<ICommDlgBrowser> pcdb; 1442 if (SUCCEEDED(psb->QueryInterface(IID_PPV_ARG(ICommDlgBrowser, &pcdb)))) 1443 { 1444 if (LOBYTE(GetVersion()) < 6 || FlagsName[0] == 'E') 1445 return 0; // Don't browse in-place 1446 } 1447 1448 /* Try to get the flag from the verb */ 1449 hr = StringCbPrintfW(wszKey, sizeof(wszKey), L"shell\\%s", pEntry->Verb.GetString()); 1450 if (FAILED_UNEXPECTEDLY(hr)) 1451 return 0; 1452 return RegGetDword(pEntry->hkClass, wszKey, FlagsName, 0); 1453} 1454 1455HRESULT 1456CDefaultContextMenu::TryToBrowse( 1457 LPCMINVOKECOMMANDINFOEX lpcmi, LPCITEMIDLIST pidlChild, DWORD wFlags) 1458{ 1459 CComPtr<IShellBrowser> psb; 1460 HRESULT hr; 1461 1462 if (!m_site) 1463 return E_FAIL; 1464 1465 /* Get a pointer to the shell browser */ 1466 hr = IUnknown_QueryService(m_site, SID_IShellBrowser, IID_PPV_ARG(IShellBrowser, &psb)); 1467 if (FAILED(hr)) 1468 return hr; 1469 1470 PIDLIST_ABSOLUTE pidl; 1471 hr = SHILCombine(m_pidlFolder, pidlChild, &pidl); 1472 if (FAILED_UNEXPECTEDLY(hr)) 1473 return hr; 1474 1475 hr = psb->BrowseObject(pidl, wFlags & ~SBSP_RELATIVE); 1476 ILFree(pidl); 1477 return hr; 1478} 1479 1480HRESULT 1481CDefaultContextMenu::InvokePidl(LPCMINVOKECOMMANDINFOEX lpcmi, LPCITEMIDLIST pidl, PStaticShellEntry pEntry) 1482{ 1483 const BOOL unicode = IsUnicode(*lpcmi); 1484 1485 LPITEMIDLIST pidlFull = ILCombine(m_pidlFolder, pidl); 1486 if (pidlFull == NULL) 1487 { 1488 return E_FAIL; 1489 } 1490 1491 WCHAR wszPath[MAX_PATH]; 1492 BOOL bHasPath = SHGetPathFromIDListW(pidlFull, wszPath); 1493 1494 WCHAR wszDir[MAX_PATH]; 1495 1496 SHELLEXECUTEINFOW sei = { sizeof(sei) }; 1497 sei.fMask = SEE_MASK_CLASSKEY | SEE_MASK_IDLIST | (CmicFlagsToSeeFlags(lpcmi->fMask) & ~SEE_MASK_INVOKEIDLIST); 1498 sei.hwnd = lpcmi->hwnd; 1499 sei.nShow = lpcmi->nShow; 1500 sei.lpVerb = pEntry->Verb; 1501 sei.lpIDList = pidlFull; 1502 sei.hkeyClass = pEntry->hkClass; 1503 sei.dwHotKey = lpcmi->dwHotKey; 1504 sei.hIcon = lpcmi->hIcon; 1505 sei.lpDirectory = wszDir; 1506 1507 if (unicode && !StrIsNullOrEmpty(lpcmi->lpDirectoryW)) 1508 { 1509 sei.lpDirectory = lpcmi->lpDirectoryW; 1510 } 1511 else if (bHasPath) 1512 { 1513 wcscpy(wszDir, wszPath); 1514 PathRemoveFileSpec(wszDir); 1515 } 1516 else 1517 { 1518 if (!SHGetPathFromIDListW(m_pidlFolder, wszDir)) 1519 *wszDir = UNICODE_NULL; 1520 } 1521 1522 if (bHasPath) 1523 sei.lpFile = wszPath; 1524 1525 CComHeapPtr<WCHAR> pszParamsW; 1526 if (unicode && !StrIsNullOrEmpty(lpcmi->lpParametersW)) 1527 sei.lpParameters = lpcmi->lpParametersW; 1528 else if (!StrIsNullOrEmpty(lpcmi->lpParameters) && __SHCloneStrAtoW(&pszParamsW, lpcmi->lpParameters)) 1529 sei.lpParameters = pszParamsW; 1530 1531 if (!sei.lpClass && (lpcmi->fMask & (CMIC_MASK_HASLINKNAME | CMIC_MASK_HASTITLE)) && unicode) 1532 sei.lpClass = lpcmi->lpTitleW; // Forward .lnk path from CShellLink::DoOpen (for consrv STARTF_TITLEISLINKNAME) 1533 1534 HRESULT hr = ShellExecuteExW(&sei) ? S_OK : HResultFromWin32(GetLastError()); 1535 ILFree(pidlFull); 1536 return hr; 1537} 1538 1539HRESULT 1540CDefaultContextMenu::InvokeRegVerb( 1541 LPCMINVOKECOMMANDINFOEX lpcmi) 1542{ 1543 INT iCmd = LOWORD(lpcmi->lpVerb); 1544 HRESULT hr; 1545 UINT i; 1546 1547 POSITION it = m_StaticEntries.FindIndex(iCmd); 1548 1549 if (it == NULL) 1550 return E_INVALIDARG; 1551 1552 PStaticShellEntry pEntry = &m_StaticEntries.GetAt(it); 1553 1554 CRegKey VerbKey; 1555 WCHAR VerbKeyPath[sizeof("shell\\") + MAX_VERB]; 1556 hr = StringCbPrintfW(VerbKeyPath, sizeof(VerbKeyPath), L"shell\\%s", pEntry->Verb.GetString()); 1557 if (SUCCEEDED(hr) && m_pDataObj && 1558 VerbKey.Open(pEntry->hkClass, VerbKeyPath, KEY_READ) == ERROR_SUCCESS) 1559 { 1560 CLSID clsid; 1561 1562 DWORD KeyState = 0; 1563 if (lpcmi->fMask & CMIC_MASK_SHIFT_DOWN) 1564 KeyState |= MK_SHIFT; 1565 if (lpcmi->fMask & CMIC_MASK_CONTROL_DOWN) 1566 KeyState |= MK_CONTROL; 1567 1568 POINTL *pPtl = NULL; 1569 C_ASSERT(sizeof(POINT) == sizeof(POINTL)); 1570 if (lpcmi->fMask & CMIC_MASK_PTINVOKE) 1571 pPtl = (POINTL*)&lpcmi->ptInvoke; 1572 1573 CComPtr<IExecuteCommand> pEC; 1574 hr = SHELL_GetRegCLSID(VerbKey, L"command", L"DelegateExecute", clsid); 1575 if (SUCCEEDED(hr)) 1576 hr = CoCreateInstance(clsid, NULL, CLSCTX_ALL, IID_PPV_ARG(IExecuteCommand, &pEC)); 1577 if (SUCCEEDED(hr)) 1578 { 1579 CComPtr<IPropertyBag> pPB; 1580 SHCreatePropertyBagOnRegKey(VerbKey, NULL, STGM_READ, IID_PPV_ARG(IPropertyBag, &pPB)); 1581 return InvokeIExecuteCommandWithDataObject(pEC, pEntry->Verb.GetString(), pPB, m_pDataObj, 1582 lpcmi, static_cast<IContextMenu*>(this)); 1583 } 1584 1585 CComPtr<IDropTarget> pDT; 1586 hr = SHELL_GetRegCLSID(VerbKey, L"DropTarget", L"CLSID", clsid); 1587 if (SUCCEEDED(hr)) 1588 hr = CoCreateInstance(clsid, NULL, CLSCTX_ALL, IID_PPV_ARG(IDropTarget, &pDT)); 1589 if (SUCCEEDED(hr)) 1590 { 1591 CScopedSetObjectWithSite site(pDT, static_cast<IContextMenu*>(this)); 1592 CComPtr<IPropertyBag> pPB; 1593 SHCreatePropertyBagOnRegKey(VerbKey, NULL, STGM_READ, IID_PPV_ARG(IPropertyBag, &pPB)); 1594 IUnknown_InitializeCommand(pDT, pEntry->Verb.GetString(), pPB); 1595 hr = SHSimulateDrop(pDT, m_pDataObj, KeyState, pPtl, NULL); 1596 return hr; 1597 } 1598 } 1599 1600 /* Get the browse flags to see if we need to browse */ 1601 DWORD wFlags = BrowserFlagsFromVerb(lpcmi, pEntry); 1602 1603 for (i=0; i < m_cidl; i++) 1604 { 1605 /* Check if we need to browse */ 1606 if (wFlags) 1607 { 1608 hr = TryToBrowse(lpcmi, m_apidl[i], wFlags); 1609 if (SUCCEEDED(hr)) 1610 { 1611 /* In WinXP if we have browsed, we don't open any more folders. 1612 * In Win7 we browse to the first folder we find and 1613 * open new windows for each of the rest of the folders */ 1614 UINT ntver = RosGetProcessEffectiveVersion(); 1615 if (ntver >= _WIN32_WINNT_VISTA) 1616 wFlags = 0; // FIXME: = SBSP_NEWBROWSER | (wFlags & ~SBSP_SAMEBROWSER); 1617 else 1618 i = m_cidl; 1619 1620 continue; 1621 } 1622 } 1623 1624 InvokePidl(lpcmi, m_apidl[i], pEntry); 1625 } 1626 1627 return S_OK; 1628} 1629 1630HRESULT 1631CDefaultContextMenu::_DoInvokeCommandCallback( 1632 LPCMINVOKECOMMANDINFOEX lpcmi, WPARAM CmdId) 1633{ 1634 BOOL Unicode = IsUnicode(*lpcmi); 1635 WCHAR lParamBuf[MAX_PATH]; 1636 LPARAM lParam = 0; 1637 1638 if (Unicode && lpcmi->lpParametersW) 1639 lParam = (LPARAM)lpcmi->lpParametersW; 1640 else if (lpcmi->lpParameters) 1641 lParam = SHAnsiToUnicode(lpcmi->lpParameters, lParamBuf, _countof(lParamBuf)) ? (LPARAM)lParamBuf : 0; 1642 1643 HRESULT hr; 1644#if 0 // TODO: Try DFM_INVOKECOMMANDEX first. 1645 DFMICS dfmics = { sizeof(DFMICS), lpcmi->fMask, lParam, m_iIdSCMFirst?, m_iIdDfltLast?, (LPCMINVOKECOMMANDINFO)lpcmi, m_site }; 1646 hr = _DoCallback(DFM_INVOKECOMMANDEX, CmdId, &dfmics); 1647 if (hr == E_NOTIMPL) 1648#endif 1649 hr = _DoCallback(DFM_INVOKECOMMAND, CmdId, (void*)lParam); 1650 return hr; 1651} 1652 1653HRESULT 1654WINAPI 1655CDefaultContextMenu::InvokeCommand( 1656 LPCMINVOKECOMMANDINFO lpcmi) 1657{ 1658 CMINVOKECOMMANDINFOEX LocalInvokeInfo = {}; 1659 HRESULT Result; 1660 UINT CmdId; 1661 1662 /* Take a local copy of the fixed members of the 1663 struct as we might need to modify the verb */ 1664 memcpy(&LocalInvokeInfo, lpcmi, min(sizeof(LocalInvokeInfo), lpcmi->cbSize)); 1665 1666 /* Check if this is a string verb */ 1667 if (!IS_INTRESOURCE(LocalInvokeInfo.lpVerb)) 1668 { 1669 /* Get the ID which corresponds to this verb, and update our local copy */ 1670 if (MapVerbToCmdId((LPVOID)LocalInvokeInfo.lpVerb, &CmdId, FALSE)) 1671 LocalInvokeInfo.lpVerb = MAKEINTRESOURCEA(CmdId); 1672 else 1673 return E_INVALIDARG; 1674 } 1675 CmdId = LOWORD(LocalInvokeInfo.lpVerb); 1676 1677 if (!m_DynamicEntries.IsEmpty() && CmdId >= m_iIdSHEFirst && CmdId < m_iIdSHELast) 1678 { 1679 LocalInvokeInfo.lpVerb -= m_iIdSHEFirst; 1680 Result = InvokeShellExt(&LocalInvokeInfo); 1681 return Result; 1682 } 1683 1684 if (!m_StaticEntries.IsEmpty() && CmdId >= m_iIdSCMFirst && CmdId < m_iIdSCMLast) 1685 { 1686 LocalInvokeInfo.lpVerb -= m_iIdSCMFirst; 1687 Result = InvokeRegVerb(&LocalInvokeInfo); 1688 // TODO: if (FAILED(Result) && !(lpcmi->fMask & CMIC_MASK_FLAG_NO_UI)) SHELL_ErrorBox(m_pSite, Result); 1689 return Result; 1690 } 1691 1692 if (m_iIdCBFirst != m_iIdCBLast && CmdId >= m_iIdCBFirst && CmdId < m_iIdCBLast) 1693 { 1694 Result = _DoInvokeCommandCallback(&LocalInvokeInfo, CmdId - m_iIdCBFirst); 1695 return Result; 1696 } 1697 1698 if (m_iIdDfltFirst != m_iIdDfltLast && CmdId >= m_iIdDfltFirst && CmdId < m_iIdDfltLast) 1699 { 1700 CmdId -= m_iIdDfltFirst; 1701 /* See the definitions of IDM_CUT and co to see how this works */ 1702 CmdId += DCM_FCIDM_SHVIEW_OFFSET; 1703 } 1704 1705 if (LocalInvokeInfo.cbSize >= sizeof(CMINVOKECOMMANDINFOEX) && (LocalInvokeInfo.fMask & CMIC_MASK_PTINVOKE)) 1706 { 1707 if (m_pDataObj && FAILED_UNEXPECTEDLY(DataObject_SetOffset(m_pDataObj, &LocalInvokeInfo.ptInvoke))) 1708 { 1709 ERR("Unable to add OFFSET to DataObject!\n"); 1710 } 1711 } 1712 1713 /* Check if this is a Id */ 1714 switch (CmdId) 1715 { 1716 case FCIDM_SHVIEW_INSERT: 1717 Result = DoPaste(&LocalInvokeInfo, FALSE); 1718 break; 1719 case FCIDM_SHVIEW_INSERTLINK: 1720 Result = DoPaste(&LocalInvokeInfo, TRUE); 1721 break; 1722 case FCIDM_SHVIEW_OPEN: 1723 case FCIDM_SHVIEW_EXPLORE: 1724 Result = DoOpenOrExplore(&LocalInvokeInfo); 1725 break; 1726 case FCIDM_SHVIEW_COPY: 1727 case FCIDM_SHVIEW_CUT: 1728 Result = DoCopyOrCut(&LocalInvokeInfo, CmdId == FCIDM_SHVIEW_COPY); 1729 break; 1730 case FCIDM_SHVIEW_CREATELINK: 1731 Result = DoCreateLink(&LocalInvokeInfo); 1732 break; 1733 case FCIDM_SHVIEW_DELETE: 1734 Result = DoDelete(&LocalInvokeInfo); 1735 break; 1736 case FCIDM_SHVIEW_RENAME: 1737 Result = DoRename(&LocalInvokeInfo); 1738 break; 1739 case FCIDM_SHVIEW_PROPERTIES: 1740 Result = DoProperties(&LocalInvokeInfo); 1741 break; 1742 case FCIDM_SHVIEW_NEWFOLDER: 1743 Result = DoCreateNewFolder(&LocalInvokeInfo); 1744 break; 1745 case FCIDM_SHVIEW_COPYTO: 1746 Result = DoCopyToMoveToFolder(&LocalInvokeInfo, TRUE); 1747 break; 1748 case FCIDM_SHVIEW_MOVETO: 1749 Result = DoCopyToMoveToFolder(&LocalInvokeInfo, FALSE); 1750 break; 1751 case FCIDM_SHVIEW_UNDO: 1752 Result = DoUndo(&LocalInvokeInfo); 1753 break; 1754 default: 1755 Result = E_INVALIDARG; 1756 ERR("Unhandled Verb %xl\n", LOWORD(LocalInvokeInfo.lpVerb)); 1757 break; 1758 } 1759 1760 return Result; 1761} 1762 1763HRESULT 1764WINAPI 1765CDefaultContextMenu::GetCommandString( 1766 UINT_PTR idCommand, 1767 UINT uFlags, 1768 UINT* lpReserved, 1769 LPSTR lpszName, 1770 UINT uMaxNameLen) 1771{ 1772 /* We don't handle the help text yet */ 1773 if (uFlags == GCS_HELPTEXTA || 1774 uFlags == GCS_HELPTEXTW || 1775 HIWORD(idCommand) != 0) 1776 { 1777 return E_NOTIMPL; 1778 } 1779 1780 UINT CmdId = LOWORD(idCommand); 1781 1782 if (uFlags == GCS_VERBA || uFlags == GCS_VERBW) 1783 { 1784 UINT uMsg = (uFlags == GCS_VERBA) ? DFM_GETVERBA : DFM_GETVERBW; 1785 WPARAM wParam = MAKEWPARAM(idCommand, uMaxNameLen); 1786 HRESULT hr = _DoCallback(uMsg, wParam, lpszName); 1787 if (hr == S_OK) 1788 return S_OK; 1789 } 1790 1791 if (!m_DynamicEntries.IsEmpty() && CmdId >= m_iIdSHEFirst && CmdId < m_iIdSHELast) 1792 { 1793 idCommand -= m_iIdSHEFirst; 1794 PDynamicShellEntry pEntry = GetDynamicEntry(idCommand); 1795 if (!pEntry) 1796 return E_FAIL; 1797 1798 idCommand -= pEntry->iIdCmdFirst; 1799 return pEntry->pCM->GetCommandString(idCommand, 1800 uFlags, 1801 lpReserved, 1802 lpszName, 1803 uMaxNameLen); 1804 } 1805 1806 if (!m_StaticEntries.IsEmpty() && CmdId >= m_iIdSCMFirst && CmdId < m_iIdSCMLast) 1807 { 1808 /* Validation just returns S_OK on a match. The id exists. */ 1809 if (uFlags == GCS_VALIDATEA || uFlags == GCS_VALIDATEW) 1810 return S_OK; 1811 1812 CmdId -= m_iIdSCMFirst; 1813 1814 POSITION it = m_StaticEntries.FindIndex(CmdId); 1815 1816 if (it == NULL) 1817 return E_INVALIDARG; 1818 1819 PStaticShellEntry pEntry = &m_StaticEntries.GetAt(it); 1820 1821 if (uFlags == GCS_VERBW) 1822 return StringCchCopyW((LPWSTR)lpszName, uMaxNameLen, pEntry->Verb); 1823 1824 if (uFlags == GCS_VERBA) 1825 { 1826 if (SHUnicodeToAnsi(pEntry->Verb, lpszName, uMaxNameLen)) 1827 return S_OK; 1828 } 1829 1830 return E_INVALIDARG; 1831 } 1832 1833 //FIXME: Should we handle callbacks here? 1834 if (m_iIdDfltFirst != m_iIdDfltLast && CmdId >= m_iIdDfltFirst && CmdId < m_iIdDfltLast) 1835 { 1836 CmdId -= m_iIdDfltFirst; 1837 /* See the definitions of IDM_CUT and co to see how this works */ 1838 CmdId += DCM_FCIDM_SHVIEW_OFFSET; 1839 } 1840 1841 /* Loop looking for a matching Id */ 1842 for (UINT i = 0; i < _countof(g_StaticInvokeCmdMap); i++) 1843 { 1844 if (g_StaticInvokeCmdMap[i].IntVerb == CmdId) 1845 { 1846 /* Validation just returns S_OK on a match */ 1847 if (uFlags == GCS_VALIDATEA || uFlags == GCS_VALIDATEW) 1848 return S_OK; 1849 1850 /* Return a copy of the ANSI verb */ 1851 if (uFlags == GCS_VERBA) 1852 return StringCchCopyA(lpszName, uMaxNameLen, g_StaticInvokeCmdMap[i].szStringVerb); 1853 1854 /* Convert the ANSI verb to unicode and return that */ 1855 if (uFlags == GCS_VERBW) 1856 { 1857 if (SHAnsiToUnicode(g_StaticInvokeCmdMap[i].szStringVerb, (LPWSTR)lpszName, uMaxNameLen)) 1858 return S_OK; 1859 } 1860 } 1861 } 1862 1863 return E_INVALIDARG; 1864} 1865 1866HRESULT 1867WINAPI 1868CDefaultContextMenu::HandleMenuMsg( 1869 UINT uMsg, 1870 WPARAM wParam, 1871 LPARAM lParam) 1872{ 1873 return HandleMenuMsg2(uMsg, wParam, lParam, NULL); 1874} 1875 1876HRESULT SHGetMenuIdFromMenuMsg(UINT uMsg, LPARAM lParam, UINT *CmdId) 1877{ 1878 if (uMsg == WM_DRAWITEM) 1879 { 1880 DRAWITEMSTRUCT* pDrawStruct = reinterpret_cast<DRAWITEMSTRUCT*>(lParam); 1881 *CmdId = pDrawStruct->itemID; 1882 return S_OK; 1883 } 1884 else if (uMsg == WM_MEASUREITEM) 1885 { 1886 MEASUREITEMSTRUCT* pMeasureStruct = reinterpret_cast<MEASUREITEMSTRUCT*>(lParam); 1887 *CmdId = pMeasureStruct->itemID; 1888 return S_OK; 1889 } 1890 return E_FAIL; 1891} 1892 1893HRESULT 1894WINAPI 1895CDefaultContextMenu::HandleMenuMsg2( 1896 UINT uMsg, 1897 WPARAM wParam, 1898 LPARAM lParam, 1899 LRESULT *plResult) 1900{ 1901 if (!SHELL_IsContextMenuMsg(uMsg)) 1902 return E_FAIL; 1903 1904 UINT CmdId; 1905 if (uMsg == WM_INITMENUPOPUP) 1906 { 1907 CmdId = GetMenuItemID((HMENU)wParam, 0); 1908 if (CmdId == ~0ul) 1909 return E_FAIL; 1910 } 1911 else 1912 { 1913 HRESULT hr = SHGetMenuIdFromMenuMsg(uMsg, lParam, &CmdId); 1914 if (FAILED(hr)) 1915 return S_FALSE; 1916 } 1917 CmdId -= m_iIdQCMFirst; // Convert from Win32 id to our base 1918 1919 if (CmdId >= m_iIdSHEFirst && CmdId < m_iIdSHELast) 1920 { 1921 if (PDynamicShellEntry pEntry = GetDynamicEntry(CmdId - m_iIdSHEFirst)) 1922 return SHForwardContextMenuMsg(pEntry->pCM, uMsg, wParam, lParam, plResult, TRUE); 1923 } 1924 // TODO: _DoCallback(DFM_WM_*, ...) 1925 return E_FAIL; 1926} 1927 1928HRESULT 1929WINAPI 1930CDefaultContextMenu::SetSite(IUnknown *pUnkSite) 1931{ 1932 m_site = pUnkSite; 1933 return S_OK; 1934} 1935 1936HRESULT 1937WINAPI 1938CDefaultContextMenu::GetSite(REFIID riid, void **ppvSite) 1939{ 1940 if (!m_site) 1941 return E_FAIL; 1942 1943 return m_site->QueryInterface(riid, ppvSite); 1944} 1945 1946static 1947HRESULT 1948CDefaultContextMenu_CreateInstance(const DEFCONTEXTMENU *pdcm, LPFNDFMCALLBACK lpfn, REFIID riid, void **ppv) 1949{ 1950 return ShellObjectCreatorInit<CDefaultContextMenu>(pdcm, lpfn, riid, ppv); 1951} 1952 1953/************************************************************************* 1954 * SHCreateDefaultContextMenu [SHELL32.325] Vista API 1955 * 1956 */ 1957 1958HRESULT 1959WINAPI 1960SHCreateDefaultContextMenu(const DEFCONTEXTMENU *pdcm, REFIID riid, void **ppv) 1961{ 1962 HRESULT hr; 1963 1964 if (!ppv) 1965 return E_INVALIDARG; 1966 1967 hr = CDefaultContextMenu_CreateInstance(pdcm, NULL, riid, ppv); 1968 if (FAILED_UNEXPECTEDLY(hr)) 1969 return hr; 1970 1971 return S_OK; 1972} 1973 1974/************************************************************************* 1975 * CDefFolderMenu_Create2 [SHELL32.701] 1976 * 1977 */ 1978 1979HRESULT 1980WINAPI 1981CDefFolderMenu_Create2( 1982 PCIDLIST_ABSOLUTE pidlFolder, 1983 HWND hwnd, 1984 UINT cidl, 1985 PCUITEMID_CHILD_ARRAY apidl, 1986 IShellFolder *psf, 1987 LPFNDFMCALLBACK lpfn, 1988 UINT nKeys, 1989 const HKEY *ahkeyClsKeys, 1990 IContextMenu **ppcm) 1991{ 1992 DEFCONTEXTMENU dcm; 1993 dcm.hwnd = hwnd; 1994 dcm.pcmcb = NULL; 1995 dcm.pidlFolder = pidlFolder; 1996 dcm.psf = psf; 1997 dcm.cidl = cidl; 1998 dcm.apidl = apidl; 1999 dcm.punkAssociationInfo = NULL; 2000 dcm.cKeys = nKeys; 2001 dcm.aKeys = ahkeyClsKeys; 2002 2003 HRESULT hr = CDefaultContextMenu_CreateInstance(&dcm, lpfn, IID_PPV_ARG(IContextMenu, ppcm)); 2004 if (FAILED_UNEXPECTEDLY(hr)) 2005 return hr; 2006 2007 return S_OK; 2008}