diff --git a/dll/win32/shell32/shell32.spec b/dll/win32/shell32/shell32.spec index 759f4676e94..9a4f8b07c43 100644 --- a/dll/win32/shell32/shell32.spec +++ b/dll/win32/shell32/shell32.spec @@ -354,7 +354,7 @@ 354 stdcall SheShortenPathW(wstr long) 355 stdcall ShellAboutA(long str str long) 356 stdcall ShellAboutW(long wstr wstr long) -357 stdcall ShellExec_RunDLL(ptr ptr wstr long) +357 stdcall ShellExec_RunDLL(ptr ptr str long) ShellExec_RunDLLA 358 stdcall ShellExec_RunDLLA(ptr ptr str long) 359 stdcall ShellExec_RunDLLW(ptr ptr wstr long) 360 stdcall ShellExecuteA(long str str str str long) diff --git a/dll/win32/shell32/shlexec.cpp b/dll/win32/shell32/shlexec.cpp index f03db3de0e7..646a2a6c84c 100644 --- a/dll/win32/shell32/shlexec.cpp +++ b/dll/win32/shell32/shlexec.cpp @@ -2986,3 +2986,88 @@ RealShellExecuteW( lphProcess, 0); } + +// The common helper of ShellExec_RunDLLA and ShellExec_RunDLLW +static VOID +ShellExec_RunDLL_Helper( + _In_opt_ HWND hwnd, + _In_opt_ HINSTANCE hInstance, + _In_ PCWSTR pszCmdLine, + _In_ INT nCmdShow) +{ + TRACE("(%p, %p, %s, 0x%X)\n", hwnd, hInstance, wine_dbgstr_w(pszCmdLine), nCmdShow); + + if (!pszCmdLine || !*pszCmdLine) + return; + + // '?' enables us to specify the additional mask value + ULONG fNewMask = SEE_MASK_NOASYNC; + if (*pszCmdLine == L'?') // 1st question + { + INT MaskValue; + if (StrToIntExW(pszCmdLine + 1, STIF_SUPPORT_HEX, &MaskValue)) + fNewMask |= MaskValue; + + PCWSTR pch2ndQuestion = StrChrW(pszCmdLine + 1, L'?'); // 2nd question + if (pch2ndQuestion) + pszCmdLine = pch2ndQuestion + 1; + } + + WCHAR szPath[2 * MAX_PATH]; + if (PathProcessCommandAW(pszCmdLine, szPath, _countof(szPath), L'C') == -1) + StrCpyNW(szPath, pszCmdLine, _countof(szPath)); + + // Split arguments from the path + LPWSTR Args = PathGetArgsW(szPath); + if (*Args) + *(Args - 1) = UNICODE_NULL; + + PathUnquoteSpacesW(szPath); + + // Execute + SHELLEXECUTEINFOW execInfo = { sizeof(execInfo) }; + execInfo.fMask = fNewMask; + execInfo.hwnd = hwnd; + execInfo.lpFile = szPath; + execInfo.lpParameters = Args; + execInfo.nShow = nCmdShow; + if (!ShellExecuteExW(&execInfo)) + { + DWORD dwError = GetLastError(); + if (SHELL_InRunDllProcess()) // Is it a RUNDLL process? + ExitProcess(dwError); // Terminate it now + } +} + +/************************************************************************* + * ShellExec_RunDLLA [SHELL32.358] + * + * @see https://www.hexacorn.com/blog/2024/11/30/1-little-known-secret-of-shellexec_rundll/ + */ +EXTERN_C +VOID WINAPI +ShellExec_RunDLLA( + _In_opt_ HWND hwnd, + _In_opt_ HINSTANCE hInstance, + _In_ PCSTR pszCmdLine, + _In_ INT nCmdShow) +{ + CStringW strCmdLine = pszCmdLine; // Keep + ShellExec_RunDLL_Helper(hwnd, hInstance, strCmdLine, nCmdShow); +} + +/************************************************************************* + * ShellExec_RunDLLW [SHELL32.359] + * + * @see https://www.hexacorn.com/blog/2024/11/30/1-little-known-secret-of-shellexec_rundll/ + */ +EXTERN_C +VOID WINAPI +ShellExec_RunDLLW( + _In_opt_ HWND hwnd, + _In_opt_ HINSTANCE hInstance, + _In_ PCWSTR pszCmdLine, + _In_ INT nCmdShow) +{ + ShellExec_RunDLL_Helper(hwnd, hInstance, pszCmdLine, nCmdShow); +} diff --git a/dll/win32/shell32/stubs.cpp b/dll/win32/shell32/stubs.cpp index d54e80c14af..5fec3d1e342 100644 --- a/dll/win32/shell32/stubs.cpp +++ b/dll/win32/shell32/stubs.cpp @@ -297,36 +297,6 @@ ShellHookProc(INT iCode, WPARAM wParam, LPARAM lParam) return 0; } -/* - * Unimplemented - */ -EXTERN_C VOID -WINAPI -ShellExec_RunDLL(HWND hwnd, HINSTANCE hInstance, LPWSTR pszCmdLine, int nCmdShow) -{ - FIXME("ShellExec_RunDLL() stub\n"); -} - -/* - * Unimplemented - */ -EXTERN_C VOID -WINAPI -ShellExec_RunDLLA(HWND hwnd, HINSTANCE hInstance, LPSTR pszCmdLine, int nCmdShow) -{ - FIXME("ShellExec_RunDLLA() stub\n"); -} - -/* - * Unimplemented - */ -EXTERN_C VOID -WINAPI -ShellExec_RunDLLW(HWND hwnd, HINSTANCE hInstance, LPWSTR pszCmdLine, int nCmdShow) -{ - FIXME("ShellExec_RunDLLW() stub\n"); -} - /* * Unimplemented */ diff --git a/modules/rostests/apitests/shell32/CMakeLists.txt b/modules/rostests/apitests/shell32/CMakeLists.txt index 10ef4004e7c..39f1092027e 100644 --- a/modules/rostests/apitests/shell32/CMakeLists.txt +++ b/modules/rostests/apitests/shell32/CMakeLists.txt @@ -35,6 +35,7 @@ list(APPEND SOURCE SHRestricted.cpp SHShouldShowWizards.cpp She.cpp + ShellExec_RunDLL.cpp ShellExecCmdLine.cpp ShellExecuteEx.cpp ShellExecuteW.cpp diff --git a/modules/rostests/apitests/shell32/ShellExec_RunDLL.cpp b/modules/rostests/apitests/shell32/ShellExec_RunDLL.cpp new file mode 100644 index 00000000000..bea1d8b0682 --- /dev/null +++ b/modules/rostests/apitests/shell32/ShellExec_RunDLL.cpp @@ -0,0 +1,61 @@ +/* + * PROJECT: ReactOS API tests + * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) + * PURPOSE: Test for ShellExec_RunDLL + * COPYRIGHT: Copyright 2025 Katayama Hirofumi MZ + */ + +#include "shelltest.h" +#include "closewnd.h" +#include + +static WINDOW_LIST s_List1, s_List2; + +static VOID TEST_ShellExec_RunDLL(VOID) +{ + ShellExec_RunDLL(NULL, NULL, "?0?notepad.exe", SW_SHOWNORMAL); +} + +static VOID TEST_ShellExec_RunDLLA(VOID) +{ + ShellExec_RunDLLA(NULL, NULL, "?0?notepad.exe", SW_SHOWNORMAL); +} + +static VOID TEST_ShellExec_RunDLLW(VOID) +{ + ShellExec_RunDLLW(NULL, NULL, L"?0?notepad.exe", SW_SHOWNORMAL); +} + +static VOID CleanupWindowList(VOID) +{ + GetWindowListForClose(&s_List2); + CloseNewWindows(&s_List1, &s_List2); + FreeWindowList(&s_List1); + FreeWindowList(&s_List2); +} + +START_TEST(ShellExec_RunDLL) +{ + HWND hwndNotepad; + + GetWindowList(&s_List1); + TEST_ShellExec_RunDLL(); + Sleep(1000); + hwndNotepad = FindWindowW(L"Notepad", NULL); + ok(hwndNotepad != NULL, "Notepad not found\n"); + CleanupWindowList(); + + GetWindowList(&s_List1); + TEST_ShellExec_RunDLLA(); + Sleep(1000); + hwndNotepad = FindWindowW(L"Notepad", NULL); + ok(hwndNotepad != NULL, "Notepad not found\n"); + CleanupWindowList(); + + GetWindowList(&s_List1); + TEST_ShellExec_RunDLLW(); + Sleep(1000); + hwndNotepad = FindWindowW(L"Notepad", NULL); + ok(hwndNotepad != NULL, "Notepad not found\n"); + CleanupWindowList(); +} diff --git a/modules/rostests/apitests/shell32/testlist.c b/modules/rostests/apitests/shell32/testlist.c index 0da5972b358..134f01a7838 100644 --- a/modules/rostests/apitests/shell32/testlist.c +++ b/modules/rostests/apitests/shell32/testlist.c @@ -35,6 +35,7 @@ extern void func_SHCreateDataObject(void); extern void func_SHCreateFileDataObject(void); extern void func_SHCreateFileExtractIconW(void); extern void func_She(void); +extern void func_ShellExec_RunDLL(void); extern void func_ShellExecCmdLine(void); extern void func_ShellExecuteEx(void); extern void func_ShellExecuteW(void); @@ -83,6 +84,7 @@ const struct test winetest_testlist[] = { "SHCreateFileDataObject", func_SHCreateFileDataObject }, { "SHCreateFileExtractIconW", func_SHCreateFileExtractIconW }, { "She", func_She }, + { "ShellExec_RunDLL", func_ShellExec_RunDLL }, { "ShellExecCmdLine", func_ShellExecCmdLine }, { "ShellExecuteEx", func_ShellExecuteEx }, { "ShellExecuteW", func_ShellExecuteW }, diff --git a/sdk/include/reactos/undocshell.h b/sdk/include/reactos/undocshell.h index 3e685e08046..beb840c89e2 100644 --- a/sdk/include/reactos/undocshell.h +++ b/sdk/include/reactos/undocshell.h @@ -712,6 +712,27 @@ RealShellExecuteExW( _Out_opt_ PHANDLE lphProcess, _In_ DWORD dwFlags); +VOID WINAPI +ShellExec_RunDLL( + _In_opt_ HWND hwnd, + _In_opt_ HINSTANCE hInstance, + _In_ PCSTR pszCmdLine, + _In_ INT nCmdShow); + +VOID WINAPI +ShellExec_RunDLLA( + _In_opt_ HWND hwnd, + _In_opt_ HINSTANCE hInstance, + _In_ PCSTR pszCmdLine, + _In_ INT nCmdShow); + +VOID WINAPI +ShellExec_RunDLLW( + _In_opt_ HWND hwnd, + _In_opt_ HINSTANCE hInstance, + _In_ PCWSTR pszCmdLine, + _In_ INT nCmdShow); + /* RegisterShellHook types */ #define RSH_DEREGISTER 0 #define RSH_REGISTER 1