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(&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 */
+            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(&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;
+    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 )


Reply via email to