From 373816c729a7186ba2049cabc7a32a585f899ab7 Mon Sep 17 00:00:00 2001 From: Whindmar Saksit Date: Sun, 24 Mar 2024 18:23:07 +0100 Subject: [PATCH] [SHELL32] Support IDropTarget handler in IContextMenu static verbs (#5860) This adds support for the `DropHandler` subkey in static verbs. `IDropTarget` is called when the verb is invoked. The advantage of this is that no library needs to be loaded until the verb is actually invoked unlike `IContextMenu` shell extensions that have to be loaded during menu creation. This method is preferred for verb implementations that must work on Windows XP, see https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/dd940354%28v%3Dvs.85%29?redirectedfrom=MSDN#description --- dll/win32/shell32/CDefaultContextMenu.cpp | 65 ++++++++++++++++++++--- 1 file changed, 59 insertions(+), 6 deletions(-) diff --git a/dll/win32/shell32/CDefaultContextMenu.cpp b/dll/win32/shell32/CDefaultContextMenu.cpp index cfd43b95ae9..31231af1db9 100644 --- a/dll/win32/shell32/CDefaultContextMenu.cpp +++ b/dll/win32/shell32/CDefaultContextMenu.cpp @@ -11,6 +11,19 @@ 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 HRESULT +SHELL_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 inline bool RegValueExists(HKEY hKey, LPCWSTR Name) { return RegQueryValueExW(hKey, Name, NULL, NULL, NULL, NULL) == ERROR_SUCCESS; @@ -70,7 +83,8 @@ static const struct _StaticInvokeCommandMap_ class CDefaultContextMenu : public CComObjectRootEx, public IContextMenu3, - public IObjectWithSite + public IObjectWithSite, + public IServiceProvider { private: CComPtr m_site; @@ -142,11 +156,18 @@ class CDefaultContextMenu : STDMETHOD(SetSite)(IUnknown *pUnkSite) override; STDMETHOD(GetSite)(REFIID riid, void **ppvSite) override; + // IServiceProvider + virtual HRESULT STDMETHODCALLTYPE QueryService(REFGUID svc, REFIID riid, void**ppv) + { + 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() }; @@ -531,7 +552,7 @@ CDefaultContextMenu::AddStaticContextMenusToMenu( /* By default use verb for menu item name */ mii.dwTypeData = (LPWSTR)info.Verb.GetString(); - WCHAR wszKey[256]; + WCHAR wszKey[sizeof("shell\\") + MAX_VERB]; HRESULT hr; hr = StringCbPrintfW(wszKey, sizeof(wszKey), L"shell\\%s", info.Verb.GetString()); if (FAILED_UNEXPECTEDLY(hr)) @@ -1083,9 +1104,6 @@ PDynamicShellEntry CDefaultContextMenu::GetDynamicEntry(UINT idCmd) return NULL; } -// FIXME: 260 is correct, but should this be part of the SDK or just MAX_PATH? -#define MAX_VERB 260 - BOOL CDefaultContextMenu::MapVerbToCmdId(PVOID Verb, PUINT idCmd, BOOL IsUnicode) { @@ -1141,7 +1159,7 @@ CDefaultContextMenu::BrowserFlagsFromVerb(LPCMINVOKECOMMANDINFOEX lpcmi, PStatic CComPtr psb; HWND hwndTree; LPCWSTR FlagsName; - WCHAR wszKey[256]; + WCHAR wszKey[sizeof("shell\\") + MAX_VERB]; HRESULT hr; DWORD wFlags; DWORD cbVerb; @@ -1260,6 +1278,40 @@ CDefaultContextMenu::InvokeRegVerb( 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; + + // TODO: IExecuteCommand + + CComPtr 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)) + { + IUnknown_SetSite(pDT, static_cast(this)); + hr = SHSimulateDrop(pDT, m_pDataObj, KeyState, pPtl, NULL); + IUnknown_SetSite(pDT, NULL); + return hr; + } + } + /* Get the browse flags to see if we need to browse */ DWORD wFlags = BrowserFlagsFromVerb(lpcmi, pEntry); @@ -1324,6 +1376,7 @@ CDefaultContextMenu::InvokeCommand( { 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; }