From d21c131fb543cd3c43632821dc96df930cb8f84e Mon Sep 17 00:00:00 2001 From: Peter Dons Tychsen Date: Mon, 11 Jan 2010 21:47:43 +0100 Subject: [PATCH] user32: Do not allow a change of capture if the currently capture window is a menu unless explicitly specified. --- dlls/user32/input.c | 15 ++++--- dlls/user32/tests/win.c | 92 +++++++++++++++++++++++++++++++++++++++++ server/queue.c | 6 +++ 3 files changed, 107 insertions(+), 6 deletions(-) diff --git a/dlls/user32/input.c b/dlls/user32/input.c index bfe80c4ca24..96c70387812 100644 --- a/dlls/user32/input.c +++ b/dlls/user32/input.c @@ -101,12 +101,15 @@ BOOL set_capture_window( HWND hwnd, UINT gui_flags, HWND *prev_ret ) } SERVER_END_REQ; - USER_Driver->pSetCapture( hwnd, gui_flags ); + if (ret) + { + USER_Driver->pSetCapture( hwnd, gui_flags ); - if (previous && previous != hwnd) - SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd ); + if (previous && previous != hwnd) + SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd ); - if (prev_ret) *prev_ret = previous; + if (prev_ret) *prev_ret = previous; + } return ret; } @@ -226,7 +229,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH SetCursorPos( INT x, INT y ) */ HWND WINAPI DECLSPEC_HOTPATCH SetCapture( HWND hwnd ) { - HWND previous; + HWND previous = 0; set_capture_window( hwnd, 0, &previous ); return previous; @@ -241,7 +244,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH ReleaseCapture(void) BOOL ret = set_capture_window( 0, 0, NULL ); /* Somebody may have missed some mouse movements */ - mouse_event( MOUSEEVENTF_MOVE, 0, 0, 0, 0 ); + if (ret) mouse_event( MOUSEEVENTF_MOVE, 0, 0, 0, 0 ); return ret; } diff --git a/dlls/user32/tests/win.c b/dlls/user32/tests/win.c index 24e6ac28dbd..34ff9d0bbc8 100644 --- a/dlls/user32/tests/win.c +++ b/dlls/user32/tests/win.c @@ -2697,6 +2697,97 @@ static void test_capture_3(HWND hwnd1, HWND hwnd2) ok (ret, "releasecapture did not return TRUE after second try.\n"); } +static LRESULT CALLBACK test_capture_4_proc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + GUITHREADINFO gti; + HWND cap_wnd, cap_wnd2, set_cap_wnd; + BOOL status; + switch (msg) + { + case WM_CAPTURECHANGED: + + /* now try to release capture from menu. this should fail */ + memset(>i, 0, sizeof(GUITHREADINFO)); + gti.cbSize = sizeof(GUITHREADINFO); + status = GetGUIThreadInfo(GetCurrentThreadId(), >i); + ok(status, "GetGUIThreadInfo() failed!\n"); + cap_wnd = GetCapture(); + ok(gti.flags & GUI_INMENUMODE, "Thread info incorrect (flags=%08X)!\n", gti.flags); + + /* check that re-setting the capture for the menu fails */ + set_cap_wnd = SetCapture(cap_wnd); + ok(!set_cap_wnd, "SetCapture should have failed!\n"); + + /* check that SetCapture fails for another window and that it does not touch the error code */ + set_cap_wnd = SetCapture(hWnd); + ok(!set_cap_wnd, "ReleaseCapture should have failed!\n"); + + /* check that ReleaseCapture fails and does not touch the error code */ + status = ReleaseCapture(); + ok(!status, "ReleaseCapture should have failed!\n"); + + /* check that thread info did not change */ + memset(>i, 0, sizeof(GUITHREADINFO)); + gti.cbSize = sizeof(GUITHREADINFO); + status = GetGUIThreadInfo(GetCurrentThreadId(), >i); + ok(status, "GetGUIThreadInfo() failed!\n"); + ok(gti.flags & GUI_INMENUMODE, "Thread info incorrect (flags=%08X)!\n", gti.flags); + + /* verify that no capture change took place */ + cap_wnd2 = GetCapture(); + ok(cap_wnd2 == cap_wnd, "Capture changed!\n"); + + /* we are done. kill the window */ + DestroyWindow(hWnd); + break; + + default: + return( DefWindowProcA( hWnd, msg, wParam, lParam ) ); + } + return 0; +} + +/* Test that no-one can mess around with the current capture while a menu is open */ +static void test_capture_4(void) +{ + BOOL ret; + HMENU hmenu; + HWND hwnd; + WNDCLASSA wclass; + HINSTANCE hInstance = GetModuleHandleA( NULL ); + wclass.lpszClassName = "TestCapture4Class"; + wclass.style = CS_HREDRAW | CS_VREDRAW; + wclass.lpfnWndProc = test_capture_4_proc; + wclass.hInstance = hInstance; + wclass.hIcon = LoadIconA( 0, IDI_APPLICATION ); + wclass.hCursor = LoadCursorA( NULL, IDC_ARROW ); + wclass.hbrBackground = (HBRUSH)( COLOR_WINDOW + 1 ); + wclass.lpszMenuName = 0; + wclass.cbClsExtra = 0; + wclass.cbWndExtra = 0; + assert (RegisterClassA( &wclass )); + assert (hwnd = CreateWindowA( wclass.lpszClassName, "MenuTest", + WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, + 400, 200, NULL, NULL, hInstance, NULL) ); + ok(hwnd != NULL, "CreateWindowEx failed with error %d\n", GetLastError()); + if (!hwnd) return; + hmenu = CreatePopupMenu(); + + ret = AppendMenuA( hmenu, MF_STRING, 1, "winetest2"); + ok( ret, "AppendMenA has failed!\n"); + + /* set main window to have initial capture */ + SetCapture(hwnd); + + /* create popup (it will self-destruct) */ + ret = TrackPopupMenu(hmenu, 0x100, 100,100, 0, hwnd, NULL); + ok( ret == 0, "TrackPopupMenu returned %d expected zero\n", ret); + + /* clean up */ + DestroyMenu(hmenu); + DestroyWindow(hwnd); +} + /* PeekMessage wrapper that ignores the messages we don't care about */ static BOOL peek_message( MSG *msg ) { @@ -5821,6 +5912,7 @@ START_TEST(win) test_capture_1(); test_capture_2(); test_capture_3(hwndMain, hwndMain2); + test_capture_4(); test_CreateWindow(); test_parent_owner(); diff --git a/server/queue.c b/server/queue.c index d880dff2317..cfe981877da 100644 --- a/server/queue.c +++ b/server/queue.c @@ -2145,6 +2145,12 @@ DECL_HANDLER(set_capture_window) { struct thread_input *input = queue->input; + /* if in menu mode, reject all requests to change focus, except if the menu bit is set */ + if (input->menu_owner && !(req->flags & CAPTURE_MENU)) + { + set_error(STATUS_ACCESS_DENIED); + return; + } reply->previous = input->capture; input->capture = get_user_full_handle( req->handle ); input->menu_owner = (req->flags & CAPTURE_MENU) ? input->capture : 0;