Alexandre Julliard : user32: Don' t wait for other threads to process WM_NCDESTROY.

Alexandre Julliard julliard at winehq.org
Fri Apr 27 13:26:03 CDT 2018


Module: wine
Branch: master
Commit: 08b19c6f673837b960c460b17601979aa595470f
URL:    https://source.winehq.org/git/wine.git/?a=commit;h=08b19c6f673837b960c460b17601979aa595470f

Author: Alexandre Julliard <julliard at winehq.org>
Date:   Fri Apr 27 14:14:14 2018 +0200

user32: Don't wait for other threads to process WM_NCDESTROY.

Based on a patch by Andrew Eikum.

Signed-off-by: Alexandre Julliard <julliard at winehq.org>

---

 dlls/user32/tests/win.c | 167 ++++++++++++++++++++++++++++++++++++++++++++++++
 dlls/user32/win.c       |   4 +-
 2 files changed, 169 insertions(+), 2 deletions(-)

diff --git a/dlls/user32/tests/win.c b/dlls/user32/tests/win.c
index d409918..6030121 100644
--- a/dlls/user32/tests/win.c
+++ b/dlls/user32/tests/win.c
@@ -10537,6 +10537,172 @@ static void test_display_affinity( HWND win )
     SetWindowLongW(win, GWL_EXSTYLE, styleex);
 }
 
+static struct destroy_data
+{
+    HWND main_wnd;
+    HWND thread1_wnd;
+    HWND thread2_wnd;
+    HANDLE evt;
+    DWORD main_tid;
+    DWORD destroy_count;
+    DWORD ncdestroy_count;
+} destroy_data;
+
+static LRESULT WINAPI destroy_thread1_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+    switch (msg)
+    {
+    case WM_DESTROY:
+        ok( destroy_data.destroy_count > 0, "parent didn't get WM_DESTROY\n" );
+        PostQuitMessage(0);
+        break;
+    case WM_NCDESTROY:
+        ok( destroy_data.ncdestroy_count > 0, "parent didn't get WM_NCDESTROY\n" );
+        break;
+    }
+    return DefWindowProcA(hwnd, msg, wparam, lparam);
+}
+
+static LRESULT WINAPI destroy_thread2_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+    switch (msg)
+    {
+    case WM_DESTROY:
+        ok( destroy_data.destroy_count > 0, "parent didn't get WM_DESTROY\n" );
+        break;
+    case WM_NCDESTROY:
+        ok( destroy_data.ncdestroy_count > 0, "parent didn't get WM_NCDESTROY\n" );
+        ok( WaitForSingleObject(destroy_data.evt, 10000) != WAIT_TIMEOUT, "timeout\n" );
+        PostQuitMessage(0);
+        break;
+    }
+    return DefWindowProcA(hwnd, msg, wparam, lparam);
+}
+
+static LRESULT WINAPI destroy_main_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+    switch (msg)
+    {
+    case WM_DESTROY:
+        destroy_data.destroy_count++;
+        break;
+    case WM_NCDESTROY:
+        destroy_data.ncdestroy_count++;
+        break;
+    }
+    return DefWindowProcA(hwnd, msg, wparam, lparam);
+}
+
+static DWORD CALLBACK destroy_thread1(void *user)
+{
+    MSG msg;
+
+    destroy_data.thread1_wnd = CreateWindowExA(0, "destroy_test_thread1",
+            "destroy test thread", WS_CHILD, 100, 100, 100, 100,
+            destroy_data.main_wnd, 0, GetModuleHandleA(NULL), NULL);
+    ok(destroy_data.thread1_wnd != NULL, "CreateWindowEx failed with error %d\n", GetLastError());
+    PostThreadMessageW(destroy_data.main_tid, WM_USER, 0, 0);
+
+    while (GetMessageA(&msg, 0, 0, 0))
+    {
+        TranslateMessage(&msg);
+        DispatchMessageA(&msg);
+    }
+    PostThreadMessageW(destroy_data.main_tid, WM_USER + 2, 0, 0);
+    ok( WaitForSingleObject(destroy_data.evt, 10000) != WAIT_TIMEOUT, "timeout\n" );
+    ok( IsWindow( destroy_data.thread1_wnd ), "window destroyed\n" );
+    return 0;
+}
+
+static DWORD CALLBACK destroy_thread2(void *user)
+{
+    MSG msg;
+
+    destroy_data.thread2_wnd = CreateWindowExA(0, "destroy_test_thread2",
+            "destroy test thread", WS_CHILD, 100, 100, 100, 100,
+            destroy_data.main_wnd, 0, GetModuleHandleA(NULL), NULL);
+    ok(destroy_data.thread2_wnd != NULL, "CreateWindowEx failed with error %d\n", GetLastError());
+
+    PostThreadMessageW(destroy_data.main_tid, WM_USER + 1, 0, 0);
+    Sleep( 100 );
+
+    while (GetMessageA(&msg, 0, 0, 0))
+    {
+        TranslateMessage(&msg);
+        DispatchMessageA(&msg);
+    }
+    ok( !IsWindow( destroy_data.thread2_wnd ), "window not destroyed\n" );
+    return 0;
+}
+
+static void test_destroy_quit(void)
+{
+    MSG msg;
+    WNDCLASSA wnd_classA;
+    ATOM ret;
+    HANDLE thread1, thread2;
+
+    destroy_data.main_tid = GetCurrentThreadId();
+    destroy_data.evt = CreateEventW(NULL, TRUE, FALSE, NULL);
+    destroy_data.destroy_count = 0;
+    destroy_data.ncdestroy_count = 0;
+
+    memset(&wnd_classA, 0, sizeof(wnd_classA));
+    wnd_classA.lpszClassName = "destroy_test_main";
+    wnd_classA.lpfnWndProc = destroy_main_wndproc;
+    ret = RegisterClassA(&wnd_classA);
+    ok(ret, "RegisterClass failed with error %d\n", GetLastError());
+
+    wnd_classA.lpszClassName = "destroy_test_thread1";
+    wnd_classA.lpfnWndProc = destroy_thread1_wndproc;
+    ret = RegisterClassA(&wnd_classA);
+    ok(ret, "RegisterClass failed with error %d\n", GetLastError());
+
+    wnd_classA.lpszClassName = "destroy_test_thread2";
+    wnd_classA.lpfnWndProc = destroy_thread2_wndproc;
+    ret = RegisterClassA(&wnd_classA);
+    ok(ret, "RegisterClass failed with error %d\n", GetLastError());
+
+    destroy_data.main_wnd = CreateWindowExA(0, "destroy_test_main",
+            "destroy test main", WS_OVERLAPPED | WS_CAPTION, 100, 100, 100, 100,
+            0, 0, GetModuleHandleA(NULL), NULL);
+    ok(destroy_data.main_wnd != NULL, "CreateWindowEx failed with error %d\n", GetLastError());
+    if (!destroy_data.main_wnd)
+    {
+        CloseHandle(destroy_data.evt);
+        return;
+    }
+
+    thread1 = CreateThread(NULL, 0, destroy_thread1, 0, 0, NULL);
+
+    while (GetMessageA(&msg, 0, 0, 0))
+    {
+        BOOL done = 0;
+        switch (msg.message)
+        {
+        case WM_USER:
+            thread2 = CreateThread(NULL, 0, destroy_thread2, 0, 0, NULL);
+            CloseHandle( thread2 );
+            break;
+        case WM_USER + 1:
+            DestroyWindow(destroy_data.main_wnd);
+            break;
+        case WM_USER + 2:
+            SetEvent(destroy_data.evt);
+            done = 1;
+            break;
+        default:
+            DispatchMessageA(&msg);
+            break;
+        }
+        if (done) break;
+    }
+
+    ok( WaitForSingleObject( thread1, 10000 ) != WAIT_TIMEOUT, "timeout" );
+    ok( !IsWindow( destroy_data.thread1_wnd ), "window not destroyed\n" );
+    CloseHandle( thread1 );
+}
+
 START_TEST(win)
 {
     char **argv;
@@ -10692,6 +10858,7 @@ START_TEST(win)
     test_display_affinity(hwndMain);
     test_hide_window();
     test_minimize_window(hwndMain);
+    test_destroy_quit();
 
     /* add the tests above this line */
     if (hhook) UnhookWindowsHookEx(hhook);
diff --git a/dlls/user32/win.c b/dlls/user32/win.c
index 17a72d2..7bb0088 100644
--- a/dlls/user32/win.c
+++ b/dlls/user32/win.c
@@ -971,7 +971,7 @@ LRESULT WIN_DestroyWindow( HWND hwnd )
         for (i = 0; list[i]; i++)
         {
             if (WIN_IsCurrentThread( list[i] )) WIN_DestroyWindow( list[i] );
-            else SendMessageW( list[i], WM_WINE_DESTROYWINDOW, 0, 0 );
+            else SendNotifyMessageW( list[i], WM_WINE_DESTROYWINDOW, 0, 0 );
         }
         HeapFree( GetProcessHeap(), 0, list );
     }
@@ -1088,7 +1088,7 @@ void destroy_thread_windows(void)
         if (!list) continue;
         for (i = 0; list[i]; i++)
             if (!WIN_IsCurrentThread( list[i] ))
-                SendMessageW( list[i], WM_WINE_DESTROYWINDOW, 0, 0 );
+                SendNotifyMessageW( list[i], WM_WINE_DESTROYWINDOW, 0, 0 );
         HeapFree( GetProcessHeap(), 0, list );
     }
 }




More information about the wine-cvs mailing list