From 375fca5058ca56a7ae6580934e4d7d9821153b5c Mon Sep 17 00:00:00 2001 From: Whindmar Saksit Date: Sat, 5 Jul 2025 18:18:51 +0200 Subject: [PATCH] [SHELL32] Implement SHSimulateDropOnClsid (#8223) --- dll/win32/shell32/CDefaultContextMenu.cpp | 3 +- dll/win32/shell32/CDropTargetHelper.cpp | 19 +++++ dll/win32/shell32/shell32.spec | 3 +- dll/win32/shell32/utils.cpp | 8 +-- dll/win32/shell32/wine/shell32_main.h | 10 +++ dll/win32/shell32/wine/shellole.c | 87 ++++++++++++++++------- sdk/include/reactos/shellutils.h | 18 +++++ 7 files changed, 112 insertions(+), 36 deletions(-) diff --git a/dll/win32/shell32/CDefaultContextMenu.cpp b/dll/win32/shell32/CDefaultContextMenu.cpp index 36b605efd53..ddbe835528a 100644 --- a/dll/win32/shell32/CDefaultContextMenu.cpp +++ b/dll/win32/shell32/CDefaultContextMenu.cpp @@ -1570,12 +1570,11 @@ CDefaultContextMenu::InvokeRegVerb( hr = CoCreateInstance(clsid, NULL, CLSCTX_ALL, IID_PPV_ARG(IDropTarget, &pDT)); if (SUCCEEDED(hr)) { + CScopedSetObjectWithSite site(pDT, static_cast(this)); CComPtr pPB; SHCreatePropertyBagOnRegKey(VerbKey, NULL, STGM_READ, IID_PPV_ARG(IPropertyBag, &pPB)); - IUnknown_SetSite(pDT, static_cast(this)); IUnknown_InitializeCommand(pDT, pEntry->Verb.GetString(), pPB); hr = SHSimulateDrop(pDT, m_pDataObj, KeyState, pPtl, NULL); - IUnknown_SetSite(pDT, NULL); return hr; } } diff --git a/dll/win32/shell32/CDropTargetHelper.cpp b/dll/win32/shell32/CDropTargetHelper.cpp index b042f2df068..4533c266ad2 100644 --- a/dll/win32/shell32/CDropTargetHelper.cpp +++ b/dll/win32/shell32/CDropTargetHelper.cpp @@ -77,3 +77,22 @@ HRESULT WINAPI CDropTargetHelper::Show(BOOL fShow) FIXME ("(%p)->(%u)\n", this, fShow); return E_NOTIMPL; } + +/************************************************************************* + * SH32_SimulateDropWithSite [SHELL32.INTERNAL] + */ +static HRESULT SH32_SimulateDropWithSite(IDropTarget *pDT, IDataObject *pDO, DWORD grfKeyState, PPOINTL pPtl, LPDWORD pdwEffect, IUnknown *pSite) +{ + CScopedSetObjectWithSite site(pDT, pSite); + return SHSimulateDrop(pDT, pDO, grfKeyState, pPtl, pdwEffect); +} + +/************************************************************************* + * SHSimulateDropOnClsid [SHELL32.751] + */ +EXTERN_C HRESULT WINAPI SHSimulateDropOnClsid(_In_ REFCLSID clsid, _In_opt_ IUnknown* pSite, _In_ IDataObject* pDO) +{ + CComPtr pDT; + HRESULT hr = SH32_ExtCoCreateInstance(NULL, &clsid, NULL, CLSCTX_ALL, IID_PPV_ARG(IDropTarget, &pDT)); + return SUCCEEDED(hr) ? SH32_SimulateDropWithSite(pDT, pDO, 0, NULL, NULL, pSite) : hr; +} diff --git a/dll/win32/shell32/shell32.spec b/dll/win32/shell32/shell32.spec index c6fb211fe49..e88358c7650 100644 --- a/dll/win32/shell32/shell32.spec +++ b/dll/win32/shell32/shell32.spec @@ -460,7 +460,7 @@ 748 stdcall -noname SHLimitInputCombo(ptr ptr) 749 stdcall -noname -version=0x501-0x502 SHGetShellStyleHInstance() 750 stdcall -noname SHGetAttributesFromDataObject(ptr long ptr ptr) -751 stub -noname SHSimulateDropOnClsid +751 stdcall -noname SHSimulateDropOnClsid(ptr ptr ptr) 752 stdcall -noname SHGetComputerDisplayNameW(wstr long ptr long) 753 stdcall -noname CheckStagingArea() 754 stub -noname SHLimitInputEditWithFlags @@ -468,3 +468,4 @@ 756 stub -noname DeleteFileThumbnail 757 stdcall -noname -version=0x600+ DisplayNameOfW(ptr ptr long ptr long) 866 stdcall -noname -version=0x600+ SHExtCoCreateInstance(wstr ptr ptr ptr ptr) +887 stub -noname -version=0x601+ SHExtCoCreateInstanceCheckCategory diff --git a/dll/win32/shell32/utils.cpp b/dll/win32/shell32/utils.cpp index 5a64f514094..6d759a1d1b0 100644 --- a/dll/win32/shell32/utils.cpp +++ b/dll/win32/shell32/utils.cpp @@ -1698,8 +1698,7 @@ InvokeIExecuteCommand( if (!pEC) return E_INVALIDARG; - if (pSite) - IUnknown_SetSite(pEC, pSite); + CScopedSetObjectWithSite site(pEC, pSite); IUnknown_InitializeCommand(pEC, pszCommandName, pPB); CComPtr pOWS; @@ -1721,10 +1720,7 @@ InvokeIExecuteCommand( if (fMask & CMIC_MASK_PTINVOKE) pEC->SetPosition(pICI->ptInvoke); - HRESULT hr = pEC->Execute(); - if (pSite) - IUnknown_SetSite(pEC, NULL); - return hr; + return pEC->Execute(); } EXTERN_C HRESULT diff --git a/dll/win32/shell32/wine/shell32_main.h b/dll/win32/shell32/wine/shell32_main.h index 98bfbd36ab9..3b47e4a01ba 100644 --- a/dll/win32/shell32/wine/shell32_main.h +++ b/dll/win32/shell32/wine/shell32_main.h @@ -42,6 +42,16 @@ enum { }; DWORD SH32_InternalRestricted(DWORD rest); +/* COM */ +HRESULT WINAPI +SH32_ExtCoCreateInstance( + _In_opt_ LPCWSTR aclsid, + _In_opt_ const CLSID *clsid, + _In_opt_ LPUNKNOWN pUnkOuter, + _In_ DWORD dwClsCtx, + _In_ REFIID riid, + _Out_ LPVOID *ppv); + /* Iconcache */ #define INVALID_INDEX -1 BOOL SIC_Initialize(void); diff --git a/dll/win32/shell32/wine/shellole.c b/dll/win32/shell32/wine/shellole.c index e9cd279d850..426dac51b72 100644 --- a/dll/win32/shell32/wine/shellole.c +++ b/dll/win32/shell32/wine/shellole.c @@ -86,28 +86,13 @@ static const struct { #endif /* !__REACTOS__ */ -/************************************************************************* - * SHCoCreateInstance [SHELL32.102] - * - * Equivalent to CoCreateInstance. Under Windows 9x this function could sometimes - * use the shell32 built-in "mini-COM" without the need to load ole32.dll - see - * SHLoadOLE for details. - * - * Under wine if a "LoadWithoutCOM" value is present or the object resides in - * shell32.dll the function will load the object manually without the help of ole32 - * - * NOTES - * exported by ordinal - * - * SEE ALSO - * CoCreateInstance, SHLoadOLE - */ -HRESULT WINAPI SHCoCreateInstance( - LPCWSTR aclsid, - const CLSID *clsid, - LPUNKNOWN pUnkOuter, - REFIID refiid, - LPVOID *ppv) +HRESULT WINAPI SH32_CoCreateInstance( + LPCWSTR aclsid, + const CLSID *clsid, + LPUNKNOWN pUnkOuter, + DWORD dwClsCtx, + REFIID refiid, + LPVOID *ppv) { DWORD hres; CLSID iid; @@ -148,7 +133,7 @@ HRESULT WINAPI SHCoCreateInstance( return E_ACCESSDENIED; /* if a special registry key is set, we load a shell extension without help of OLE32 */ - if (!SHQueryValueExW(hKey, L"LoadWithoutCOM", 0, 0, 0, 0)) + if ((dwClsCtx & CLSCTX_INPROC_SERVER) && !SHQueryValueExW(hKey, L"LoadWithoutCOM", 0, 0, 0, 0)) { /* load an external dll without ole32 */ HANDLE hLibrary; @@ -177,7 +162,7 @@ HRESULT WINAPI SHCoCreateInstance( } else { /* load an external dll in the usual way */ - hres = CoCreateInstance(myclsid, pUnkOuter, CLSCTX_INPROC_SERVER, refiid, ppv); + hres = CoCreateInstance(myclsid, pUnkOuter, dwClsCtx, refiid, ppv); } end: @@ -193,17 +178,65 @@ end: return hres; } +/************************************************************************* + * SHCoCreateInstance [SHELL32.102] + * + * Equivalent to CoCreateInstance. Under Windows 9x this function could sometimes + * use the shell32 built-in "mini-COM" without the need to load ole32.dll - see + * SHLoadOLE for details. + * + * Under wine if a "LoadWithoutCOM" value is present or the object resides in + * shell32.dll the function will load the object manually without the help of ole32 + * + * NOTES + * exported by ordinal + * + * SEE ALSO + * CoCreateInstance, SHLoadOLE + */ +HRESULT WINAPI SHCoCreateInstance( + LPCWSTR aclsid, + const CLSID *clsid, + LPUNKNOWN pUnkOuter, + REFIID riid, + LPVOID *ppv) +{ + return SH32_CoCreateInstance(aclsid, clsid, pUnkOuter, CLSCTX_INPROC_SERVER, riid, ppv); +} + +HRESULT WINAPI SH32_ExtCoCreateInstance( + _In_opt_ LPCWSTR aclsid, + _In_opt_ const CLSID *clsid, + _In_opt_ LPUNKNOWN pUnkOuter, + _In_ DWORD dwClsCtx, + _In_ REFIID riid, + _Out_ LPVOID *ppv) +{ + // TODO: Verify that this CLSID is allowed (..\CurrentVersion\Shell Extensions\Approved) if REST_ENFORCESHELLEXTSECURITY is active + return SH32_CoCreateInstance(aclsid, clsid, pUnkOuter, dwClsCtx, riid, ppv); +} + +/************************************************************************* + * SHCoCreateInstance [SHELL32.866] + */ HRESULT WINAPI SHExtCoCreateInstance( _In_opt_ LPCWSTR aclsid, _In_opt_ const CLSID *clsid, _In_opt_ LPUNKNOWN pUnkOuter, - _In_ REFIID refiid, + _In_ REFIID riid, _Out_ LPVOID *ppv) { - // TODO: Verify that this CLSID is allowed if REST_ENFORCESHELLEXTSECURITY is active - return SHCoCreateInstance(aclsid, clsid, pUnkOuter, refiid, ppv); + return SH32_ExtCoCreateInstance(aclsid, clsid, pUnkOuter, CLSCTX_INPROC_SERVER, riid, ppv); } +#if 0 +/************************************************************************* + * SHExtCoCreateInstanceCheckCategory [SHELL32.887] + */ +// TODO: ICatInformation::IsClassOfCategories? +// return SH32_ExtCoCreateInstance() +#endif + #ifndef __REACTOS__ /************************************************************************* * DllGetClassObject [SHELL32.@] diff --git a/sdk/include/reactos/shellutils.h b/sdk/include/reactos/shellutils.h index 9d3af701068..31c3682484e 100644 --- a/sdk/include/reactos/shellutils.h +++ b/sdk/include/reactos/shellutils.h @@ -652,6 +652,24 @@ public: return m_pUnkSite ? m_pUnkSite->QueryInterface(riid, ppvSite) : E_FAIL; } }; + +#if defined(__WINE_SHLWAPI_H) && !defined(NO_SHLWAPI) +struct CScopedSetObjectWithSite +{ + IUnknown *m_pObj; + explicit CScopedSetObjectWithSite(IUnknown *pObj, IUnknown *pSite) : m_pObj(pObj) + { + if (pSite) + IUnknown_SetSite(pObj, pSite); + else + m_pObj = NULL; + } + ~CScopedSetObjectWithSite() + { + IUnknown_SetSite(m_pObj, NULL); + } +}; +#endif #endif /* __cplusplus */ #define S_LESSTHAN 0xffff