Hello A. I was afraid you were going to say that! :-)
My initial change actually did that, but then i ran into the problem that i need the request to fail, but not set an error-code. I could push/pop the current error code in user32 but that seemed a bit ugly. I could also single out that error code and then only translate the others. But what error code should be used then? Also seemed a bit ugly. Instead i have added a new member to the response to check if the request was "allowed". Seemed like the cleanest solution. Is this acceptable? I have attached a new patch which does this. Thanks, /pedro On Fri, 2010-01-08 at 10:06 +0100, Alexandre Julliard wrote: > Peter Dons Tychsen <donpe...@tdcadsl.dk> writes: > > > @@ -85,6 +85,17 @@ BOOL set_capture_window( HWND hwnd, UINT gui_flags, HWND > > *prev_ret ) > > HWND previous = 0; > > UINT flags = 0; > > BOOL ret; > > + GUITHREADINFO info; > > + if(!GetGUIThreadInfo(GetCurrentThreadId(), &info)) > > + { > > + return FALSE; > > + } > > + > > + /* if in menu mode, reject all requests to change focus, except if the > > menu bit is set */ > > + if((info.flags & GUI_INMENUMODE) && !(gui_flags & GUI_INMENUMODE)) > > + { > > + return FALSE; > > + } > > This should be handled on the server side. >
diff --git a/dlls/user32/input.c b/dlls/user32/input.c index bfe80c4..7fcfe40 100644 --- a/dlls/user32/input.c +++ b/dlls/user32/input.c @@ -97,15 +97,19 @@ BOOL set_capture_window( HWND hwnd, UINT gui_flags, HWND *prev_ret ) { previous = wine_server_ptr_handle( reply->previous ); hwnd = wine_server_ptr_handle( reply->full_handle ); + ret = reply->allowed; } } SERVER_END_REQ; - USER_Driver->pSetCapture( hwnd, gui_flags ); - - if (previous && previous != hwnd) - SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd ); + if(ret) + { + USER_Driver->pSetCapture( hwnd, gui_flags ); + if (previous && previous != hwnd) + SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd ); + } + if (prev_ret) *prev_ret = previous; return ret; } @@ -228,7 +232,11 @@ HWND WINAPI DECLSPEC_HOTPATCH SetCapture( HWND hwnd ) { HWND previous; - set_capture_window( hwnd, 0, &previous ); + if(!set_capture_window( hwnd, 0, &previous )) + { + return NULL; + } + return previous; } @@ -239,11 +247,15 @@ HWND WINAPI DECLSPEC_HOTPATCH SetCapture( HWND hwnd ) BOOL WINAPI DECLSPEC_HOTPATCH ReleaseCapture(void) { BOOL ret = set_capture_window( 0, 0, NULL ); + if(!ret) + { + return FALSE; + } /* Somebody may have missed some mouse movements */ mouse_event( MOUSEEVENTF_MOVE, 0, 0, 0, 0 ); - return ret; + return TRUE; } diff --git a/dlls/user32/tests/win.c b/dlls/user32/tests/win.c index 24e6ac2..9175abd 100644 --- a/dlls/user32/tests/win.c +++ b/dlls/user32/tests/win.c @@ -2697,6 +2697,104 @@ 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; + DWORD err; + 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 */ + SetLastError(0xdeadbeef); + set_cap_wnd = SetCapture(hWnd); + err = GetLastError(); + ok(!set_cap_wnd, "ReleaseCapture should have failed!\n"); + ok(err == 0xdeadbeef, "Bad error-code from SetCapture. got %08X expected %08X\n", err, 0xdeadbeef); + + /* check that ReleaseCapture fails and does not touch the error code */ + status = ReleaseCapture(); + err = GetLastError(); + ok(!status, "ReleaseCapture should have failed!\n"); + err = GetLastError(); + ok(err == 0xdeadbeef, "Bad error-code from SetCapture. got %08X expected %08X\n", err, 0xdeadbeef); + + /* 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 +5919,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/include/wine/server_protocol.h b/include/wine/server_protocol.h index 6a860df..06c222e 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -3751,6 +3751,8 @@ struct set_capture_window_reply struct reply_header __header; user_handle_t previous; user_handle_t full_handle; + int allowed; + char __pad_20[4]; }; #define CAPTURE_MENU 0x01 #define CAPTURE_MOVESIZE 0x02 @@ -5394,6 +5396,6 @@ union generic_reply struct free_user_handle_reply free_user_handle_reply; }; -#define SERVER_PROTOCOL_VERSION 394 +#define SERVER_PROTOCOL_VERSION 396 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */ diff --git a/server/protocol.def b/server/protocol.def index bba6788..4efed89 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -2656,6 +2656,7 @@ enum message_type @REPLY user_handle_t previous; /* handle to the previous capture window */ user_handle_t full_handle; /* full 32-bit handle of new capture window */ + int allowed; /* is the capture change allowed? */ @END #define CAPTURE_MENU 0x01 /* capture is for a menu */ #define CAPTURE_MOVESIZE 0x02 /* capture is for moving/resizing */ diff --git a/server/queue.c b/server/queue.c index d880dff..e5aa7b1 100644 --- a/server/queue.c +++ b/server/queue.c @@ -2141,14 +2141,21 @@ DECL_HANDLER(set_capture_window) struct msg_queue *queue = get_current_queue(); reply->previous = reply->full_handle = 0; + reply->allowed = 0; if (queue && check_queue_input_window( queue, req->handle )) { struct thread_input *input = queue->input; reply->previous = input->capture; - input->capture = get_user_full_handle( req->handle ); - input->menu_owner = (req->flags & CAPTURE_MENU) ? input->capture : 0; - input->move_size = (req->flags & CAPTURE_MOVESIZE) ? input->capture : 0; + + /* 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)) + { + input->capture = get_user_full_handle( req->handle ); + input->menu_owner = (req->flags & CAPTURE_MENU) ? input->capture : 0; + input->move_size = (req->flags & CAPTURE_MOVESIZE) ? input->capture : 0; + reply->allowed = 1; + } reply->full_handle = input->capture; } } diff --git a/server/request.h b/server/request.h index ca5fc88..dc05f8c 100644 --- a/server/request.h +++ b/server/request.h @@ -1607,7 +1607,8 @@ C_ASSERT( FIELD_OFFSET(struct set_capture_window_request, handle) == 12 ); C_ASSERT( FIELD_OFFSET(struct set_capture_window_request, flags) == 16 ); C_ASSERT( FIELD_OFFSET(struct set_capture_window_reply, previous) == 8 ); C_ASSERT( FIELD_OFFSET(struct set_capture_window_reply, full_handle) == 12 ); -C_ASSERT( sizeof(struct set_capture_window_reply) == 16 ); +C_ASSERT( FIELD_OFFSET(struct set_capture_window_reply, allowed) == 16 ); +C_ASSERT( sizeof(struct set_capture_window_reply) == 24 ); C_ASSERT( FIELD_OFFSET(struct set_caret_window_request, handle) == 12 ); C_ASSERT( FIELD_OFFSET(struct set_caret_window_request, width) == 16 ); C_ASSERT( FIELD_OFFSET(struct set_caret_window_request, height) == 20 ); diff --git a/server/trace.c b/server/trace.c index 6cb4cc9..0b2e971 100644 --- a/server/trace.c +++ b/server/trace.c @@ -3143,6 +3143,7 @@ static void dump_set_capture_window_reply( const struct set_capture_window_reply { fprintf( stderr, " previous=%08x", req->previous ); fprintf( stderr, ", full_handle=%08x", req->full_handle ); + fprintf( stderr, ", allowed=%d", req->allowed ); } static void dump_set_caret_window_request( const struct set_caret_window_request *req )