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

Based on a patch by Andrew Eikum.

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Alexandre Julliard 2018-04-27 14:14:14 +02:00
parent 460b4e7adf
commit 08b19c6f67
2 changed files with 169 additions and 2 deletions

View File

@ -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);

View File

@ -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 );
}
}