https://git.reactos.org/?p=reactos.git;a=commitdiff;h=ea87f9102b1c4484d4240708e828e236aad4ebb6
commit ea87f9102b1c4484d4240708e828e236aad4ebb6 Author: Whindmar Saksit <whinds...@proton.me> AuthorDate: Thu Oct 24 16:44:53 2024 +0200 Commit: GitHub <nore...@github.com> CommitDate: Thu Oct 24 16:44:53 2024 +0200 [SHELL32][SHELL32_APITEST] Fix ShellExecuteEx IDLIST folder (#7464) This is mainly to fix the shell32 apitest, a better way to handle SEE_MASK_IDLIST should be investigated in the future. --- dll/win32/shell32/shlexec.cpp | 20 +++++-- .../rostests/apitests/shell32/ShellExecuteEx.cpp | 42 +++++++++----- modules/rostests/apitests/shell32/closewnd.cpp | 64 ++++++++++++++++------ modules/rostests/apitests/shell32/closewnd.h | 1 + 4 files changed, 90 insertions(+), 37 deletions(-) diff --git a/dll/win32/shell32/shlexec.cpp b/dll/win32/shell32/shlexec.cpp index 2e1d0e2a443..3a665288535 100644 --- a/dll/win32/shell32/shlexec.cpp +++ b/dll/win32/shell32/shlexec.cpp @@ -287,11 +287,12 @@ static BOOL SHELL_ArgifyW(WCHAR* out, DWORD len, const WCHAR* fmt, const WCHAR* else cmd = lpFile; - used += wcslen(cmd); + SIZE_T cmdlen = wcslen(cmd); + used += cmdlen; if (used < len) { wcscpy(res, cmd); - res += wcslen(cmd); + res += cmdlen; } } found_p1 = TRUE; @@ -1730,7 +1731,7 @@ static BOOL SHELL_translate_idlist(LPSHELLEXECUTEINFOW sei, LPWSTR wszParameters sei->fMask &= ~SEE_MASK_INVOKEIDLIST; } else { - WCHAR target[MAX_PATH]; + WCHAR target[max(MAX_PATH, _countof(buffer))]; DWORD attribs; DWORD resultLen; /* Check if we're executing a directory and if so use the @@ -1744,10 +1745,19 @@ static BOOL SHELL_translate_idlist(LPSHELLEXECUTEINFOW sei, LPWSTR wszParameters buffer, sizeof(buffer))) { SHELL_ArgifyW(wszApplicationName, dwApplicationNameLen, buffer, target, (LPITEMIDLIST)sei->lpIDList, NULL, &resultLen, - (sei->lpDirectory && *sei->lpDirectory) ? sei->lpDirectory : NULL); + !StrIsNullOrEmpty(sei->lpDirectory) ? sei->lpDirectory : NULL); if (resultLen > dwApplicationNameLen) - ERR("Argify buffer not large enough... truncating\n"); + ERR("Argify buffer not large enough... truncating\n"); // FIXME: Report this to the caller? appKnownSingular = FALSE; + // HACKFIX: We really want the !appKnownSingular code in SHELL_execute to split the + // parameters for us but we cannot guarantee that the exe in the registry is quoted. + // We have now turned 'explorer.exe "%1" into 'explorer.exe "c:\path\from\pidl"' and + // need to split to application and parameters. + LPCWSTR params = PathGetArgsW(wszApplicationName); + lstrcpynW(wszParameters, params, parametersLen); + PathRemoveArgsW(wszApplicationName); + PathUnquoteSpacesW(wszApplicationName); + appKnownSingular = TRUE; } sei->fMask &= ~SEE_MASK_INVOKEIDLIST; } diff --git a/modules/rostests/apitests/shell32/ShellExecuteEx.cpp b/modules/rostests/apitests/shell32/ShellExecuteEx.cpp index 2c26b33be37..64759a49c1e 100644 --- a/modules/rostests/apitests/shell32/ShellExecuteEx.cpp +++ b/modules/rostests/apitests/shell32/ShellExecuteEx.cpp @@ -14,6 +14,7 @@ #include <stdio.h> #include <strsafe.h> #include <versionhelpers.h> +#include <shellutils.h> #include "shell32_apitest_sub.h" static WCHAR s_win_dir[MAX_PATH]; @@ -313,8 +314,7 @@ static BOOL TEST_Start(void) static void TEST_End(void) { - Sleep(500); - GetWindowList(&s_List2); + GetWindowListForClose(&s_List2); CloseNewWindows(&s_List1, &s_List2); FreeWindowList(&s_List1); FreeWindowList(&s_List2); @@ -368,26 +368,40 @@ static void test_properties() static void test_sei_lpIDList() { - if (IsWindowsVistaOrGreater()) + // Note: SEE_MASK_FLAG_NO_UI prevents the test from blocking with a MessageBox + WCHAR path[MAX_PATH]; + + /* This tests ShellExecuteEx with lpIDList for explorer C:\ */ + GetSystemDirectoryW(path, _countof(path)); + PathStripToRootW(path); + LPITEMIDLIST pidl = ILCreateFromPathW(path); + if (!pidl) { - skip("Vista+\n"); + skip("Unable to initialize test\n"); return; } - /* This tests ShellExecuteEx with lpIDList for explorer C:\ */ - - /* ITEMIDLIST for CLSID of 'My Computer' followed by PIDL for 'C:\' */ - BYTE lpitemidlist[30] = { 0x14, 0, 0x1f, 0, 0xe0, 0x4f, 0xd0, 0x20, 0xea, - 0x3a, 0x69, 0x10, 0xa2, 0xd8, 0x08, 0, 0x2b, 0x30, 0x30, 0x9d, // My Computer - 0x8, 0, 0x23, 0x43, 0x3a, 0x5c, 0x5c, 0, 0, 0,}; // C:\\ + NUL-NUL ending - SHELLEXECUTEINFOW ShellExecInfo = { sizeof(ShellExecInfo) }; - ShellExecInfo.fMask = SEE_MASK_IDLIST; - ShellExecInfo.hwnd = NULL; ShellExecInfo.nShow = SW_SHOWNORMAL; - ShellExecInfo.lpIDList = lpitemidlist; + ShellExecInfo.fMask = SEE_MASK_IDLIST | SEE_MASK_FLAG_NO_UI | SEE_MASK_FLAG_DDEWAIT; + ShellExecInfo.lpIDList = pidl; BOOL ret = ShellExecuteExW(&ShellExecInfo); ok_int(ret, TRUE); + ILFree(pidl); + + /* This tests ShellExecuteEx with lpIDList going through IContextMenu */ + CCoInit ComInit; + pidl = SHCloneSpecialIDList(NULL, CSIDL_PROFILE, TRUE); + if (!pidl) + { + skip("Unable to initialize test\n"); + return; + } + ShellExecInfo.fMask = SEE_MASK_INVOKEIDLIST | SEE_MASK_FLAG_NO_UI | SEE_MASK_FLAG_DDEWAIT; + ShellExecInfo.lpIDList = pidl; + ret = ShellExecuteExW(&ShellExecInfo); + ok_int(ret, TRUE); + ILFree(pidl); } static BOOL diff --git a/modules/rostests/apitests/shell32/closewnd.cpp b/modules/rostests/apitests/shell32/closewnd.cpp index d868f65fd79..2a7d8991b5c 100644 --- a/modules/rostests/apitests/shell32/closewnd.cpp +++ b/modules/rostests/apitests/shell32/closewnd.cpp @@ -37,6 +37,33 @@ void GetWindowList(PWINDOW_LIST pList) EnumWindows(EnumWindowsProc, (LPARAM)pList); } +void GetWindowListForClose(PWINDOW_LIST pList) +{ + for (UINT tries = 5; tries--;) + { + if (tries) + FreeWindowList(pList); + GetWindowList(pList); + Sleep(500); + WINDOW_LIST list; + GetWindowList(&list); + SIZE_T count = list.m_chWnds; + FreeWindowList(&list); + if (count == pList->m_chWnds) + break; + } +} + +static HWND FindInWindowList(const WINDOW_LIST &list, HWND hWnd) +{ + for (SIZE_T i = 0; i < list.m_chWnds; ++i) + { + if (list.m_phWnds[i] == hWnd) + return hWnd; + } + return NULL; +} + HWND FindNewWindow(PWINDOW_LIST List1, PWINDOW_LIST List2) { for (SIZE_T i2 = 0; i2 < List2->m_chWnds; ++i2) @@ -61,30 +88,31 @@ HWND FindNewWindow(PWINDOW_LIST List1, PWINDOW_LIST List2) return NULL; } -#define TRIALS_COUNT 8 +static void WaitForForegroundWindow(HWND hWnd, UINT wait = 250) +{ + for (UINT waited = 0, interval = 50; waited < wait; waited += interval) + { + if (GetForegroundWindow() == hWnd || !IsWindowVisible(hWnd)) + return; + Sleep(interval); + } +} void CloseNewWindows(PWINDOW_LIST List1, PWINDOW_LIST List2) { - INT cDiff = List2->m_chWnds - List1->m_chWnds; - for (INT j = 0; j < cDiff; ++j) + for (SIZE_T i = 0; i < List2->m_chWnds; ++i) { - HWND hWnd = FindNewWindow(List1, List2); - if (!hWnd) - break; - - for (INT i = 0; i < TRIALS_COUNT; ++i) - { - if (!IsWindow(hWnd)) - break; + HWND hWnd = List2->m_phWnds[i]; + if (!IsWindow(hWnd) || FindInWindowList(*List1, hWnd)) + continue; - SwitchToThisWindow(hWnd, TRUE); + SwitchToThisWindow(hWnd, TRUE); + WaitForForegroundWindow(hWnd); - // Alt+F4 - keybd_event(VK_MENU, 0x38, 0, 0); - keybd_event(VK_F4, 0x3E, 0, 0); - keybd_event(VK_F4, 0x3E, KEYEVENTF_KEYUP, 0); - keybd_event(VK_MENU, 0x38, KEYEVENTF_KEYUP, 0); - Sleep(100); + if (!PostMessageW(hWnd, WM_SYSCOMMAND, SC_CLOSE, 0)) + { + DWORD_PTR result; + SendMessageTimeoutW(hWnd, WM_SYSCOMMAND, SC_CLOSE, 0, 0, 3000, &result); } } } diff --git a/modules/rostests/apitests/shell32/closewnd.h b/modules/rostests/apitests/shell32/closewnd.h index 367c058b054..096ce88c1fe 100644 --- a/modules/rostests/apitests/shell32/closewnd.h +++ b/modules/rostests/apitests/shell32/closewnd.h @@ -14,6 +14,7 @@ typedef struct WINDOW_LIST } WINDOW_LIST, *PWINDOW_LIST; void GetWindowList(PWINDOW_LIST pList); +void GetWindowListForClose(PWINDOW_LIST pList); HWND FindNewWindow(PWINDOW_LIST List1, PWINDOW_LIST List2); void CloseNewWindows(PWINDOW_LIST List1, PWINDOW_LIST List2); void FreeWindowList(PWINDOW_LIST pList);