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