Something went wrong. Try again.
Reactos
Something went wrong. Try again.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009/* * PROJECT: shell32 * LICENSE: GPL - See COPYING in the top level directory * FILE: dll/win32/shell32/shv_item_new.c * PURPOSE: provides default context menu implementation * PROGRAMMERS: Johannes Anderwald (johannes.anderwald@reactos.org) */
#include "precomp.h"#include <compat_undoc.h>
WINE_DEFAULT_DEBUG_CHANNEL(dmenu);
// FIXME: 260 is correct, but should this be part of the SDK or just MAX_PATH?#define MAX_VERB 260
static HRESULTSHELL_GetRegCLSID(HKEY hKey, LPCWSTR SubKey, LPCWSTR Value, CLSID &clsid){ WCHAR buf[42]; DWORD cb = sizeof(buf); DWORD err = RegGetValueW(hKey, SubKey, Value, RRF_RT_REG_SZ, NULL, buf, &cb); return !err ? CLSIDFromString(buf, &clsid) : HRESULT_FROM_WIN32(err);}
static BOOL InsertMenuItemAt(HMENU hMenu, UINT Pos, UINT Flags){ MENUITEMINFOW mii; mii.cbSize = FIELD_OFFSET(MENUITEMINFOW, hbmpItem); // USER32 version agnostic mii.fMask = MIIM_TYPE; mii.fType = Flags; return InsertMenuItemW(hMenu, Pos, TRUE, &mii);}
static void DCMA_DestroyEntry(DCMENTRY &dcme){ if (!dcme.pCM) return; IUnknown_SetSite(dcme.pCM, NULL); dcme.pCM->Release(); dcme.pCM = NULL;}
void DCMA_Destroy(HDCMA hDCMA){ UINT i = 0; for (DCMENTRY *p; (p = DCMA_GetEntry(hDCMA, i)) != NULL; ++i) DCMA_DestroyEntry(*p); DSA_Destroy(hDCMA);}
UINT DCMA_InsertMenuItems( _In_ HDCMA hDCMA, _In_ HDCIA hDCIA, _In_opt_ LPCITEMIDLIST pidlFolder, _In_opt_ IDataObject *pDO, _In_opt_ HKEY *pKeys, _In_opt_ UINT nKeys, _In_ QCMINFO *pQCMI, _In_opt_ UINT fCmf, _In_opt_ IUnknown *pUnkSite){ UINT idCmdBase = pQCMI->idCmdFirst, idCmdFirst = idCmdBase; UINT nOffset = 0;
// Insert in reverse order for (int iCls = DCIA_GetCount(hDCIA) - 1; iCls >= 0; --iCls) { REFCLSID clsid = *DCIA_GetEntry(hDCIA, iCls); if (fCmf & CMF_DEFAULTONLY) { WCHAR szKey[MAX_PATH]; wcscpy(szKey, L"CLSID\\"); StringFromGUID2(clsid, szKey + _countof(L"CLSID\\") - 1, CHARS_IN_GUID); wcscpy(szKey + _countof(L"CLSID\\") - 1 + CHARS_IN_GUID - 1, L"\\shellex\\MayChangeDefaultMenu"); if (!RegKeyExists(HKEY_CLASSES_ROOT, szKey)) continue; }
for (UINT iKey = 0; iKey < nKeys; ++iKey) { CComPtr<IShellExtInit> pInit; HRESULT hr = SHExtCoCreateInstance(NULL, &clsid, NULL, IID_PPV_ARG(IShellExtInit, &pInit)); if (FAILED(hr)) break; if (FAILED(hr = pInit->Initialize(pidlFolder, pDO, pKeys[iKey]))) continue;
IContextMenu *pCM; if (FAILED(hr = pInit->QueryInterface(IID_PPV_ARG(IContextMenu, &pCM)))) break; IUnknown_SetSite(pCM, pUnkSite);
hr = pCM->QueryContextMenu(pQCMI->hmenu, pQCMI->indexMenu + nOffset, idCmdFirst, pQCMI->idCmdLast, fCmf); const UINT nCount = HRESULT_CODE(hr); const UINT idThisFirst = idCmdFirst - idCmdBase; DCMENTRY dcme = { pCM, idThisFirst, idThisFirst + nCount - 1 }; if (hr > 0) { idCmdFirst += nCount; if (DSA_AppendItem(hDCMA, &dcme) >= 0) { if (nOffset == 0 && GetMenuDefaultItem(pQCMI->hmenu, TRUE, 0) == 0) nOffset++; // Insert new items below the default break; } } DCMA_DestroyEntry(dcme); } } return idCmdFirst;}
HRESULT DCMA_InvokeCommand(HDCMA hDCMA, CMINVOKECOMMANDINFO *pICI){ HRESULT hr = S_FALSE; for (UINT i = 0;; ++i) { DCMENTRY *p = DCMA_GetEntry(hDCMA, i); if (!p) return hr;
UINT id = LOWORD(pICI->lpVerb); if (!IS_INTRESOURCE(pICI->lpVerb)) { if (SUCCEEDED(hr = p->pCM->InvokeCommand(pICI))) return hr; } else if (id >= p->idCmdFirst && id <= p->idCmdLast) { CMINVOKECOMMANDINFOEX ici; CopyMemory(&ici, pICI, min(sizeof(ici), pICI->cbSize)); ici.cbSize = min(sizeof(ici), pICI->cbSize); ici.lpVerb = MAKEINTRESOURCEA(id - p->idCmdFirst); ici.lpVerbW = (PWSTR)ici.lpVerb; return p->pCM->InvokeCommand((CMINVOKECOMMANDINFO*)&ici); } }}
typedef struct _DynamicShellEntry_{ UINT iIdCmdFirst; UINT NumIds; CLSID ClassID; CComPtr<IContextMenu> pCM;} DynamicShellEntry, *PDynamicShellEntry;
typedef struct _StaticShellEntry_{ CStringW Verb; HKEY hkClass;} StaticShellEntry, *PStaticShellEntry;
#define DCM_FCIDM_SHVIEW_OFFSET 0x7000 // Offset from the menu ids in the menu resource to FCIDM_SHVIEW_*
//// verbs for InvokeCommandInfo//static const struct _StaticInvokeCommandMap_{ LPCSTR szStringVerb; WORD IntVerb; SHORT DfmCmd;} g_StaticInvokeCmdMap[] ={ { "runas", 0 }, // Unimplemented { "print", 0 }, // Unimplemented { "preview", 0 }, // Unimplemented { "open", FCIDM_SHVIEW_OPEN }, { CMDSTR_NEWFOLDERA, FCIDM_SHVIEW_NEWFOLDER, (SHORT)DFM_CMD_NEWFOLDER }, { "cut", FCIDM_SHVIEW_CUT, /* ? */ }, { "copy", FCIDM_SHVIEW_COPY, (SHORT)DFM_CMD_COPY }, { "paste", FCIDM_SHVIEW_INSERT, (SHORT)DFM_CMD_PASTE }, { "link", FCIDM_SHVIEW_CREATELINK, (SHORT)DFM_CMD_LINK }, { "delete", FCIDM_SHVIEW_DELETE, (SHORT)DFM_CMD_DELETE }, { "properties", FCIDM_SHVIEW_PROPERTIES, (SHORT)DFM_CMD_PROPERTIES }, { "rename", FCIDM_SHVIEW_RENAME, (SHORT)DFM_CMD_RENAME }, { "copyto", FCIDM_SHVIEW_COPYTO }, { "moveto", FCIDM_SHVIEW_MOVETO },};
PCSTR MapFcidmCmdToVerb(_In_ UINT_PTR CmdId){ for (SIZE_T i = 0; i < _countof(g_StaticInvokeCmdMap); ++i) { if (g_StaticInvokeCmdMap[i].IntVerb == CmdId && CmdId) return g_StaticInvokeCmdMap[i].szStringVerb; } return NULL;}
UINT MapVerbToDfmCmd(_In_ LPCSTR verba){ for (UINT i = 0; i < _countof(g_StaticInvokeCmdMap); ++i) { if (!lstrcmpiA(g_StaticInvokeCmdMap[i].szStringVerb, verba)) return (int)g_StaticInvokeCmdMap[i].DfmCmd; } return 0;}
static HRESULTMapVerbToCmdId(PVOID Verb, BOOL IsUnicode, IContextMenu *pCM, UINT idFirst, UINT idLast){ const UINT gcs = IsUnicode ? GCS_VERBW : GCS_VERBA; for (UINT id = idFirst; id <= idLast; ++id) { WCHAR buf[MAX_PATH]; *buf = UNICODE_NULL; HRESULT hr = pCM->GetCommandString(id, gcs, NULL, (LPSTR)buf, _countof(buf)); if (FAILED(hr) || !*buf) continue; else if (IsUnicode && !_wcsicmp((LPWSTR)Verb, buf)) return id; else if (!IsUnicode && !lstrcmpiA((LPSTR)Verb, (LPSTR)buf)) return id; } return HRESULT_FROM_WIN32(ERROR_NOT_FOUND);}
static inline bool IsVerbListSeparator(WCHAR Ch){ return Ch == L' ' || Ch == L','; // learn.microsoft.com/en-us/windows/win32/shell/context-menu-handlers}
static int FindVerbInDefaultVerbList(LPCWSTR List, LPCWSTR Verb){ for (UINT index = 0; *List; ++index) { while (IsVerbListSeparator(*List)) List++; LPCWSTR Start = List; while (*List && !IsVerbListSeparator(*List)) List++; // "List > Start" to verify that the list item is non-empty to avoid the edge case where Verb is "" and the list contains ",," if (!_wcsnicmp(Verb, Start, List - Start) && List > Start) return index; } return -1;}
EXTERN_C HRESULT SHELL32_EnumDefaultVerbList(LPCWSTR List, UINT Index, LPWSTR Verb, SIZE_T cchMax){ for (UINT i = 0; *List; ++i) { while (IsVerbListSeparator(*List)) List++; LPCWSTR Start = List; while (*List && !IsVerbListSeparator(*List)) List++; if (List > Start && i == Index) return StringCchCopyNW(Verb, cchMax, Start, List - Start); } return HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS);}
static HRESULT GetFriendlyVerb(_In_ PCWSTR pszVerb, _Out_ PWSTR pszBuf, _In_ SIZE_T cchMax){ static const struct { PCWSTR pszVerb; WORD iResId; } map[] = { // { L"open", IDS_OPEN_VERB }, These two have already been handled // { L"explore", IDS_EXPLORE_VERB }, { L"edit", IDS_EDIT_VERB }, { L"print", IDS_PRINT_VERB }, { L"runas", IDS_RUNAS_VERB }, { L"openas", IDS_OPEN_VERB }, { L"find", IDS_FIND_VERB }, }; for (SIZE_T i = 0; i < _countof(map); ++i) { if (!_wcsicmp(pszVerb, map[i].pszVerb) && LoadStringW(shell32_hInstance, map[i].iResId, pszBuf, cchMax)) { return S_OK; } }
// Try to make a friendly verb based on the verb subkey if (pszVerb[0] < 127 && !StrChrW(pszVerb, '&') && SUCCEEDED(StringCchCopyW(pszBuf + 1, --cchMax, pszVerb))) { *pszBuf = L'&'; return S_OK; // This can be changed to S_FALSE if the caller needs to know we faked it } return E_FAIL;}
class CDefaultContextMenu : public CComObjectRootEx<CComMultiThreadModelNoCS>, public IContextMenu3, public IObjectWithSite, public IServiceProvider{ private: CComPtr<IUnknown> m_site; CComPtr<IShellFolder> m_psf; CComPtr<IContextMenuCB> m_pmcb; LPFNDFMCALLBACK m_pfnmcb; UINT m_cidl; PCUITEMID_CHILD_ARRAY m_apidl; CComPtr<IDataObject> m_pDataObj; HKEY m_aKeys[16]; // This limit is documented for both the old API and for DEFCONTEXTMENU UINT m_cKeys; PIDLIST_ABSOLUTE m_pidlFolder; DWORD m_bGroupPolicyActive; UINT m_iIdQCMFirst; /* The first id passed to us in QueryContextMenu */ CAtlList<DynamicShellEntry> m_DynamicEntries; UINT m_iIdSHEFirst; /* first used id */ UINT m_iIdSHELast; /* last used id */ CAtlList<StaticShellEntry> m_StaticEntries; UINT m_iIdSCMFirst; /* first static used id */ UINT m_iIdSCMLast; /* last static used id */ UINT m_iIdCBFirst; /* first callback used id */ UINT m_iIdCBLast; /* last callback used id */ UINT m_iIdDfltFirst; /* first default part id */ UINT m_iIdDfltLast; /* last default part id */ HWND m_hwnd; /* window passed to callback */ WCHAR m_DefVerbs[MAX_PATH];
HRESULT _DoCallback(UINT uMsg, WPARAM wParam, LPVOID lParam); HRESULT _DoInvokeCommandCallback(LPCMINVOKECOMMANDINFOEX lpcmi, WPARAM CmdId); void AddStaticEntry(const HKEY hkeyClass, const WCHAR *szVerb, UINT uFlags); void AddStaticEntriesForKey(HKEY hKey, UINT uFlags); void TryPickDefault(HMENU hMenu, UINT idCmdFirst, UINT DfltOffset, UINT uFlags); BOOL IsShellExtensionAlreadyLoaded(REFCLSID clsid); HRESULT LoadDynamicContextMenuHandler(HKEY hKey, REFCLSID clsid); BOOL EnumerateDynamicContextHandlerForKey(HKEY hRootKey); UINT AddShellExtensionsToMenu(HMENU hMenu, UINT* pIndexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags); UINT AddStaticContextMenusToMenu(HMENU hMenu, UINT* IndexMenu, UINT iIdCmdFirst, UINT iIdCmdLast, UINT uFlags); HRESULT DoPaste(LPCMINVOKECOMMANDINFOEX lpcmi, BOOL bLink); HRESULT DoOpenOrExplore(LPCMINVOKECOMMANDINFOEX lpcmi); HRESULT DoCreateLink(LPCMINVOKECOMMANDINFOEX lpcmi); HRESULT DoDelete(LPCMINVOKECOMMANDINFOEX lpcmi); HRESULT DoCopyOrCut(LPCMINVOKECOMMANDINFOEX lpcmi, BOOL bCopy); HRESULT DoRename(LPCMINVOKECOMMANDINFOEX lpcmi); HRESULT DoProperties(LPCMINVOKECOMMANDINFOEX lpcmi); HRESULT DoUndo(LPCMINVOKECOMMANDINFOEX lpcmi); HRESULT DoCreateNewFolder(LPCMINVOKECOMMANDINFOEX lpici); HRESULT DoCopyToMoveToFolder(LPCMINVOKECOMMANDINFOEX lpici, BOOL bCopy); HRESULT InvokeShellExt(LPCMINVOKECOMMANDINFOEX lpcmi); HRESULT InvokeRegVerb(LPCMINVOKECOMMANDINFOEX lpcmi); DWORD BrowserFlagsFromVerb(LPCMINVOKECOMMANDINFOEX lpcmi, PStaticShellEntry pEntry); HRESULT TryToBrowse(LPCMINVOKECOMMANDINFOEX lpcmi, LPCITEMIDLIST pidl, DWORD wFlags); HRESULT InvokePidl(LPCMINVOKECOMMANDINFOEX lpcmi, LPCITEMIDLIST pidl, PStaticShellEntry pEntry); PDynamicShellEntry GetDynamicEntry(UINT idCmd); BOOL MapVerbToCmdId(PVOID Verb, PUINT idCmd, BOOL IsUnicode);
public: CDefaultContextMenu(); ~CDefaultContextMenu(); HRESULT WINAPI Initialize(const DEFCONTEXTMENU *pdcm, LPFNDFMCALLBACK lpfn);
// IContextMenu STDMETHOD(QueryContextMenu)(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) override; STDMETHOD(InvokeCommand)(LPCMINVOKECOMMANDINFO lpcmi) override; STDMETHOD(GetCommandString)(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen) override;
// IContextMenu2 STDMETHOD(HandleMenuMsg)(UINT uMsg, WPARAM wParam, LPARAM lParam) override;
// IContextMenu3 STDMETHOD(HandleMenuMsg2)(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plResult) override;
// IObjectWithSite STDMETHOD(SetSite)(IUnknown *pUnkSite) override; STDMETHOD(GetSite)(REFIID riid, void **ppvSite) override;
// IServiceProvider STDMETHOD(QueryService)(REFGUID svc, REFIID riid, void**ppv) override { return IUnknown_QueryService(m_site, svc, riid, ppv); }
BEGIN_COM_MAP(CDefaultContextMenu) COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu) COM_INTERFACE_ENTRY_IID(IID_IContextMenu2, IContextMenu2) COM_INTERFACE_ENTRY_IID(IID_IContextMenu3, IContextMenu3) COM_INTERFACE_ENTRY_IID(IID_IObjectWithSite, IObjectWithSite) COM_INTERFACE_ENTRY_IID(IID_IServiceProvider, IServiceProvider) END_COM_MAP()};
CDefaultContextMenu::CDefaultContextMenu() : m_psf(NULL), m_pmcb(NULL), m_pfnmcb(NULL), m_cidl(0), m_apidl(NULL), m_pDataObj(NULL), m_cKeys(0), m_pidlFolder(NULL), m_bGroupPolicyActive(0), m_iIdQCMFirst(0), m_iIdSHEFirst(0), m_iIdSHELast(0), m_iIdSCMFirst(0), m_iIdSCMLast(0), m_iIdCBFirst(0), m_iIdCBLast(0), m_iIdDfltFirst(0), m_iIdDfltLast(0), m_hwnd(NULL){ *m_DefVerbs = UNICODE_NULL;}
CDefaultContextMenu::~CDefaultContextMenu(){ for (POSITION it = m_DynamicEntries.GetHeadPosition(); it != NULL;) { const DynamicShellEntry& info = m_DynamicEntries.GetNext(it); IUnknown_SetSite(info.pCM.p, NULL); } m_DynamicEntries.RemoveAll(); m_StaticEntries.RemoveAll();
for (UINT i = 0; i < m_cKeys; i++) RegCloseKey(m_aKeys[i]);
if (m_pidlFolder) CoTaskMemFree(m_pidlFolder); _ILFreeaPidl(const_cast<PITEMID_CHILD *>(m_apidl), m_cidl);}
HRESULT WINAPI CDefaultContextMenu::Initialize(const DEFCONTEXTMENU *pdcm, LPFNDFMCALLBACK lpfn){ TRACE("cidl %u\n", pdcm->cidl);
HRESULT hr = S_OK; if (!pdcm->pcmcb && !lpfn) { ERR("CDefaultContextMenu needs a callback!\n"); return E_INVALIDARG; }
m_cidl = pdcm->cidl; m_apidl = const_cast<PCUITEMID_CHILD_ARRAY>(_ILCopyaPidl(pdcm->apidl, m_cidl)); if (m_cidl && !m_apidl) return E_OUTOFMEMORY; m_psf = pdcm->psf; m_pmcb = pdcm->pcmcb; m_pfnmcb = lpfn; m_hwnd = pdcm->hwnd;
for (UINT i = 0; i < pdcm->cKeys; ++i) { if (i >= _countof(m_aKeys)) hr = E_INVALIDARG; else if ((m_aKeys[i] = SHRegDuplicateHKey(pdcm->aKeys[i])) != NULL) m_cKeys++; else hr = E_OUTOFMEMORY; }
m_psf->GetUIObjectOf(pdcm->hwnd, m_cidl, m_apidl, IID_NULL_PPV_ARG(IDataObject, &m_pDataObj));
if (pdcm->pidlFolder) { m_pidlFolder = ILClone(pdcm->pidlFolder); } else { CComPtr<IPersistFolder2> pf = NULL; if (SUCCEEDED(m_psf->QueryInterface(IID_PPV_ARG(IPersistFolder2, &pf)))) { if (FAILED(pf->GetCurFolder(&m_pidlFolder))) ERR("GetCurFolder failed\n"); } TRACE("pidlFolder %p\n", m_pidlFolder); }
return hr;}
HRESULT CDefaultContextMenu::_DoCallback(UINT uMsg, WPARAM wParam, LPVOID lParam){ if (m_pmcb) { return m_pmcb->CallBack(m_psf, m_hwnd, m_pDataObj, uMsg, wParam, (LPARAM)lParam); } else if(m_pfnmcb) { return m_pfnmcb(m_psf, m_hwnd, m_pDataObj, uMsg, wParam, (LPARAM)lParam); }
return E_FAIL;}
void CDefaultContextMenu::AddStaticEntry(const HKEY hkeyClass, const WCHAR *szVerb, UINT uFlags){ POSITION it = m_StaticEntries.GetHeadPosition(); while (it != NULL) { const StaticShellEntry& info = m_StaticEntries.GetNext(it); if (info.Verb.CompareNoCase(szVerb) == 0) { /* entry already exists */ return; } }
TRACE("adding verb %s\n", debugstr_w(szVerb));
if (!_wcsicmp(szVerb, L"open") && !(uFlags & CMF_NODEFAULT)) { /* open verb is always inserted in front */ m_StaticEntries.AddHead({ szVerb, hkeyClass }); } else { m_StaticEntries.AddTail({ szVerb, hkeyClass }); }}
void CDefaultContextMenu::AddStaticEntriesForKey(HKEY hKey, UINT uFlags){ WCHAR wszName[VERBKEY_CCHMAX]; DWORD cchName, dwIndex = 0; HKEY hShellKey;
LRESULT lres = RegOpenKeyExW(hKey, L"shell", 0, KEY_READ, &hShellKey); if (lres != STATUS_SUCCESS) return;
if (!*m_DefVerbs) { DWORD cb = sizeof(m_DefVerbs); RegGetValueW(hShellKey, NULL, NULL, RRF_RT_REG_SZ, NULL, m_DefVerbs, &cb); }
while(TRUE) { cchName = _countof(wszName); if (RegEnumKeyExW(hShellKey, dwIndex++, wszName, &cchName, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) break;
AddStaticEntry(hKey, wszName, uFlags); }
RegCloseKey(hShellKey);}
staticBOOLHasClipboardData(){ BOOL bRet = FALSE; CComPtr<IDataObject> pDataObj;
if (SUCCEEDED(OleGetClipboard(&pDataObj))) { FORMATETC formatetc;
TRACE("pDataObj=%p\n", pDataObj.p);
/* Set the FORMATETC structure*/ InitFormatEtc(formatetc, RegisterClipboardFormatW(CFSTR_SHELLIDLIST), TYMED_HGLOBAL); bRet = SUCCEEDED(pDataObj->QueryGetData(&formatetc)); }
return bRet;}
BOOLCDefaultContextMenu::IsShellExtensionAlreadyLoaded(REFCLSID clsid){ POSITION it = m_DynamicEntries.GetHeadPosition(); while (it != NULL) { const DynamicShellEntry& info = m_DynamicEntries.GetNext(it); if (info.ClassID == clsid) return TRUE; }
return FALSE;}
HRESULTCDefaultContextMenu::LoadDynamicContextMenuHandler(HKEY hKey, REFCLSID clsid){ HRESULT hr; TRACE("LoadDynamicContextMenuHandler entered with This %p hKey %p pclsid %s\n", this, hKey, wine_dbgstr_guid(&clsid));
if (IsShellExtensionAlreadyLoaded(clsid)) return S_OK;
CComPtr<IContextMenu> pcm; hr = SHCoCreateInstance(NULL, &clsid, NULL, IID_PPV_ARG(IContextMenu, &pcm)); if (FAILED(hr)) { ERR("SHCoCreateInstance(IContextMenu) failed.clsid %s hr 0x%x\n", wine_dbgstr_guid(&clsid), hr); return hr; }
CComPtr<IShellExtInit> pExtInit; hr = pcm->QueryInterface(IID_PPV_ARG(IShellExtInit, &pExtInit)); if (FAILED(hr)) { ERR("IContextMenu->QueryInterface(IShellExtInit) failed.clsid %s hr 0x%x\n", wine_dbgstr_guid(&clsid), hr); return hr; }
hr = pExtInit->Initialize(m_pDataObj ? NULL : m_pidlFolder, m_pDataObj, hKey); if (FAILED(hr)) { WARN("IShellExtInit::Initialize failed.clsid %s hr 0x%x\n", wine_dbgstr_guid(&clsid), hr); return hr; }
if (m_site) IUnknown_SetSite(pcm, m_site);
m_DynamicEntries.AddTail({ 0, 0, clsid, pcm });
return S_OK;}
BOOLCDefaultContextMenu::EnumerateDynamicContextHandlerForKey(HKEY hRootKey){ WCHAR wszName[MAX_PATH], wszBuf[MAX_PATH], *pwszClsid; DWORD cchName; HRESULT hr; HKEY hKey;
if (RegOpenKeyExW(hRootKey, L"shellex\\ContextMenuHandlers", 0, KEY_READ, &hKey) != ERROR_SUCCESS) { TRACE("RegOpenKeyExW failed\n"); return FALSE; }
DWORD dwIndex = 0; while (TRUE) { cchName = _countof(wszName); if (RegEnumKeyExW(hKey, dwIndex++, wszName, &cchName, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) break;
/* Key name or key value is CLSID */ CLSID clsid; hr = CLSIDFromString(wszName, &clsid); if (hr == S_OK) pwszClsid = wszName; else { DWORD cchBuf = _countof(wszBuf); if (RegGetValueW(hKey, wszName, NULL, RRF_RT_REG_SZ, NULL, wszBuf, &cchBuf) == ERROR_SUCCESS) hr = CLSIDFromString(wszBuf, &clsid); pwszClsid = wszBuf; }
if (FAILED(hr)) { ERR("CLSIDFromString failed for clsid %S hr 0x%x\n", pwszClsid, hr); continue; }
if (m_bGroupPolicyActive) { if (RegGetValueW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved", pwszClsid, RRF_RT_REG_SZ, NULL, NULL, NULL) != ERROR_SUCCESS) { ERR("Shell extension %s not approved!\n", pwszClsid); continue; } }
hr = LoadDynamicContextMenuHandler(hRootKey, clsid); if (FAILED(hr)) WARN("Failed to get context menu entires from shell extension! clsid: %S\n", pwszClsid); }
RegCloseKey(hKey); return TRUE;}
UINTCDefaultContextMenu::AddShellExtensionsToMenu(HMENU hMenu, UINT* pIndexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags){ UINT cIds = 0;
if (m_DynamicEntries.IsEmpty()) return cIds;
POSITION it = m_DynamicEntries.GetHeadPosition(); while (it != NULL) { DynamicShellEntry& info = m_DynamicEntries.GetNext(it);
HRESULT hr = info.pCM->QueryContextMenu(hMenu, *pIndexMenu, idCmdFirst + cIds, idCmdLast, uFlags); if (SUCCEEDED(hr)) { info.iIdCmdFirst = cIds; info.NumIds = HRESULT_CODE(hr); (*pIndexMenu) += info.NumIds;
cIds += info.NumIds; if (idCmdFirst + cIds >= idCmdLast) break; } TRACE("pEntry hr %x contextmenu %p cmdfirst %x num ids %x\n", hr, info.pCM.p, info.iIdCmdFirst, info.NumIds); } return cIds;}
UINTCDefaultContextMenu::AddStaticContextMenusToMenu( HMENU hMenu, UINT* pIndexMenu, UINT iIdCmdFirst, UINT iIdCmdLast, UINT uFlags){ UINT ntver = RosGetProcessEffectiveVersion(); MENUITEMINFOW mii = { sizeof(mii) }; WCHAR wszDispVerb[80]; // The limit on XP. If the friendly string is longer, it falls back to the verb key. UINT fState, idVerbRes; UINT cIds = 0, indexFirst = *pIndexMenu, indexDefault; int iDefVerbIndex = -1;
mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE | MIIM_DATA; mii.fType = MFT_STRING;
POSITION it = m_StaticEntries.GetHeadPosition(); bool first = true; while (it != NULL) { StaticShellEntry& info = m_StaticEntries.GetNext(it); BOOL forceFirstPos = FALSE; bool hide = false;
fState = MFS_ENABLED;
/* set first entry as default */ if (first) { fState |= MFS_DEFAULT; first = false; }
if (info.Verb.CompareNoCase(L"open") == 0) { idVerbRes = IDS_OPEN_VERB; fState |= MFS_DEFAULT; /* override default when open verb is found */ forceFirstPos++; } else if (info.Verb.CompareNoCase(L"explore") == 0) { idVerbRes = IDS_EXPLORE_VERB; if (uFlags & CMF_EXPLORE) { fState |= MFS_DEFAULT; forceFirstPos++; } } else if (info.Verb.CompareNoCase(L"printto") == 0) hide = true; else idVerbRes = 0;
/* By default use verb for menu item name */ mii.dwTypeData = (LPWSTR)info.Verb.GetString();
WCHAR wszKey[sizeof("shell\\") + MAX_VERB]; HRESULT hr = StringCbPrintfW(wszKey, sizeof(wszKey), L"shell\\%s", info.Verb.GetString()); if (FAILED_UNEXPECTEDLY(hr)) hide = true;
UINT cmdFlags = 0; HKEY hkVerb; if (RegOpenKeyExW(info.hkClass, wszKey, 0, KEY_READ, &hkVerb) == ERROR_SUCCESS) { if (!(uFlags & CMF_OPTIMIZEFORINVOKE)) { DWORD cbVerb = sizeof(wszDispVerb); LONG res = RegLoadMUIStringW(hkVerb, L"MUIVerb", wszDispVerb, cbVerb, NULL, 0, NULL); if (res || !*wszDispVerb) res = RegLoadMUIStringW(hkVerb, NULL, wszDispVerb, cbVerb, NULL, 0, NULL);
if ((res == ERROR_SUCCESS && *wszDispVerb) || (idVerbRes && LoadStringW(shell32_hInstance, idVerbRes, wszDispVerb, _countof(wszDispVerb))) || SUCCEEDED(GetFriendlyVerb(info.Verb, wszDispVerb, _countof(wszDispVerb)))) { mii.dwTypeData = wszDispVerb; } } } else { hkVerb = NULL; }
if (hkVerb) { if (!hide && !(uFlags & CMF_EXTENDEDVERBS)) hide = RegValueExists(hkVerb, L"Extended");
if (!hide) hide = RegValueExists(hkVerb, L"ProgrammaticAccessOnly");
if (!hide && !(uFlags & CMF_DISABLEDVERBS)) hide = RegValueExists(hkVerb, L"LegacyDisable");
if (DWORD dwRest = (hide ? 0 : RegGetDword(hkVerb, NULL, L"SuppressionPolicy", 0))) hide = SHRestricted((RESTRICTIONS)dwRest);
if (RegValueExists(hkVerb, L"NeverDefault")) fState &= ~MFS_DEFAULT;
if (RegValueExists(hkVerb, L"SeparatorBefore")) cmdFlags |= ECF_SEPARATORBEFORE; if (RegValueExists(hkVerb, L"SeparatorAfter")) cmdFlags |= ECF_SEPARATORAFTER;
RegCloseKey(hkVerb); }
if (((uFlags & CMF_NODEFAULT) && ntver >= _WIN32_WINNT_VISTA) || ((uFlags & CMF_DONOTPICKDEFAULT) && ntver >= _WIN32_WINNT_WIN7)) { fState &= ~MFS_DEFAULT; }
if (!hide) { if (cmdFlags & ECF_SEPARATORBEFORE) { if (InsertMenuItemAt(hMenu, *pIndexMenu, MF_SEPARATOR)) (*pIndexMenu)++; }
UINT pos = *pIndexMenu; int verbIndex = hkVerb ? FindVerbInDefaultVerbList(m_DefVerbs, info.Verb) : -1; if (verbIndex >= 0) { if (verbIndex < iDefVerbIndex || iDefVerbIndex < 0) { iDefVerbIndex = verbIndex; fState |= MFS_DEFAULT; forceFirstPos = TRUE; } else { fState &= ~MFS_DEFAULT; // We have already set a better default pos = indexDefault; } } else if (iDefVerbIndex >= 0) { fState &= ~MFS_DEFAULT; // We have already set the default if (forceFirstPos) pos = indexDefault; forceFirstPos = FALSE; }
mii.fState = fState; mii.wID = iIdCmdFirst + cIds; if (InsertMenuItemW(hMenu, forceFirstPos ? indexFirst : pos, TRUE, &mii)) (*pIndexMenu)++;
if (cmdFlags & ECF_SEPARATORAFTER) { if (InsertMenuItemAt(hMenu, *pIndexMenu, MF_SEPARATOR)) (*pIndexMenu)++; }
if (fState & MFS_DEFAULT) indexDefault = *pIndexMenu; // This is where we want to insert "high priority" verbs } cIds++; // Always increment the id because it acts as the index into m_StaticEntries
if (mii.wID >= iIdCmdLast) break; }
return cIds;}
BOOL WINAPI _InsertMenuItemW( HMENU hMenu, UINT indexMenu, BOOL fByPosition, UINT wID, UINT fType, LPCWSTR dwTypeData, UINT fState){ MENUITEMINFOW mii; WCHAR wszText[100];
ZeroMemory(&mii, sizeof(mii)); mii.cbSize = sizeof(mii); if (fType == MFT_SEPARATOR) mii.fMask = MIIM_ID | MIIM_TYPE; else if (fType == MFT_STRING) { mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE; if (IS_INTRESOURCE(dwTypeData)) { if (LoadStringW(shell32_hInstance, LOWORD((ULONG_PTR)dwTypeData), wszText, _countof(wszText))) mii.dwTypeData = wszText; else { ERR("failed to load string %p\n", dwTypeData); return FALSE; } } else mii.dwTypeData = (LPWSTR)dwTypeData; mii.fState = fState; }
mii.wID = wID; mii.fType = fType; return InsertMenuItemW(hMenu, indexMenu, fByPosition, &mii);}
voidCDefaultContextMenu::TryPickDefault(HMENU hMenu, UINT idCmdFirst, UINT DfltOffset, UINT uFlags){ // Are we allowed to pick a default? if ((uFlags & CMF_NODEFAULT) || ((uFlags & CMF_DONOTPICKDEFAULT) && RosGetProcessEffectiveVersion() >= _WIN32_WINNT_WIN7)) { return; }
// Do we already have a default? if ((int)GetMenuDefaultItem(hMenu, MF_BYPOSITION, 0) != -1) return;
// Does the view want to pick one? INT_PTR forceDfm = 0; if (SUCCEEDED(_DoCallback(DFM_GETDEFSTATICID, 0, &forceDfm)) && forceDfm) { for (UINT i = 0; i < _countof(g_StaticInvokeCmdMap); ++i) { UINT menuItemId = g_StaticInvokeCmdMap[i].IntVerb + DfltOffset - DCM_FCIDM_SHVIEW_OFFSET; if (g_StaticInvokeCmdMap[i].DfmCmd == forceDfm && SetMenuDefaultItem(hMenu, menuItemId, MF_BYCOMMAND)) { return; } } }
// Don't want to pick something like cut or delete as the default but // a static or dynamic verb is a good default. if (m_iIdSCMLast > m_iIdSCMFirst || m_iIdSHELast > m_iIdSHEFirst) SetMenuDefaultItem(hMenu, idCmdFirst, MF_BYCOMMAND);}
HRESULTWINAPICDefaultContextMenu::QueryContextMenu( HMENU hMenu, UINT IndexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags){ HRESULT hr; UINT idCmdNext = m_iIdQCMFirst = idCmdFirst; UINT cIds = 0;
TRACE("BuildShellItemContextMenu entered\n");
/* Load static verbs and shell extensions from registry */ for (UINT i = 0; i < m_cKeys && !(uFlags & CMF_NOVERBS); i++) { AddStaticEntriesForKey(m_aKeys[i], uFlags); EnumerateDynamicContextHandlerForKey(m_aKeys[i]); }
/* Add static context menu handlers */ cIds = AddStaticContextMenusToMenu(hMenu, &IndexMenu, idCmdFirst, idCmdLast, uFlags); m_iIdSCMFirst = 0; // FIXME: This should be = idCmdFirst? m_iIdSCMLast = cIds; idCmdNext = idCmdFirst + cIds;
/* Add dynamic context menu handlers */ cIds += AddShellExtensionsToMenu(hMenu, &IndexMenu, idCmdNext, idCmdLast, uFlags); m_iIdSHEFirst = m_iIdSCMLast; m_iIdSHELast = cIds; idCmdNext = idCmdFirst + cIds; TRACE("SH_LoadContextMenuHandlers first %x last %x\n", m_iIdSHEFirst, m_iIdSHELast);
/* Now let the callback add its own items */ QCMINFO qcminfo = {hMenu, IndexMenu, idCmdNext, idCmdLast, NULL}; if (SUCCEEDED(_DoCallback(DFM_MERGECONTEXTMENU, uFlags, &qcminfo))) { UINT added = qcminfo.idCmdFirst - idCmdNext; cIds += added; IndexMenu += added; m_iIdCBFirst = m_iIdSHELast; m_iIdCBLast = cIds; idCmdNext = idCmdFirst + cIds; }
//TODO: DFM_MERGECONTEXTMENU_BOTTOM
UINT idDefaultOffset = 0; BOOL isBackgroundMenu = !m_cidl; if (!(uFlags & CMF_VERBSONLY) && !isBackgroundMenu) { /* Get the attributes of the items */ SFGAOF rfg = SFGAO_BROWSABLE | SFGAO_CANCOPY | SFGAO_CANLINK | SFGAO_CANMOVE | SFGAO_CANDELETE | SFGAO_CANRENAME | SFGAO_HASPROPSHEET | SFGAO_FILESYSTEM | SFGAO_FOLDER; hr = m_psf->GetAttributesOf(m_cidl, m_apidl, &rfg); if (FAILED_UNEXPECTEDLY(hr)) return MAKE_HRESULT(SEVERITY_SUCCESS, 0, cIds);
/* Add the default part of the menu */ HMENU hmenuDefault = LoadMenuW(_AtlBaseModule.GetResourceInstance(), MAKEINTRESOURCEW(MENU_SHV_FILE));
/* Remove uneeded entries */ if (!(rfg & SFGAO_CANMOVE)) DeleteMenu(hmenuDefault, IDM_CUT, MF_BYCOMMAND); if (!(rfg & SFGAO_CANCOPY)) DeleteMenu(hmenuDefault, IDM_COPY, MF_BYCOMMAND); if (!((rfg & SFGAO_FILESYSTEM) && HasClipboardData())) DeleteMenu(hmenuDefault, IDM_INSERT, MF_BYCOMMAND); if (!(rfg & SFGAO_CANLINK)) DeleteMenu(hmenuDefault, IDM_CREATELINK, MF_BYCOMMAND); if (!(rfg & SFGAO_CANDELETE)) DeleteMenu(hmenuDefault, IDM_DELETE, MF_BYCOMMAND); if (!(rfg & SFGAO_CANRENAME) || !(uFlags & CMF_CANRENAME)) DeleteMenu(hmenuDefault, IDM_RENAME, MF_BYCOMMAND); if (!(rfg & SFGAO_HASPROPSHEET)) DeleteMenu(hmenuDefault, IDM_PROPERTIES, MF_BYCOMMAND);
idDefaultOffset = idCmdNext; UINT idMax = Shell_MergeMenus(hMenu, GetSubMenu(hmenuDefault, 0), IndexMenu, idCmdNext, idCmdLast, 0); m_iIdDfltFirst = cIds; cIds += idMax - idCmdNext; m_iIdDfltLast = cIds;
DestroyMenu(hmenuDefault); }
TryPickDefault(hMenu, idCmdFirst, idDefaultOffset, uFlags);
// TODO: DFM_MERGECONTEXTMENU_TOP
// TODO: Remove duplicate verbs. This will be easier when the static items handling // has been moved to CLSID_ShellFileDefExt so we only have to deal with ShellEx. // This is a Windows XP+ feature. On an unknown file type, Windows 2000 will // display both "Open" (openas from Unknown) and "Open with..." (openas from *).
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, cIds);}
HRESULT CDefaultContextMenu::DoPaste(LPCMINVOKECOMMANDINFOEX lpcmi, BOOL bLink){ HRESULT hr = _DoInvokeCommandCallback(lpcmi, DFM_CMD_PASTE); if (hr == S_OK) return hr;
CComPtr<IDataObject> pda; hr = OleGetClipboard(&pda); if (FAILED_UNEXPECTEDLY(hr)) return hr;
FORMATETC formatetc2; STGMEDIUM medium2; InitFormatEtc(formatetc2, RegisterClipboardFormatW(CFSTR_PREFERREDDROPEFFECT), TYMED_HGLOBAL);
DWORD dwKey= 0;
if (SUCCEEDED(pda->GetData(&formatetc2, &medium2))) { DWORD * pdwFlag = (DWORD*)GlobalLock(medium2.hGlobal); if (pdwFlag) { if (*pdwFlag == DROPEFFECT_COPY) dwKey = MK_CONTROL; else dwKey = MK_SHIFT; } else { ERR("No drop effect obtained\n"); } GlobalUnlock(medium2.hGlobal); }
if (bLink) { dwKey = MK_CONTROL|MK_SHIFT; }
CComPtr<IDropTarget> pdrop; if (m_cidl) hr = m_psf->GetUIObjectOf(NULL, 1, &m_apidl[0], IID_NULL_PPV_ARG(IDropTarget, &pdrop)); else hr = m_psf->CreateViewObject(NULL, IID_PPV_ARG(IDropTarget, &pdrop));
if (FAILED_UNEXPECTEDLY(hr)) return hr;
SHSimulateDrop(pdrop, pda, dwKey, NULL, NULL);
TRACE("CP result %x\n", hr); return S_OK;}
HRESULTCDefaultContextMenu::DoOpenOrExplore(LPCMINVOKECOMMANDINFOEX lpcmi){ UNIMPLEMENTED; return E_FAIL;}
HRESULT CDefaultContextMenu::DoCreateLink(LPCMINVOKECOMMANDINFOEX lpcmi){ HRESULT hr = _DoInvokeCommandCallback(lpcmi, DFM_CMD_LINK); if (hr == S_OK) return hr;
if (!m_cidl || !m_pDataObj) return E_FAIL;
CComPtr<IDropTarget> pDT; hr = m_psf->CreateViewObject(NULL, IID_PPV_ARG(IDropTarget, &pDT)); if (FAILED_UNEXPECTEDLY(hr)) return hr;
SHSimulateDrop(pDT, m_pDataObj, MK_CONTROL|MK_SHIFT, NULL, NULL);
return S_OK;}
HRESULT CDefaultContextMenu::DoDelete(LPCMINVOKECOMMANDINFOEX lpcmi){ HRESULT hr = _DoInvokeCommandCallback(lpcmi, DFM_CMD_DELETE); if (hr == S_OK) return hr;
if (!m_cidl || !m_pDataObj) return E_FAIL;
CComPtr<IDropTarget> pDT; hr = CRecyclerDropTarget_CreateInstance(IID_PPV_ARG(IDropTarget, &pDT)); if (FAILED_UNEXPECTEDLY(hr)) return hr;
DWORD grfKeyState = (lpcmi->fMask & CMIC_MASK_SHIFT_DOWN) ? MK_SHIFT : 0; SHSimulateDrop(pDT, m_pDataObj, grfKeyState, NULL, NULL);
return S_OK;}
HRESULT CDefaultContextMenu::DoCopyOrCut(LPCMINVOKECOMMANDINFOEX lpcmi, BOOL bCopy){ if (!m_cidl || !m_pDataObj) return E_FAIL;
HRESULT hr = _DoInvokeCommandCallback(lpcmi, bCopy ? DFM_CMD_COPY : DFM_CMD_MOVE); if (hr == S_OK) return hr;
FORMATETC formatetc; InitFormatEtc(formatetc, RegisterClipboardFormatW(CFSTR_PREFERREDDROPEFFECT), TYMED_HGLOBAL); STGMEDIUM medium = {0}; medium.tymed = TYMED_HGLOBAL; medium.hGlobal = GlobalAlloc(GHND, sizeof(DWORD)); DWORD* pdwFlag = (DWORD*)GlobalLock(medium.hGlobal); if (pdwFlag) *pdwFlag = bCopy ? DROPEFFECT_COPY : DROPEFFECT_MOVE; GlobalUnlock(medium.hGlobal); m_pDataObj->SetData(&formatetc, &medium, TRUE);
CComPtr<IShellFolderView> psfv; if (SUCCEEDED(IUnknown_QueryService(m_site, SID_SFolderView, IID_PPV_ARG(IShellFolderView, &psfv)))) psfv->SetPoints(m_pDataObj);
hr = OleSetClipboard(m_pDataObj); if (FAILED_UNEXPECTEDLY(hr)) return hr;
if (psfv) psfv->SetClipboard(!bCopy); return S_OK;}
HRESULT CDefaultContextMenu::DoRename(LPCMINVOKECOMMANDINFOEX lpcmi){ CComPtr<IShellBrowser> psb; HRESULT hr;
hr = _DoInvokeCommandCallback(lpcmi, DFM_CMD_RENAME); if (hr == S_OK) return hr;
if (!m_site || !m_cidl) return E_FAIL;
/* Get a pointer to the shell browser */ hr = IUnknown_QueryService(m_site, SID_IShellBrowser, IID_PPV_ARG(IShellBrowser, &psb)); if (FAILED_UNEXPECTEDLY(hr)) return hr;
CComPtr<IShellView> lpSV; hr = psb->QueryActiveShellView(&lpSV); if (FAILED_UNEXPECTEDLY(hr)) return hr;
SVSIF selFlags = SVSI_DESELECTOTHERS | SVSI_EDIT | SVSI_ENSUREVISIBLE | SVSI_FOCUSED | SVSI_SELECT; hr = lpSV->SelectItem(m_apidl[0], selFlags); if (FAILED_UNEXPECTEDLY(hr)) return hr;
return S_OK;}
HRESULTCDefaultContextMenu::DoProperties( LPCMINVOKECOMMANDINFOEX lpcmi){ HRESULT hr = _DoInvokeCommandCallback(lpcmi, DFM_CMD_PROPERTIES);
// We are asked to run the default property sheet if (hr == S_FALSE) { return SHELL32_ShowPropertiesDialog(m_pDataObj); }
return hr;}
HRESULTCDefaultContextMenu::DoUndo(LPCMINVOKECOMMANDINFOEX lpcmi){ ERR("TODO: Undo\n"); return E_NOTIMPL;}
HRESULTCDefaultContextMenu::DoCopyToMoveToFolder(LPCMINVOKECOMMANDINFOEX lpici, BOOL bCopy){ HRESULT hr = E_FAIL; if (!m_pDataObj) { ERR("m_pDataObj is NULL\n"); return hr; }
CComPtr<IContextMenu> pContextMenu; if (bCopy) hr = SHCoCreateInstance(NULL, &CLSID_CopyToMenu, NULL, IID_PPV_ARG(IContextMenu, &pContextMenu)); else hr = SHCoCreateInstance(NULL, &CLSID_MoveToMenu, NULL, IID_PPV_ARG(IContextMenu, &pContextMenu)); if (FAILED_UNEXPECTEDLY(hr)) return hr;
CComPtr<IShellExtInit> pInit; hr = pContextMenu->QueryInterface(IID_PPV_ARG(IShellExtInit, &pInit)); if (FAILED_UNEXPECTEDLY(hr)) return hr;
hr = pInit->Initialize(m_pidlFolder, m_pDataObj, NULL); if (FAILED_UNEXPECTEDLY(hr)) return hr;
if (bCopy) lpici->lpVerb = "copyto"; else lpici->lpVerb = "moveto";
return pContextMenu->InvokeCommand((LPCMINVOKECOMMANDINFO)lpici);}
// This code is taken from CNewMenu and should be shared between the 2 classesHRESULTCDefaultContextMenu::DoCreateNewFolder( LPCMINVOKECOMMANDINFOEX lpici){ WCHAR wszPath[MAX_PATH]; WCHAR wszName[MAX_PATH]; WCHAR wszNewFolder[25]; HRESULT hr;
/* Get folder path */ hr = SHGetPathFromIDListW(m_pidlFolder, wszPath); if (FAILED_UNEXPECTEDLY(hr)) return hr;
if (!LoadStringW(shell32_hInstance, IDS_NEWFOLDER, wszNewFolder, _countof(wszNewFolder))) return E_FAIL;
/* Create the name of the new directory */ if (!PathYetAnotherMakeUniqueName(wszName, wszPath, NULL, wszNewFolder)) return E_FAIL;
/* Create the new directory and show the appropriate dialog in case of error */ if (SHCreateDirectory(lpici->hwnd, wszName) != ERROR_SUCCESS) return E_FAIL;
/* Show and select the new item in the def view */ LPITEMIDLIST pidl; PITEMID_CHILD pidlNewItem; CComPtr<IShellView> psv;
/* Notify the view object about the new item */ SHChangeNotify(SHCNE_MKDIR, SHCNF_PATHW | SHCNF_FLUSH, (LPCVOID)wszName, NULL);
if (!m_site) return S_OK;
/* Get a pointer to the shell view */ hr = IUnknown_QueryService(m_site, SID_SFolderView, IID_PPV_ARG(IShellView, &psv)); if (FAILED_UNEXPECTEDLY(hr)) return S_OK;
/* Attempt to get the pidl of the new item */ hr = SHILCreateFromPathW(wszName, &pidl, NULL); if (FAILED_UNEXPECTEDLY(hr)) return hr;
pidlNewItem = ILFindLastID(pidl);
hr = psv->SelectItem(pidlNewItem, SVSI_DESELECTOTHERS | SVSI_EDIT | SVSI_ENSUREVISIBLE | SVSI_FOCUSED | SVSI_SELECT); if (FAILED_UNEXPECTEDLY(hr)) return hr;
SHFree(pidl);
return S_OK;}
PDynamicShellEntry CDefaultContextMenu::GetDynamicEntry(UINT idCmd){ POSITION it = m_DynamicEntries.GetHeadPosition(); while (it != NULL) { DynamicShellEntry& info = m_DynamicEntries.GetNext(it);
if (idCmd >= info.iIdCmdFirst + info.NumIds) continue;
if (idCmd < info.iIdCmdFirst || idCmd > info.iIdCmdFirst + info.NumIds) return NULL;
return &info; }
return NULL;}
BOOLCDefaultContextMenu::MapVerbToCmdId(PVOID Verb, PUINT idCmd, BOOL IsUnicode){ WCHAR UnicodeStr[MAX_VERB];
/* Loop through all the static verbs looking for a match */ for (UINT i = 0; i < _countof(g_StaticInvokeCmdMap); i++) { /* We can match both ANSI and unicode strings */ if (IsUnicode) { /* The static verbs are ANSI, get a unicode version before doing the compare */ SHAnsiToUnicode(g_StaticInvokeCmdMap[i].szStringVerb, UnicodeStr, MAX_VERB); if (!_wcsicmp(UnicodeStr, (LPWSTR)Verb)) { /* Return the Corresponding Id */ *idCmd = g_StaticInvokeCmdMap[i].IntVerb; return TRUE; } } else { if (!_stricmp(g_StaticInvokeCmdMap[i].szStringVerb, (LPSTR)Verb)) { *idCmd = g_StaticInvokeCmdMap[i].IntVerb; return TRUE; } } }
for (POSITION it = m_DynamicEntries.GetHeadPosition(); it != NULL;) { DynamicShellEntry& entry = m_DynamicEntries.GetNext(it); if (!entry.NumIds) continue; HRESULT hr = ::MapVerbToCmdId(Verb, IsUnicode, entry.pCM, 0, entry.NumIds - 1); if (SUCCEEDED(hr)) { *idCmd = m_iIdSHEFirst + entry.iIdCmdFirst + hr; return TRUE; } } return FALSE;}
HRESULTCDefaultContextMenu::InvokeShellExt( LPCMINVOKECOMMANDINFOEX lpcmi){ TRACE("verb %p first %x last %x\n", lpcmi->lpVerb, m_iIdSHEFirst, m_iIdSHELast);
UINT idCmd = LOWORD(lpcmi->lpVerb); PDynamicShellEntry pEntry = GetDynamicEntry(idCmd); if (!pEntry) return E_FAIL;
/* invoke the dynamic context menu */ lpcmi->lpVerb = MAKEINTRESOURCEA(idCmd - pEntry->iIdCmdFirst); return pEntry->pCM->InvokeCommand((LPCMINVOKECOMMANDINFO)lpcmi);}
DWORDCDefaultContextMenu::BrowserFlagsFromVerb(LPCMINVOKECOMMANDINFOEX lpcmi, PStaticShellEntry pEntry){ CComPtr<IShellBrowser> psb; HWND hwndTree; LPCWSTR FlagsName; WCHAR wszKey[sizeof("shell\\") + MAX_VERB]; HRESULT hr;
if (!m_site) return 0;
/* Get a pointer to the shell browser */ hr = IUnknown_QueryService(m_site, SID_IShellBrowser, IID_PPV_ARG(IShellBrowser, &psb)); if (FAILED(hr)) return 0;
/* See if we are in Explore or Browse mode. If the browser's tree is present, we are in Explore mode.*/ if (SUCCEEDED(psb->GetControlWindow(FCW_TREE, &hwndTree)) && hwndTree) FlagsName = L"ExplorerFlags"; else FlagsName = L"BrowserFlags";
CComPtr<ICommDlgBrowser> pcdb; if (SUCCEEDED(psb->QueryInterface(IID_PPV_ARG(ICommDlgBrowser, &pcdb)))) { if (LOBYTE(GetVersion()) < 6 || FlagsName[0] == 'E') return 0; // Don't browse in-place }
/* Try to get the flag from the verb */ hr = StringCbPrintfW(wszKey, sizeof(wszKey), L"shell\\%s", pEntry->Verb.GetString()); if (FAILED_UNEXPECTEDLY(hr)) return 0; return RegGetDword(pEntry->hkClass, wszKey, FlagsName, 0);}
HRESULTCDefaultContextMenu::TryToBrowse( LPCMINVOKECOMMANDINFOEX lpcmi, LPCITEMIDLIST pidlChild, DWORD wFlags){ CComPtr<IShellBrowser> psb; HRESULT hr;
if (!m_site) return E_FAIL;
/* Get a pointer to the shell browser */ hr = IUnknown_QueryService(m_site, SID_IShellBrowser, IID_PPV_ARG(IShellBrowser, &psb)); if (FAILED(hr)) return hr;
PIDLIST_ABSOLUTE pidl; hr = SHILCombine(m_pidlFolder, pidlChild, &pidl); if (FAILED_UNEXPECTEDLY(hr)) return hr;
hr = psb->BrowseObject(pidl, wFlags & ~SBSP_RELATIVE); ILFree(pidl); return hr;}
HRESULTCDefaultContextMenu::InvokePidl(LPCMINVOKECOMMANDINFOEX lpcmi, LPCITEMIDLIST pidl, PStaticShellEntry pEntry){ const BOOL unicode = IsUnicode(*lpcmi);
LPITEMIDLIST pidlFull = ILCombine(m_pidlFolder, pidl); if (pidlFull == NULL) { return E_FAIL; }
WCHAR wszPath[MAX_PATH]; BOOL bHasPath = SHGetPathFromIDListW(pidlFull, wszPath);
WCHAR wszDir[MAX_PATH];
SHELLEXECUTEINFOW sei = { sizeof(sei) }; sei.fMask = SEE_MASK_CLASSKEY | SEE_MASK_IDLIST | (CmicFlagsToSeeFlags(lpcmi->fMask) & ~SEE_MASK_INVOKEIDLIST); sei.hwnd = lpcmi->hwnd; sei.nShow = lpcmi->nShow; sei.lpVerb = pEntry->Verb; sei.lpIDList = pidlFull; sei.hkeyClass = pEntry->hkClass; sei.dwHotKey = lpcmi->dwHotKey; sei.hIcon = lpcmi->hIcon; sei.lpDirectory = wszDir;
if (unicode && !StrIsNullOrEmpty(lpcmi->lpDirectoryW)) { sei.lpDirectory = lpcmi->lpDirectoryW; } else if (bHasPath) { wcscpy(wszDir, wszPath); PathRemoveFileSpec(wszDir); } else { if (!SHGetPathFromIDListW(m_pidlFolder, wszDir)) *wszDir = UNICODE_NULL; }
if (bHasPath) sei.lpFile = wszPath;
CComHeapPtr<WCHAR> pszParamsW; if (unicode && !StrIsNullOrEmpty(lpcmi->lpParametersW)) sei.lpParameters = lpcmi->lpParametersW; else if (!StrIsNullOrEmpty(lpcmi->lpParameters) && __SHCloneStrAtoW(&pszParamsW, lpcmi->lpParameters)) sei.lpParameters = pszParamsW;
if (!sei.lpClass && (lpcmi->fMask & (CMIC_MASK_HASLINKNAME | CMIC_MASK_HASTITLE)) && unicode) sei.lpClass = lpcmi->lpTitleW; // Forward .lnk path from CShellLink::DoOpen (for consrv STARTF_TITLEISLINKNAME)
HRESULT hr = ShellExecuteExW(&sei) ? S_OK : HResultFromWin32(GetLastError()); ILFree(pidlFull); return hr;}
HRESULTCDefaultContextMenu::InvokeRegVerb( LPCMINVOKECOMMANDINFOEX lpcmi){ INT iCmd = LOWORD(lpcmi->lpVerb); HRESULT hr; UINT i;
POSITION it = m_StaticEntries.FindIndex(iCmd);
if (it == NULL) return E_INVALIDARG;
PStaticShellEntry pEntry = &m_StaticEntries.GetAt(it);
CRegKey VerbKey; WCHAR VerbKeyPath[sizeof("shell\\") + MAX_VERB]; hr = StringCbPrintfW(VerbKeyPath, sizeof(VerbKeyPath), L"shell\\%s", pEntry->Verb.GetString()); if (SUCCEEDED(hr) && m_pDataObj && VerbKey.Open(pEntry->hkClass, VerbKeyPath, KEY_READ) == ERROR_SUCCESS) { CLSID clsid;
DWORD KeyState = 0; if (lpcmi->fMask & CMIC_MASK_SHIFT_DOWN) KeyState |= MK_SHIFT; if (lpcmi->fMask & CMIC_MASK_CONTROL_DOWN) KeyState |= MK_CONTROL;
POINTL *pPtl = NULL; C_ASSERT(sizeof(POINT) == sizeof(POINTL)); if (lpcmi->fMask & CMIC_MASK_PTINVOKE) pPtl = (POINTL*)&lpcmi->ptInvoke;
CComPtr<IExecuteCommand> pEC; hr = SHELL_GetRegCLSID(VerbKey, L"command", L"DelegateExecute", clsid); if (SUCCEEDED(hr)) hr = CoCreateInstance(clsid, NULL, CLSCTX_ALL, IID_PPV_ARG(IExecuteCommand, &pEC)); if (SUCCEEDED(hr)) { CComPtr<IPropertyBag> pPB; SHCreatePropertyBagOnRegKey(VerbKey, NULL, STGM_READ, IID_PPV_ARG(IPropertyBag, &pPB)); return InvokeIExecuteCommandWithDataObject(pEC, pEntry->Verb.GetString(), pPB, m_pDataObj, lpcmi, static_cast<IContextMenu*>(this)); }
CComPtr<IDropTarget> pDT; hr = SHELL_GetRegCLSID(VerbKey, L"DropTarget", L"CLSID", clsid); if (SUCCEEDED(hr)) hr = CoCreateInstance(clsid, NULL, CLSCTX_ALL, IID_PPV_ARG(IDropTarget, &pDT)); if (SUCCEEDED(hr)) { CScopedSetObjectWithSite site(pDT, static_cast<IContextMenu*>(this)); CComPtr<IPropertyBag> pPB; SHCreatePropertyBagOnRegKey(VerbKey, NULL, STGM_READ, IID_PPV_ARG(IPropertyBag, &pPB)); IUnknown_InitializeCommand(pDT, pEntry->Verb.GetString(), pPB); hr = SHSimulateDrop(pDT, m_pDataObj, KeyState, pPtl, NULL); return hr; } }
/* Get the browse flags to see if we need to browse */ DWORD wFlags = BrowserFlagsFromVerb(lpcmi, pEntry);
for (i=0; i < m_cidl; i++) { /* Check if we need to browse */ if (wFlags) { hr = TryToBrowse(lpcmi, m_apidl[i], wFlags); if (SUCCEEDED(hr)) { /* In WinXP if we have browsed, we don't open any more folders. * In Win7 we browse to the first folder we find and * open new windows for each of the rest of the folders */ UINT ntver = RosGetProcessEffectiveVersion(); if (ntver >= _WIN32_WINNT_VISTA) wFlags = 0; // FIXME: = SBSP_NEWBROWSER | (wFlags & ~SBSP_SAMEBROWSER); else i = m_cidl;
continue; } }
InvokePidl(lpcmi, m_apidl[i], pEntry); }
return S_OK;}
HRESULTCDefaultContextMenu::_DoInvokeCommandCallback( LPCMINVOKECOMMANDINFOEX lpcmi, WPARAM CmdId){ BOOL Unicode = IsUnicode(*lpcmi); WCHAR lParamBuf[MAX_PATH]; LPARAM lParam = 0;
if (Unicode && lpcmi->lpParametersW) lParam = (LPARAM)lpcmi->lpParametersW; else if (lpcmi->lpParameters) lParam = SHAnsiToUnicode(lpcmi->lpParameters, lParamBuf, _countof(lParamBuf)) ? (LPARAM)lParamBuf : 0;
HRESULT hr;#if 0 // TODO: Try DFM_INVOKECOMMANDEX first. DFMICS dfmics = { sizeof(DFMICS), lpcmi->fMask, lParam, m_iIdSCMFirst?, m_iIdDfltLast?, (LPCMINVOKECOMMANDINFO)lpcmi, m_site }; hr = _DoCallback(DFM_INVOKECOMMANDEX, CmdId, &dfmics); if (hr == E_NOTIMPL)#endif hr = _DoCallback(DFM_INVOKECOMMAND, CmdId, (void*)lParam); return hr;}
HRESULTWINAPICDefaultContextMenu::InvokeCommand( LPCMINVOKECOMMANDINFO lpcmi){ CMINVOKECOMMANDINFOEX LocalInvokeInfo = {}; HRESULT Result; UINT CmdId;
/* Take a local copy of the fixed members of the struct as we might need to modify the verb */ memcpy(&LocalInvokeInfo, lpcmi, min(sizeof(LocalInvokeInfo), lpcmi->cbSize));
/* Check if this is a string verb */ if (!IS_INTRESOURCE(LocalInvokeInfo.lpVerb)) { /* Get the ID which corresponds to this verb, and update our local copy */ if (MapVerbToCmdId((LPVOID)LocalInvokeInfo.lpVerb, &CmdId, FALSE)) LocalInvokeInfo.lpVerb = MAKEINTRESOURCEA(CmdId); else return E_INVALIDARG; } CmdId = LOWORD(LocalInvokeInfo.lpVerb);
if (!m_DynamicEntries.IsEmpty() && CmdId >= m_iIdSHEFirst && CmdId < m_iIdSHELast) { LocalInvokeInfo.lpVerb -= m_iIdSHEFirst; Result = InvokeShellExt(&LocalInvokeInfo); return Result; }
if (!m_StaticEntries.IsEmpty() && CmdId >= m_iIdSCMFirst && CmdId < m_iIdSCMLast) { LocalInvokeInfo.lpVerb -= m_iIdSCMFirst; Result = InvokeRegVerb(&LocalInvokeInfo); // TODO: if (FAILED(Result) && !(lpcmi->fMask & CMIC_MASK_FLAG_NO_UI)) SHELL_ErrorBox(m_pSite, Result); return Result; }
if (m_iIdCBFirst != m_iIdCBLast && CmdId >= m_iIdCBFirst && CmdId < m_iIdCBLast) { Result = _DoInvokeCommandCallback(&LocalInvokeInfo, CmdId - m_iIdCBFirst); return Result; }
if (m_iIdDfltFirst != m_iIdDfltLast && CmdId >= m_iIdDfltFirst && CmdId < m_iIdDfltLast) { CmdId -= m_iIdDfltFirst; /* See the definitions of IDM_CUT and co to see how this works */ CmdId += DCM_FCIDM_SHVIEW_OFFSET; }
if (LocalInvokeInfo.cbSize >= sizeof(CMINVOKECOMMANDINFOEX) && (LocalInvokeInfo.fMask & CMIC_MASK_PTINVOKE)) { if (m_pDataObj && FAILED_UNEXPECTEDLY(DataObject_SetOffset(m_pDataObj, &LocalInvokeInfo.ptInvoke))) { ERR("Unable to add OFFSET to DataObject!\n"); } }
/* Check if this is a Id */ switch (CmdId) { case FCIDM_SHVIEW_INSERT: Result = DoPaste(&LocalInvokeInfo, FALSE); break; case FCIDM_SHVIEW_INSERTLINK: Result = DoPaste(&LocalInvokeInfo, TRUE); break; case FCIDM_SHVIEW_OPEN: case FCIDM_SHVIEW_EXPLORE: Result = DoOpenOrExplore(&LocalInvokeInfo); break; case FCIDM_SHVIEW_COPY: case FCIDM_SHVIEW_CUT: Result = DoCopyOrCut(&LocalInvokeInfo, CmdId == FCIDM_SHVIEW_COPY); break; case FCIDM_SHVIEW_CREATELINK: Result = DoCreateLink(&LocalInvokeInfo); break; case FCIDM_SHVIEW_DELETE: Result = DoDelete(&LocalInvokeInfo); break; case FCIDM_SHVIEW_RENAME: Result = DoRename(&LocalInvokeInfo); break; case FCIDM_SHVIEW_PROPERTIES: Result = DoProperties(&LocalInvokeInfo); break; case FCIDM_SHVIEW_NEWFOLDER: Result = DoCreateNewFolder(&LocalInvokeInfo); break; case FCIDM_SHVIEW_COPYTO: Result = DoCopyToMoveToFolder(&LocalInvokeInfo, TRUE); break; case FCIDM_SHVIEW_MOVETO: Result = DoCopyToMoveToFolder(&LocalInvokeInfo, FALSE); break; case FCIDM_SHVIEW_UNDO: Result = DoUndo(&LocalInvokeInfo); break; default: Result = E_INVALIDARG; ERR("Unhandled Verb %xl\n", LOWORD(LocalInvokeInfo.lpVerb)); break; }
return Result;}
HRESULTWINAPICDefaultContextMenu::GetCommandString( UINT_PTR idCommand, UINT uFlags, UINT* lpReserved, LPSTR lpszName, UINT uMaxNameLen){ /* We don't handle the help text yet */ if (uFlags == GCS_HELPTEXTA || uFlags == GCS_HELPTEXTW || HIWORD(idCommand) != 0) { return E_NOTIMPL; }
UINT CmdId = LOWORD(idCommand);
if (uFlags == GCS_VERBA || uFlags == GCS_VERBW) { UINT uMsg = (uFlags == GCS_VERBA) ? DFM_GETVERBA : DFM_GETVERBW; WPARAM wParam = MAKEWPARAM(idCommand, uMaxNameLen); HRESULT hr = _DoCallback(uMsg, wParam, lpszName); if (hr == S_OK) return S_OK; }
if (!m_DynamicEntries.IsEmpty() && CmdId >= m_iIdSHEFirst && CmdId < m_iIdSHELast) { idCommand -= m_iIdSHEFirst; PDynamicShellEntry pEntry = GetDynamicEntry(idCommand); if (!pEntry) return E_FAIL;
idCommand -= pEntry->iIdCmdFirst; return pEntry->pCM->GetCommandString(idCommand, uFlags, lpReserved, lpszName, uMaxNameLen); }
if (!m_StaticEntries.IsEmpty() && CmdId >= m_iIdSCMFirst && CmdId < m_iIdSCMLast) { /* Validation just returns S_OK on a match. The id exists. */ if (uFlags == GCS_VALIDATEA || uFlags == GCS_VALIDATEW) return S_OK;
CmdId -= m_iIdSCMFirst;
POSITION it = m_StaticEntries.FindIndex(CmdId);
if (it == NULL) return E_INVALIDARG;
PStaticShellEntry pEntry = &m_StaticEntries.GetAt(it);
if (uFlags == GCS_VERBW) return StringCchCopyW((LPWSTR)lpszName, uMaxNameLen, pEntry->Verb);
if (uFlags == GCS_VERBA) { if (SHUnicodeToAnsi(pEntry->Verb, lpszName, uMaxNameLen)) return S_OK; }
return E_INVALIDARG; }
//FIXME: Should we handle callbacks here? if (m_iIdDfltFirst != m_iIdDfltLast && CmdId >= m_iIdDfltFirst && CmdId < m_iIdDfltLast) { CmdId -= m_iIdDfltFirst; /* See the definitions of IDM_CUT and co to see how this works */ CmdId += DCM_FCIDM_SHVIEW_OFFSET; }
/* Loop looking for a matching Id */ for (UINT i = 0; i < _countof(g_StaticInvokeCmdMap); i++) { if (g_StaticInvokeCmdMap[i].IntVerb == CmdId) { /* Validation just returns S_OK on a match */ if (uFlags == GCS_VALIDATEA || uFlags == GCS_VALIDATEW) return S_OK;
/* Return a copy of the ANSI verb */ if (uFlags == GCS_VERBA) return StringCchCopyA(lpszName, uMaxNameLen, g_StaticInvokeCmdMap[i].szStringVerb);
/* Convert the ANSI verb to unicode and return that */ if (uFlags == GCS_VERBW) { if (SHAnsiToUnicode(g_StaticInvokeCmdMap[i].szStringVerb, (LPWSTR)lpszName, uMaxNameLen)) return S_OK; } } }
return E_INVALIDARG;}
HRESULTWINAPICDefaultContextMenu::HandleMenuMsg( UINT uMsg, WPARAM wParam, LPARAM lParam){ return HandleMenuMsg2(uMsg, wParam, lParam, NULL);}
HRESULT SHGetMenuIdFromMenuMsg(UINT uMsg, LPARAM lParam, UINT *CmdId){ if (uMsg == WM_DRAWITEM) { DRAWITEMSTRUCT* pDrawStruct = reinterpret_cast<DRAWITEMSTRUCT*>(lParam); *CmdId = pDrawStruct->itemID; return S_OK; } else if (uMsg == WM_MEASUREITEM) { MEASUREITEMSTRUCT* pMeasureStruct = reinterpret_cast<MEASUREITEMSTRUCT*>(lParam); *CmdId = pMeasureStruct->itemID; return S_OK; } return E_FAIL;}
HRESULTWINAPICDefaultContextMenu::HandleMenuMsg2( UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plResult){ if (!SHELL_IsContextMenuMsg(uMsg)) return E_FAIL;
UINT CmdId; if (uMsg == WM_INITMENUPOPUP) { CmdId = GetMenuItemID((HMENU)wParam, 0); if (CmdId == ~0ul) return E_FAIL; } else { HRESULT hr = SHGetMenuIdFromMenuMsg(uMsg, lParam, &CmdId); if (FAILED(hr)) return S_FALSE; } CmdId -= m_iIdQCMFirst; // Convert from Win32 id to our base
if (CmdId >= m_iIdSHEFirst && CmdId < m_iIdSHELast) { if (PDynamicShellEntry pEntry = GetDynamicEntry(CmdId - m_iIdSHEFirst)) return SHForwardContextMenuMsg(pEntry->pCM, uMsg, wParam, lParam, plResult, TRUE); } // TODO: _DoCallback(DFM_WM_*, ...) return E_FAIL;}
HRESULTWINAPICDefaultContextMenu::SetSite(IUnknown *pUnkSite){ m_site = pUnkSite; return S_OK;}
HRESULTWINAPICDefaultContextMenu::GetSite(REFIID riid, void **ppvSite){ if (!m_site) return E_FAIL;
return m_site->QueryInterface(riid, ppvSite);}
staticHRESULTCDefaultContextMenu_CreateInstance(const DEFCONTEXTMENU *pdcm, LPFNDFMCALLBACK lpfn, REFIID riid, void **ppv){ return ShellObjectCreatorInit<CDefaultContextMenu>(pdcm, lpfn, riid, ppv);}
/************************************************************************* * SHCreateDefaultContextMenu [SHELL32.325] Vista API * */
HRESULTWINAPISHCreateDefaultContextMenu(const DEFCONTEXTMENU *pdcm, REFIID riid, void **ppv){ HRESULT hr;
if (!ppv) return E_INVALIDARG;
hr = CDefaultContextMenu_CreateInstance(pdcm, NULL, riid, ppv); if (FAILED_UNEXPECTEDLY(hr)) return hr;
return S_OK;}
/************************************************************************* * CDefFolderMenu_Create2 [SHELL32.701] * */
HRESULTWINAPICDefFolderMenu_Create2( PCIDLIST_ABSOLUTE pidlFolder, HWND hwnd, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, IShellFolder *psf, LPFNDFMCALLBACK lpfn, UINT nKeys, const HKEY *ahkeyClsKeys, IContextMenu **ppcm){ DEFCONTEXTMENU dcm; dcm.hwnd = hwnd; dcm.pcmcb = NULL; dcm.pidlFolder = pidlFolder; dcm.psf = psf; dcm.cidl = cidl; dcm.apidl = apidl; dcm.punkAssociationInfo = NULL; dcm.cKeys = nKeys; dcm.aKeys = ahkeyClsKeys;
HRESULT hr = CDefaultContextMenu_CreateInstance(&dcm, lpfn, IID_PPV_ARG(IContextMenu, ppcm)); if (FAILED_UNEXPECTEDLY(hr)) return hr;
return S_OK;}