If the window does not render the delayed-rendering clipboard formats before it is destroyed, then they should be removed from the clipboard, including the derived formats added by CloseClipboard().
Signed-off-by: Francois Gouget fgouget@codeweavers.com --- Currently Wine fails to remove the derived formats (e.g. CF_TEXT in the case of CF_UNICODETEXT), and ends up creating circular references in the CloseClipboard() call. In turn this causes GetClipboardData() to go into an infinite recursive loop and to crash. This usually happens in explorer.exe on desktop environments that have a clipboard manager. https://bugs.winehq.org/show_bug.cgi?id=51496 --- dlls/user32/tests/clipboard.c | 42 +++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+)
diff --git a/dlls/user32/tests/clipboard.c b/dlls/user32/tests/clipboard.c index f351a6a29a8..8515ba5b88a 100644 --- a/dlls/user32/tests/clipboard.c +++ b/dlls/user32/tests/clipboard.c @@ -949,6 +949,48 @@ static void test_synthesized(void) r = CloseClipboard(); ok(r, "gle %d\n", GetLastError()); DestroyWindow( hwnd ); + + /* Check what happens to the delayed rendering clipboard formats when the + * owner window is destroyed. + */ + hwnd = CreateWindowA( "static", NULL, WS_POPUP, 0, 0, 10, 10, 0, 0, 0, NULL ); + SetWindowLongPtrA( hwnd, GWLP_WNDPROC, (LONG_PTR)renderer_winproc ); + + r = open_clipboard(hwnd); + ok(r, "gle %d\n", GetLastError()); + r = EmptyClipboard(); + ok(r, "gle %d\n", GetLastError()); + SetClipboardData( CF_UNICODETEXT, NULL ); + r = CloseClipboard(); + ok(r, "gle %d\n", GetLastError()); + + r = open_clipboard(NULL); + ok(r, "gle %d\n", GetLastError()); + count = CountClipboardFormats(); + ok(count == 4, "count %u\n", count ); + + DestroyWindow( hwnd ); + + /* CF_UNICODETEXT and derivatives, CF_TEXT + CF_OEMTEXT, should be gone */ + count = CountClipboardFormats(); + todo_wine ok(count == 1, "count %u\n", count ); + cf = EnumClipboardFormats( 0 ); + ok(cf == CF_LOCALE, "unexpected clipboard format %u\n", cf); + + r = CloseClipboard(); + ok(r, "gle %d\n", GetLastError()); + + r = open_clipboard(NULL); + ok(r, "gle %d\n", GetLastError()); + + SetLastError(0xdeadbeef); + data = (void*)1; if (broken(1)) /* FIXME Crashes in Wine */ + data = GetClipboardData( CF_TEXT ); + ok(GetLastError() == 0xdeadbeef, "unexpected last error %d\n", GetLastError()); + todo_wine ok(!data, "GetClipboardData() should have returned NULL\n"); + + r = CloseClipboard(); + ok(r, "gle %d\n", GetLastError()); }
static DWORD WINAPI clipboard_render_data_thread(void *param)
Synthesized formats must be removed too if the format they depend on has been removed.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=51496 Signed-off-by: Francois Gouget fgouget@codeweavers.com --- dlls/user32/tests/clipboard.c | 7 +++---- server/clipboard.c | 7 ++++++- 2 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/dlls/user32/tests/clipboard.c b/dlls/user32/tests/clipboard.c index 8515ba5b88a..b673fc1add5 100644 --- a/dlls/user32/tests/clipboard.c +++ b/dlls/user32/tests/clipboard.c @@ -973,7 +973,7 @@ static void test_synthesized(void)
/* CF_UNICODETEXT and derivatives, CF_TEXT + CF_OEMTEXT, should be gone */ count = CountClipboardFormats(); - todo_wine ok(count == 1, "count %u\n", count ); + ok(count == 1, "count %u\n", count ); cf = EnumClipboardFormats( 0 ); ok(cf == CF_LOCALE, "unexpected clipboard format %u\n", cf);
@@ -984,10 +984,9 @@ static void test_synthesized(void) ok(r, "gle %d\n", GetLastError());
SetLastError(0xdeadbeef); - data = (void*)1; if (broken(1)) /* FIXME Crashes in Wine */ data = GetClipboardData( CF_TEXT ); - ok(GetLastError() == 0xdeadbeef, "unexpected last error %d\n", GetLastError()); - todo_wine ok(!data, "GetClipboardData() should have returned NULL\n"); + ok(GetLastError() == 0xdeadbeef, "bad last error %d\n", GetLastError()); + ok(!data, "GetClipboardData() should have returned NULL\n");
r = CloseClipboard(); ok(r, "gle %d\n", GetLastError()); diff --git a/server/clipboard.c b/server/clipboard.c index 1c4875ff726..8d1c42cb525 100644 --- a/server/clipboard.c +++ b/server/clipboard.c @@ -303,7 +303,12 @@ static user_handle_t release_clipboard( struct clipboard *clipboard ) /* free the delayed-rendered formats, since we no longer have an owner to render them */ LIST_FOR_EACH_ENTRY_SAFE( format, next, &clipboard->formats, struct clip_format, entry ) { - if (format->data || format->from) continue; + /* format->from is earlier in the list and thus has already been + * removed if not available anymore (it is also < CF_MAX) + */ + if (format->data || + (format->from && HAS_FORMAT(clipboard->format_map, format->from))) + continue; list_remove( &format->entry ); if (format->id < CF_MAX) clipboard->format_map &= ~(1 << format->id); clipboard->format_count--;