user32: Don't regard the clipboard as changed if we don't own it.

Vincent Povirk madewokherd at gmail.com
Wed May 18 14:23:47 CDT 2016


From: Vincent Povirk <vincent at codeweavers.com>

I ran into a situation where user32 will call EndClipboardUpdate even though
the clipboard hasn't changed. It went something like this:
* Program A acquires the clipboard and adds some delay-rendered data.
* Program B requests data from the clipboard, causing program A to receive
  WM_RENDERFORMAT.
* Program A calls SetClipboardData to fulfill the request, setting
  bCBHasChanged to TRUE.
* Program C acquires the clipboard.
* Program A opens the clipboard to enumerate the formats. When it closes the
  clipboard, EndClipboardUpdate is called.

We could instead check whether the clipboard is open in SetClipboardData, but
it's not obvious to me that this is correct. A program may have the clipboard
open and call SetClipboardData from another thread, or it may close the
clipboard without calling CloseClipboard by destroying its window.

There's still a slight issue with this patch: if OpenClipboard is called when
the process already owns the clipboard, bCBHasChanged is TRUE, and the user
driver releases clipboard ownership, CloseClipboard will increment the
sequence number. We could fix this more completely by setting bCBHasChanged to
FALSE when OpenClipboard opens the clipboard, but it's possible for
OpenClipboard to succeed when the clipboard is already open, and we'd need
a wineserver change to detect this. I'm also not entirely sure it's right
(maybe we want SetClipboardData to be regarded as a change even if the
clipboard isn't open?).

Signed-off-by: Vincent Povirk <vincent at codeweavers.com>
---
 dlls/user32/clipboard.c | 17 ++++++++++++++---
 1 file changed, 14 insertions(+), 3 deletions(-)

diff --git a/dlls/user32/clipboard.c b/dlls/user32/clipboard.c
index dec738a..4764784 100644
--- a/dlls/user32/clipboard.c
+++ b/dlls/user32/clipboard.c
@@ -178,6 +178,7 @@ INT WINAPI GetClipboardFormatNameA(UINT wFormat, LPSTR retStr, INT maxlen)
 BOOL WINAPI OpenClipboard( HWND hWnd )
 {
     BOOL bRet;
+    UINT flags;
 
     TRACE("(%p)...\n", hWnd);
 
@@ -185,10 +186,16 @@ BOOL WINAPI OpenClipboard( HWND hWnd )
     {
         req->flags = SET_CB_OPEN;
         req->clipboard = wine_server_user_handle( hWnd );
-        bRet = !wine_server_call( req );
+        if ((bRet = !wine_server_call( req )))
+            flags = reply->flags;
     }
     SERVER_END_REQ;
 
+    if (bRet && !(flags & CB_PROCESS))
+    {
+        bCBHasChanged = FALSE;
+    }
+
     TRACE(" returning %i\n", bRet);
 
     return bRet;
@@ -201,6 +208,7 @@ BOOL WINAPI OpenClipboard( HWND hWnd )
 BOOL WINAPI CloseClipboard(void)
 {
     HWND viewer = 0;
+    UINT flags;
     BOOL ret;
 
     TRACE("() Changed=%d\n", bCBHasChanged);
@@ -210,18 +218,21 @@ BOOL WINAPI CloseClipboard(void)
         req->flags = SET_CB_CLOSE;
         if (bCBHasChanged) req->flags |= SET_CB_SEQNO;
         if ((ret = !wine_server_call_err( req )))
+        {
             viewer = wine_server_ptr_handle( reply->old_viewer );
+            flags = reply->flags;
+        }
     }
     SERVER_END_REQ;
 
     if (!ret) return FALSE;
 
-    if (bCBHasChanged)
+    if (bCBHasChanged && (flags & CB_PROCESS))
     {
         USER_Driver->pEndClipboardUpdate();
-        bCBHasChanged = FALSE;
         if (viewer) SendNotifyMessageW(viewer, WM_DRAWCLIPBOARD, (WPARAM) GetClipboardOwner(), 0);
     }
+    bCBHasChanged = FALSE;
     return TRUE;
 }
 
-- 
2.5.0




More information about the wine-patches mailing list