[SHELL32] Fix IShellExecuteHookW (#8097)

This commit is contained in:
Whindmar Saksit 2025-06-16 21:47:45 +02:00 committed by GitHub
parent c0b4a2cab0
commit a3b032b118
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 150 additions and 49 deletions

View file

@ -672,6 +672,7 @@ HRESULT WINAPI CNetConUiObject::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
}
case IDS_NET_STATUS:
{
// TODO: Windows does not display this with an owner window
return ShowNetConnectionStatus(m_lpOleCmd, m_pidl, lpcmi->hwnd);
}
case IDS_NET_REPAIR:
@ -837,27 +838,6 @@ HRESULT WINAPI CNetworkConnections::GetCurFolder(PIDLIST_ABSOLUTE *pidl)
return S_OK;
}
/************************************************************************
* ISF_NetConnect_ShellExecuteHookW_Execute
*/
HRESULT WINAPI CNetworkConnections::Execute(LPSHELLEXECUTEINFOW pei)
{
PCUITEMID_CHILD pidl = ILFindLastID((ITEMIDLIST*)pei->lpIDList);
PNETCONIDSTRUCT pdata = ILGetConnData(pidl);
if (!pdata)
{
ERR("Got invalid pidl\n");
return E_FAIL;
}
if (pdata->Status == NCS_CONNECTED)
{
return ShowNetConnectionStatus(m_lpOleCmd, pidl, pei->hwnd);
}
return S_OK;
}
HRESULT WINAPI CNetworkConnections::Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID)
{
FIXME("CNetworkConnections::Initialize()\n");

View file

@ -14,8 +14,7 @@ class CNetworkConnections:
public IShellExtInit,
public IShellFolder2,
public IOleCommandTarget,
public IShellFolderViewCB,
public IShellExecuteHookW
public IShellFolderViewCB
{
public:
CNetworkConnections();
@ -57,9 +56,6 @@ class CNetworkConnections:
// IShellFolderViewCB
STDMETHOD(MessageSFVCB)(UINT uMsg, WPARAM wParam, LPARAM lParam) override;
// IShellExecuteHookW
STDMETHOD(Execute)(LPSHELLEXECUTEINFOW pei) override;
private:
/* both paths are parsible from the desktop */
@ -77,7 +73,6 @@ class CNetworkConnections:
COM_INTERFACE_ENTRY_IID(IID_IShellExtInit, IShellExtInit)
COM_INTERFACE_ENTRY_IID(IID_IOleCommandTarget, IOleCommandTarget)
COM_INTERFACE_ENTRY_IID(IID_IShellFolderViewCB, IShellFolderViewCB)
COM_INTERFACE_ENTRY_IID(IID_IShellExecuteHookW, IShellExecuteHookW)
END_COM_MAP()
DECLARE_NO_REGISTRY()

View file

@ -1397,10 +1397,9 @@ CDefaultContextMenu::InvokePidl(LPCMINVOKECOMMANDINFOEX lpcmi, LPCITEMIDLIST pid
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)
ShellExecuteExW(&sei);
HRESULT hr = ShellExecuteExW(&sei) ? S_OK : HResultFromWin32(GetLastError());
ILFree(pidlFull);
return S_OK;
return hr;
}
HRESULT

View file

@ -27,7 +27,7 @@ SHELL_InitializeExtension(REFCLSID clsid, PCIDLIST_ABSOLUTE pidlFolder, IDataObj
{
*ppv = NULL;
IUnknown *pUnk;
HRESULT hr = SHCoCreateInstance(NULL, &clsid, NULL, riid, (void**)&pUnk);
HRESULT hr = SHExtCoCreateInstance(NULL, &clsid, NULL, riid, (void**)&pUnk);
if (SUCCEEDED(hr))
{
CComPtr<IShellExtInit> Init;

View file

@ -98,7 +98,7 @@
99 stdcall -noname SetAppStartingCursor(long long)
100 stdcall SHRestricted(long)
101 stdcall OpenAs_RunDLLA(long long str long)
102 stdcall SHCoCreateInstance(wstr ptr long ptr ptr)
102 stdcall SHCoCreateInstance(wstr ptr ptr ptr ptr)
103 stdcall SignalFileOpen(ptr)
104 stdcall OpenAs_RunDLLW(long long wstr long)
105 stdcall Activate_RunDLL(ptr ptr wstr long)
@ -467,3 +467,4 @@
755 stdcall -noname PathIsEqualOrSubFolder(wstr wstr)
756 stub -noname DeleteFileThumbnail
757 stdcall -version=0x600+ DisplayNameOfW(ptr ptr long ptr long)
866 stdcall -version=0x600+ SHExtCoCreateInstance(wstr ptr ptr ptr ptr)

View file

@ -33,6 +33,42 @@ EXTERN_C BOOL PathIsExeW(LPCWSTR lpszPath);
typedef UINT_PTR (*SHELL_ExecuteW32)(const WCHAR *lpCmd, WCHAR *env, BOOL shWait,
const SHELLEXECUTEINFOW *sei, LPSHELLEXECUTEINFOW sei_out);
static int Win32ErrFromHInst(HINSTANCE hInst)
{
if ((SIZE_T)hInst > 32)
return ERROR_SUCCESS;
switch ((SIZE_T)hInst)
{
case SE_ERR_FNF:
case SE_ERR_PNF:
case SE_ERR_ACCESSDENIED:
case SE_ERR_OOM:
return (UINT)(SIZE_T)hInst;
case SE_ERR_SHARE:
return ERROR_SHARING_VIOLATION;
case SE_ERR_DDETIMEOUT:
case SE_ERR_DDEFAIL:
case SE_ERR_DDEBUSY:
return ERROR_DDE_FAIL;
case SE_ERR_DLLNOTFOUND:
return ERROR_DLL_NOT_FOUND;
//case SE_ERR_ASSOCINCOMPLETE: Note: Windows treats this as a success code
case SE_ERR_NOASSOC:
return ERROR_NO_ASSOCIATION;
case 10:
return ERROR_OLD_WIN_VERSION;
case 12:
return ERROR_APP_WRONG_OS;
case 15:
return ERROR_RMODE_APP;
case 16:
return ERROR_SINGLE_INSTANCE_APP;
case 20:
return ERROR_INVALID_DLL;
}
return -1;
}
// Is the current process a rundll32.exe?
static BOOL SHELL_InRunDllProcess(VOID)
{
@ -60,6 +96,57 @@ static UINT_PTR InvokeOpenWith(HWND hWndOwner, SHELLEXECUTEINFOW &sei)
return SUCCEEDED(hr) ? 42 : SE_ERR_NOASSOC;
}
static HRESULT InvokeShellExecuteHook(PCWSTR pszClsid, LPSHELLEXECUTEINFOW pSEI)
{
CComPtr<IUnknown> pUnk;
if (FAILED(SHExtCoCreateInstance(pszClsid, NULL, NULL, IID_PPV_ARG(IUnknown, &pUnk))))
return S_FALSE;
CComPtr<IShellExecuteHookW> pWide;
if (SUCCEEDED(pUnk->QueryInterface(IID_PPV_ARG(IShellExecuteHookW, &pWide))))
return pWide->Execute(pSEI);
HRESULT hr = S_FALSE;
#if 0 // TODO
CComPtr<IShellExecuteHookA> pAnsi;
if (SUCCEEDED(pUnk->QueryInterface(IID_PPV_ARG(IShellExecuteHookA, &pAnsi))))
{
SHELLEXECUTEINFOA sei = *(SHELLEXECUTEINFOA*)pSEI;
// TODO: Convert the strings
hr = pAnsi->Execute(sei);
pSEI->hProcess = sei.hProcess;
pSEI->hInstApp = sei.hInstApp;
}
#endif
return hr;
}
static HRESULT TryShellExecuteHooks(LPSHELLEXECUTEINFOW pSEI)
{
// https://devblogs.microsoft.com/oldnewthing/20080910-00/?p=20933 claims hooks
// were removed in Vista but this is incorrect, they are disabled by default.
// https://groups.google.com/g/microsoft.public.platformsdk.shell/c/ixdOX1--IKk
// says they are now controlled by the EnableShellExecuteHooks policy.
if (pSEI->fMask & SEE_MASK_NO_HOOKS)
return S_FALSE;
if (LOBYTE(GetVersion()) >= 6 && !SH32_InternalRestricted(REST_SH32_ENABLESHELLEXECUTEHOOKS))
return S_FALSE;
HRESULT hr = S_FALSE;
HKEY hKey;
LRESULT res = RegOpenKeyExW(HKEY_LOCAL_MACHINE, REGSTR_PATH_EXPLORER L"\\ShellExecuteHooks", 0, KEY_READ, &hKey);
if (res != ERROR_SUCCESS)
return S_FALSE;
for (UINT i = 0; hr == S_FALSE; ++i)
{
WCHAR szClsid[42];
DWORD cch = _countof(szClsid);
if (RegEnumValueW(hKey, i, szClsid, &cch, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
break;
hr = InvokeShellExecuteHook(szClsid, pSEI);
}
RegCloseKey(hKey);
return hr;
}
static void ParseNoTildeEffect(PWSTR &res, LPCWSTR &args, DWORD &len, DWORD &used, int argNum)
{
bool firstCharQuote = false;
@ -1968,6 +2055,20 @@ static BOOL SHELL_execute(LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc)
((sei_tmp.fMask & SEE_MASK_CLASSALL) == SEE_MASK_CLASSNAME) ?
debugstr_w(sei_tmp.lpClass) : "not used");
// Call hooks before expanding and resolving strings
HRESULT hr = TryShellExecuteHooks(sei);
if (hr != S_FALSE)
{
int err = Win32ErrFromHInst(sei->hInstApp);
if (err <= 0)
{
sei->hInstApp = (HINSTANCE)UlongToHandle(42);
return TRUE;
}
SetLastError(err);
return FALSE;
}
/* make copies of all path/command strings */
CHeapPtr<WCHAR, CLocalAllocator> wszApplicationName;
DWORD dwApplicationNameLen = MAX_PATH + 2;
@ -2083,16 +2184,6 @@ static BOOL SHELL_execute(LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc)
(sei_tmp.fMask & SEE_MASK_INVOKEIDLIST) != SEE_MASK_INVOKEIDLIST)
{
LPCITEMIDLIST pidl = (LPCITEMIDLIST)sei_tmp.lpIDList;
CComPtr<IShellExecuteHookW> pSEH;
HRESULT hr = SHBindToParent(pidl, IID_PPV_ARG(IShellExecuteHookW, &pSEH), NULL);
if (SUCCEEDED(hr))
{
hr = pSEH->Execute(&sei_tmp);
if (hr == S_OK)
return TRUE;
}
hr = SHGetNameAndFlagsW(pidl, SHGDN_FORPARSING, wszApplicationName, dwApplicationNameLen, NULL);
if (FAILED(hr))
{
@ -2117,7 +2208,7 @@ static BOOL SHELL_execute(LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc)
if ((sei_tmp.fMask & SEE_MASK_INVOKEIDLIST) == SEE_MASK_INVOKEIDLIST)
{
HRESULT hr = ShellExecute_ContextMenuVerb(&sei_tmp);
hr = ShellExecute_ContextMenuVerb(&sei_tmp);
if (SUCCEEDED(hr))
{
sei->hInstApp = (HINSTANCE)42;
@ -2157,7 +2248,7 @@ static BOOL SHELL_execute(LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc)
StrCmpNW(sei_tmp.lpFile, L"::{", 3) == 0))
{
CComHeapPtr<ITEMIDLIST> pidlParsed;
HRESULT hr = SHParseDisplayName(sei_tmp.lpFile, NULL, &pidlParsed, 0, NULL);
hr = SHParseDisplayName(sei_tmp.lpFile, NULL, &pidlParsed, 0, NULL);
if (SUCCEEDED(hr) && SHELL_InvokePidl(&sei_tmp, pidlParsed))
{
sei_tmp.hInstApp = (HINSTANCE)UlongToHandle(42);
@ -2352,7 +2443,7 @@ HINSTANCE WINAPI ShellExecuteA(HWND hWnd, LPCSTR lpVerb, LPCSTR lpFile,
debugstr_a(lpParameters), debugstr_a(lpDirectory), iShowCmd);
sei.cbSize = sizeof(sei);
sei.fMask = SEE_MASK_FLAG_NO_UI;
sei.fMask = SEE_MASK_FLAG_NO_UI | SEE_MASK_UNKNOWN_0x1000;
sei.hwnd = hWnd;
sei.lpVerb = lpVerb;
sei.lpFile = lpFile;
@ -2520,7 +2611,7 @@ HINSTANCE WINAPI ShellExecuteW(HWND hwnd, LPCWSTR lpVerb, LPCWSTR lpFile,
TRACE("\n");
sei.cbSize = sizeof(sei);
sei.fMask = SEE_MASK_FLAG_NO_UI;
sei.fMask = SEE_MASK_FLAG_NO_UI | SEE_MASK_UNKNOWN_0x1000;
sei.hwnd = hwnd;
sei.lpVerb = lpVerb;
sei.lpFile = lpFile;

View file

@ -305,3 +305,8 @@ DEFINE_POLICY( REST_USEDESKTOPINICACHE, "Explorer", "UseDesk
// NOTE: This is a SHDOCVW-only policy.
// DEFINE_POLICY( 0x50000024, "Explorer", "NoFileUrl" ),
#if defined(__REACTOS__) && defined(_SHELL32_)
// Internal SHELL32 policies (keep these at the end of the list)
DEFINE_POLICY( REST_SH32_ENABLESHELLEXECUTEHOOKS, "Explorer", "EnableShellExecuteHooks" ),
#endif

View file

@ -37,7 +37,10 @@ extern HMODULE huser32 DECLSPEC_HIDDEN;
extern HINSTANCE shell32_hInstance DECLSPEC_HIDDEN;
extern int (WINAPI* SHELL_StrCmpLogical)(PCWSTR s1, PCWSTR s2);
BOOL WINAPI Shell_GetImageLists(HIMAGELIST * lpBigList, HIMAGELIST * lpSmallList);
enum {
REST_SH32_ENABLESHELLEXECUTEHOOKS = 0x00060001, // POLID_EnableShellExecuteHooks
};
DWORD SH32_InternalRestricted(DWORD rest);
/* Iconcache */
#define INVALID_INDEX -1
@ -48,6 +51,7 @@ INT SIC_GetIconIndex (LPCWSTR sSourceFile, INT dwSourceIndex, DWORD dwFlags ) DE
extern INT ShellLargeIconSize;
extern INT ShellSmallIconSize;
extern INT ShellIconBPP;
BOOL WINAPI Shell_GetImageLists(HIMAGELIST * lpBigList, HIMAGELIST * lpSmallList);
/* Classes Root */
HRESULT HCR_GetProgIdKeyOfExtension(PCWSTR szExtension, PHKEY phKey, BOOL AllowFallback);

View file

@ -193,6 +193,17 @@ end:
return hres;
}
HRESULT WINAPI SHExtCoCreateInstance(
_In_opt_ LPCWSTR aclsid,
_In_opt_ const CLSID *clsid,
_In_opt_ LPUNKNOWN pUnkOuter,
_In_ REFIID refiid,
_Out_ LPVOID *ppv)
{
// TODO: Verify that this CLSID is allowed if REST_ENFORCESHELLEXTSECURITY is active
return SHCoCreateInstance(aclsid, clsid, pUnkOuter, refiid, ppv);
}
#ifndef __REACTOS__
/*************************************************************************
* DllGetClassObject [SHELL32.@]

View file

@ -163,7 +163,7 @@ static BOOL SHELL_QueryRestrictionsChanged(VOID)
* b: 98Lite 2.0 (which uses many of these policy keys) http://www.98lite.net/
* c: 'The Windows 95 Registry', by John Woram, 1996 MIS: Press
*/
DWORD WINAPI SHRestricted (RESTRICTIONS rest)
DWORD WINAPI SHRestricted(RESTRICTIONS rest)
{
TRACE("(0x%08lX)\n", rest);
@ -177,6 +177,12 @@ DWORD WINAPI SHRestricted (RESTRICTIONS rest)
return SHRestrictionLookup(rest, NULL, s_PolicyTable, g_RestValues);
}
DWORD SH32_InternalRestricted(DWORD rest)
{
/* assert((rest & 0x00060000) == 0x00060000) */
return SHRestricted(rest);
}
/*************************************************************************
* SHSettingsChanged [SHELL32.244]
*

View file

@ -670,7 +670,7 @@ public:
#define CmicFlagsToSeeFlags(flags) ((flags) & SEE_CMIC_COMMON_FLAGS)
static inline UINT SeeFlagsToCmicFlags(UINT flags)
{
if (flags & SEE_MASK_CLASSNAME)
if ((flags & (SEE_MASK_CLASSNAME | SEE_MASK_CLASSKEY)) == SEE_MASK_CLASSNAME)
flags &= ~(SEE_MASK_HASLINKNAME | SEE_MASK_HASTITLE);
return flags & SEE_CMIC_COMMON_FLAGS;
}

View file

@ -678,10 +678,12 @@ HRESULT WINAPI ShellExecCmdLine(
* CMINVOKECOMMANDINFOEX structure.
*/
#define SEE_MASK_UNKNOWN_0x1000 0x00001000 // FIXME: Name
#define SEE_MASK_NO_HOOKS 0x00002000 // https://www.yisu.com/ask/30968554.html
#define SEE_MASK_HASLINKNAME 0x00010000
#define SEE_MASK_FLAG_SEPVDM 0x00020000
#define SEE_MASK_USE_RESERVED 0x00040000
#define SEE_MASK_HASTITLE 0x00080000
#define SEE_MASK_FILEANDURL 0x00400000 // https://textslashplain.com/2019/09/25/web-to-app-communication-directinvoke/
HINSTANCE WINAPI
RealShellExecuteA(
@ -783,6 +785,13 @@ HRESULT WINAPI SHCreateDefClassObject(
void WINAPI SHFreeUnusedLibraries(void);
HRESULT WINAPI SHExtCoCreateInstance(
_In_opt_ LPCWSTR aclsid,
_In_opt_ const CLSID *clsid,
_In_opt_ LPUNKNOWN pUnkOuter,
_In_ REFIID refiid,
_Out_ LPVOID *ppv);
/* SHCreateLinks flags */
#define SHCLF_PREFIXNAME 0x01
#define SHCLF_CREATEONDESKTOP 0x02