[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
This commit is contained in:
Whindmar Saksit 2024-03-24 18:23:07 +01:00 committed by GitHub
parent 54df31ccac
commit 373816c729
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -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<CComMultiThreadModelNoCS>,
public IContextMenu3,
public IObjectWithSite
public IObjectWithSite,
public IServiceProvider
{
private:
CComPtr<IUnknown> 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<IShellBrowser> 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<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))
{
IUnknown_SetSite(pDT, static_cast<IContextMenu*>(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;
}