[NTUSER][USER32_APITEST] Support ShowWindow.SW_FORCEMINIMIZE (#8261)

One more step to support ghost windows.
JIRA issue: CORE-19671
- Add IntForceMinimizeWindow helper
  function.
- Use IntForceMinimizeWindow in
  co_WinPosShowWindow function.
- Enhance ShowWindow testcase in
  user32_apitest.
- Add mask to the return value of
  GetWindowLong.
This commit is contained in:
Katayama Hirofumi MZ
2025-07-20 13:14:01 +09:00
committed by GitHub
parent 6ebf15d838
commit d958a24678
3 changed files with 205 additions and 5 deletions

View File

@@ -2,10 +2,11 @@
* PROJECT: ReactOS API tests
* LICENSE: LGPL-2.1+ (https://spdx.org/licenses/LGPL-2.1+)
* PURPOSE: Tests for ShowWindow
* COPYRIGHT: Copyright 2021 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
* COPYRIGHT: Copyright 2021-2025 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
*/
#include "precomp.h"
#include <versionhelpers.h>
typedef struct TEST_ENTRY
{
@@ -164,7 +165,7 @@ WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
return 0;
}
START_TEST(ShowWindow)
static VOID Test_ShowWindow_Main(VOID)
{
WNDCLASSA wc;
UINT iTest;
@@ -181,3 +182,164 @@ START_TEST(ShowWindow)
DoTestEntry(&s_entries[iTest]);
}
}
#define RED RGB(255, 0, 0)
static COLORREF CheckColor(VOID)
{
HDC hDC = GetDC(NULL);
COLORREF color = GetPixel(hDC, 100, 100);
ReleaseDC(NULL, hDC);
return color;
}
static DWORD WINAPI
ForceMinimizeThreadFunc(LPVOID arg)
{
BOOL ret;
HWND hwnd = (HWND)arg;
DWORD style, exstyle;
Sleep(100);
ok_long(CheckColor(), RED);
SetWindowLongPtrW(hwnd, GWL_EXSTYLE, WS_EX_MAKEVISIBLEWHENUNGHOSTED);
ret = ShowWindow(hwnd, SW_FORCEMINIMIZE);
Sleep(100);
ok(ret != FALSE, "ret was FALSE\n");
ok(CheckColor() != RED, "Color was red\n");
style = GetWindowLongPtrW(hwnd, GWL_STYLE);
if (IsWindowsVistaOrGreater())
ok((style & (WS_POPUP | WS_MINIMIZE | WS_VISIBLE)) == (WS_POPUP | WS_MINIMIZE | WS_VISIBLE), "style was 0x%08lX\n", style);
else
ok((style & (WS_POPUP | WS_MINIMIZE | WS_VISIBLE)) == (WS_POPUP), "style was 0x%08lX\n", style);
exstyle = GetWindowLongPtrW(hwnd, GWL_EXSTYLE);
ok_long(exstyle, 0);
ShowWindow(hwnd, SW_MINIMIZE);
Sleep(100);
ok(CheckColor() != RED, "Color was red\n");
SetWindowLongPtrW(hwnd, GWL_EXSTYLE, WS_EX_MAKEVISIBLEWHENUNGHOSTED);
ret = ShowWindow(hwnd, SW_FORCEMINIMIZE);
Sleep(100);
ok(ret != FALSE, "ret was FALSE\n");
ok(CheckColor() != RED, "Color was red\n");
style = GetWindowLongPtrW(hwnd, GWL_STYLE);
ok((style & (WS_POPUP | WS_MINIMIZE | WS_VISIBLE)) == (WS_POPUP | WS_MINIMIZE | WS_VISIBLE), "style was 0x%08lX\n", style);
exstyle = GetWindowLongPtrW(hwnd, GWL_EXSTYLE);
ok_long(exstyle, 0);
ShowWindow(hwnd, SW_SHOWNORMAL);
Sleep(100);
ok_long(CheckColor(), RED);
SetWindowLongPtrW(hwnd, GWL_EXSTYLE, WS_EX_MAKEVISIBLEWHENUNGHOSTED);
ret = ShowWindow(hwnd, SW_FORCEMINIMIZE);
Sleep(100);
ok(ret != FALSE, "ret was FALSE\n");
ok(CheckColor() != RED, "Color was red\n");
style = GetWindowLongPtrW(hwnd, GWL_STYLE);
if (IsWindowsVistaOrGreater())
ok((style & (WS_POPUP | WS_MINIMIZE | WS_VISIBLE)) == (WS_POPUP | WS_MINIMIZE | WS_VISIBLE), "style was 0x%08lX\n", style);
else
ok((style & (WS_POPUP | WS_MINIMIZE | WS_VISIBLE)) == (WS_POPUP), "style was 0x%08lX\n", style);
exstyle = GetWindowLongPtrW(hwnd, GWL_EXSTYLE);
ok_long(exstyle, 0);
SetWindowLongPtrW(hwnd, GWL_EXSTYLE, WS_EX_MAKEVISIBLEWHENUNGHOSTED);
ret = ShowWindow(hwnd, SW_FORCEMINIMIZE);
Sleep(100);
if (IsWindowsVistaOrGreater())
ok(ret != FALSE, "ret was FALSE\n");
else
ok_bool_false(ret, "Return was");
ok(CheckColor() != RED, "Color was red\n");
style = GetWindowLongPtrW(hwnd, GWL_STYLE);
if (IsWindowsVistaOrGreater())
ok((style & (WS_POPUP | WS_MINIMIZE | WS_VISIBLE)) == (WS_POPUP | WS_MINIMIZE | WS_VISIBLE), "style was 0x%08lX\n", style);
else
ok((style & (WS_POPUP | WS_MINIMIZE | WS_VISIBLE)) == (WS_POPUP), "style was 0x%08lX\n", style);
exstyle = GetWindowLongPtrW(hwnd, GWL_EXSTYLE);
ok_long(exstyle, 0);
PostMessageW(hwnd, WM_CLOSE, 0, 0);
return 0;
}
static LRESULT CALLBACK
ForceMinimizeWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
return 0;
}
static VOID
Test_WS_FORCEMINIMIZE_Sub(HBRUSH hbr)
{
WNDCLASSW wc;
HWND hwnd;
DWORD style;
HINSTANCE hInstance = GetModuleHandleW(NULL);
DWORD dwThreadId;
HANDLE hThread;
MSG msg;
ZeroMemory(&wc, sizeof(wc));
wc.lpfnWndProc = ForceMinimizeWndProc;
wc.hInstance = hInstance;
wc.hIcon = LoadIconW(NULL, (PCWSTR)IDI_APPLICATION);
wc.hCursor = LoadCursorW(NULL, (PCWSTR)IDC_ARROW);
wc.hbrBackground = hbr;
wc.lpszClassName = L"SW_FORCEMINIMIZE";
if (!RegisterClassW(&wc))
{
skip("RegisterClassW failed\n");
return;
}
style = WS_POPUP | WS_VISIBLE;
hwnd = CreateWindowExW(0, L"SW_FORCEMINIMIZE", L"SW_FORCEMINIMIZE", style,
50, 50, 100, 100, NULL, NULL, hInstance, NULL);
if (!hwnd)
{
skip("CreateWindowExW failed\n");
return;
}
hThread = CreateThread(NULL, 0, ForceMinimizeThreadFunc, hwnd, 0, &dwThreadId);
if (!hThread)
{
skip("CreateThread failed\n");
DestroyWindow(hwnd);
return;
}
CloseHandle(hThread);
while (GetMessageW(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
static VOID
Test_WS_FORCEMINIMIZE()
{
HBRUSH hbr = CreateSolidBrush(RED);
Test_WS_FORCEMINIMIZE_Sub(hbr);
DeleteObject(hbr);
}
START_TEST(ShowWindow)
{
Test_ShowWindow_Main();
Test_WS_FORCEMINIMIZE();
}

View File

@@ -2586,9 +2586,36 @@ co_WinPosMinMaximize(PWND Wnd, UINT ShowFlag, RECT* NewPos)
return SwpFlags;
}
// SW_FORCEMINIMIZE
void IntForceMinimizeWindow(PWND pWnd)
{
HRGN hRgn;
PREGION pRgn;
if ((pWnd->style & (WS_MINIMIZE | WS_VISIBLE)) != WS_VISIBLE)
return;
if (pWnd->state & WNDS_DESTROYED)
return;
pWnd->ExStyle &= ~WS_EX_MAKEVISIBLEWHENUNGHOSTED;
IntSetStyle(pWnd, 0, WS_VISIBLE);
// Invalidate and redraw the window region
hRgn = GreCreateRectRgnIndirect(&pWnd->rcWindow);
pRgn = REGION_LockRgn(hRgn);
co_UserRedrawWindow(UserGetDesktopWindow(), NULL, pRgn, RDW_ALLCHILDREN | RDW_ERASE | RDW_INVALIDATE);
REGION_UnlockRgn(pRgn);
GreDeleteObject(hRgn);
// Activate the other window if necessary
if (pWnd->spwndParent == pWnd->head.rpdesk->pDeskInfo->spwnd)
co_WinPosActivateOtherWindow(pWnd);
}
/*
ShowWindow does not set SWP_FRAMECHANGED!!! Fix wine msg test_SetParent:WmSetParentSeq_2:23 wParam bits!
Win: xxxShowWindow
*/
BOOLEAN FASTCALL
co_WinPosShowWindow(PWND Wnd, INT Cmd)
@@ -2661,7 +2688,10 @@ co_WinPosShowWindow(PWND Wnd, INT Cmd)
break;
}
case SW_FORCEMINIMIZE: /* FIXME: Does not work if thread is hung. */
case SW_FORCEMINIMIZE:
IntForceMinimizeWindow(Wnd);
return WasVisible;
case SW_SHOWMINNOACTIVE:
Swp |= SWP_NOACTIVATE | SWP_NOZORDER;
/* Fall through. */

View File

@@ -1077,6 +1077,14 @@ GetClassWord(
return retvalue;
}
#define PUBLIC_EXSTYLE ( \
WS_EX_NOACTIVATE | WS_EX_COMPOSITED | WS_EX_LAYOUTRTL | WS_EX_NOINHERITLAYOUT | \
WS_EX_LAYERED | WS_EX_APPWINDOW | WS_EX_STATICEDGE | WS_EX_CONTROLPARENT | \
WS_EX_LEFTSCROLLBAR | WS_EX_RTLREADING | WS_EX_RIGHT | WS_EX_CONTEXTHELP | \
WS_EX_CLIENTEDGE | WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_MDICHILD | \
WS_EX_TRANSPARENT | WS_EX_ACCEPTFILES | WS_EX_TOPMOST | WS_EX_NOPARENTNOTIFY | \
WS_EX_DRAGDETECT | WS_EX_DLGMODALFRAME \
)
LONG_PTR IntGetWindowLong( HWND hwnd, INT offset, UINT size, BOOL unicode )
{
@@ -1116,7 +1124,7 @@ LONG_PTR IntGetWindowLong( HWND hwnd, INT offset, UINT size, BOOL unicode )
{
case GWLP_USERDATA: retvalue = wndPtr->dwUserData; break;
case GWL_STYLE: retvalue = wndPtr->style; break;
case GWL_EXSTYLE: retvalue = wndPtr->ExStyle; break;
case GWL_EXSTYLE: retvalue = (wndPtr->ExStyle & PUBLIC_EXSTYLE); break;
case GWLP_ID: retvalue = wndPtr->IDMenu; break;
case GWLP_HINSTANCE: retvalue = (ULONG_PTR)wndPtr->hModule; break;
#if 0