user32: Do not allow a change of capture if the currently capture window is a menu unless explicitly specified. Handle failure in set_capture_window() correctly in SetCapture() and ReleaseCapture(). With tests.

Peter Dons Tychsen (none) donpedro at donpedro.
Wed Jan 6 20:25:11 CST 2010


Tested on windows-xp
Fixes bug http://bugs.winehq.org/show_bug.cgi?id=21212
---
 dlls/user32/input.c     |   23 ++++++++++-
 dlls/user32/tests/win.c |   98 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 119 insertions(+), 2 deletions(-)

diff --git a/dlls/user32/input.c b/dlls/user32/input.c
index bfe80c4..83cb7fa 100644
--- a/dlls/user32/input.c
+++ b/dlls/user32/input.c
@@ -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;
+    }
 
     if (gui_flags & GUI_INMENUMODE) flags |= CAPTURE_MENU;
     if (gui_flags & GUI_INMOVESIZE) flags |= CAPTURE_MOVESIZE;
@@ -228,7 +239,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 +254,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..4ee293d 100644
--- a/dlls/user32/tests/win.c
+++ b/dlls/user32/tests/win.c
@@ -2697,6 +2697,103 @@ 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(&gti, 0, sizeof(GUITHREADINFO));
+            gti.cbSize = sizeof(GUITHREADINFO);
+            status = GetGUIThreadInfo(GetCurrentThreadId(), &gti);
+            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);
+            err = GetLastError();
+            ok(!set_cap_wnd, "ReleaseCapture should have failed!\n");
+            ok(err == ERROR_SUCCESS, "Bad error-code from SetCapture. got %08X expected %08X\n", err, 0);
+
+            /* check that ReleaseCapture fails and does not touch the error code */
+            status = ReleaseCapture();
+            err = GetLastError();
+            ok(!status, "ReleaseCapture should have failed!\n");
+            ok(err == ERROR_SUCCESS, "Bad error-code from ReleaseCapture. got %08X expected %08X\n", err, 0);
+
+            /* check that thread info did not change */
+            memset(&gti, 0, sizeof(GUITHREADINFO));
+            gti.cbSize = sizeof(GUITHREADINFO);
+            status = GetGUIThreadInfo(GetCurrentThreadId(), &gti);
+            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;
+    MSG msg;
+    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 +5918,7 @@ START_TEST(win)
     test_capture_1();
     test_capture_2();
     test_capture_3(hwndMain, hwndMain2);
+    test_capture_4();
 
     test_CreateWindow();
     test_parent_owner();
-- 
1.6.2.5


--=-C7vuVwwuKOCgLiPXTwdK--




More information about the wine-patches mailing list