From: "Changhui Liu" Subject: ole32:fix CoWaitForMultipleHandles cause RPC hang (try 2) Message-Id: Date: Fri, 19 Dec 2014 12:26:30 +0800 Superseded patch 108194: http://source.winehq.org/patches/data/108194 Change log: try 2: pass test on testbot.winehq.org and on this: https://github.com/wine-compholio/wine-staging/blob/master/patches/ole32-CoWaitForMultipleHandles/0001-ole32-tests-Add-additional-tests-for-CoWaitForMultip.patch
Superseded patch 108194:
http://source.winehq.org/patches/data/108194

Change log:
try 2: pass test on testbot.winehq.org and on this:
https://github.com/wine-compholio/wine-staging/blob/master/patches/ole32-CoWaitForMultipleHandles/0001-ole32-tests-Add-additional-tests-for-CoWaitForMultip.patch


From c4b047a751a4d5a243c3c60106a385b5f11ab040 Mon Sep 17 00:00:00 2001 From: Changhui Liu Date: Mon, 15 Dec 2014 11:51:37 +0800 Subject: ole32:fix CoWaitForMultipleHandles cause RPC hang To: wine-patches Reply-To: wine-devel --- diff --git a/dlls/ole32/compobj.c b/dlls/ole32/compobj.c index da466c4..bcc5178 100644 --- a/dlls/ole32/compobj.c +++ b/dlls/ole32/compobj.c @@ -4428,6 +4428,7 @@ HRESULT WINAPI CoWaitForMultipleHandles(DWORD dwFlags, DWORD dwTimeout, APARTMENT *apt = COM_CurrentApt(); BOOL message_loop = apt && !apt->multi_threaded; BOOL check_apc = (dwFlags & COWAIT_ALERTABLE) != 0; + BOOL only_dispatch_rpc_message = 0; TRACE("(0x%08x, 0x%08x, %d, %p, %p)\n", dwFlags, dwTimeout, cHandles, pHandles, lpdwindex); @@ -4508,6 +4509,17 @@ HRESULT WINAPI CoWaitForMultipleHandles(DWORD dwFlags, DWORD dwTimeout, } } + if (only_dispatch_rpc_message) + { + while (PeekMessageW(&msg, apt->win, 0, 0, PM_REMOVE|PM_NOYIELD)) + { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + + continue; + } + /* some apps (e.g. Visio 2010) don't handle WM_PAINT properly and loop forever, * so after processing 100 messages we go back to checking the wait handles */ while (count++ < 100 && COM_PeekMessage(apt, &msg)) @@ -4517,10 +4529,14 @@ HRESULT WINAPI CoWaitForMultipleHandles(DWORD dwFlags, DWORD dwTimeout, DispatchMessageW(&msg); if (msg.message == WM_QUIT) { - TRACE("resending WM_QUIT to outer message loop\n"); - PostQuitMessage(msg.wParam); - /* no longer need to process messages */ - message_loop = FALSE; + if (!only_dispatch_rpc_message) + { + /* no longer need to process wm_quit message, + * but RPC message sent to apt->win need to process*/ + only_dispatch_rpc_message = TRUE; + TRACE("resending WM_QUIT to outer message loop\n"); + PostQuitMessage(msg.wParam); + } break; } } diff --git a/dlls/ole32/tests/compobj.c b/dlls/ole32/tests/compobj.c index eed5ac8..3db7cdb 100644 --- a/dlls/ole32/tests/compobj.c +++ b/dlls/ole32/tests/compobj.c @@ -2283,6 +2283,190 @@ static void test_CoWaitForMultipleHandles(void) CoUninitialize(); } +static HANDLE g_rpc_event = 0; +static volatile char g_rpc_handled = FALSE; +#define DM_EXECUTERPC_FAKE (WM_USER + 3) /* WPARAM = 0, LPARAM = (struct dispatch_params *) */ +static void test_CoWaitForMultipleHandlesDispatchMsg(void) +{ + static const char cls_name[] = "cowait_test_class"; + HANDLE thread; + DWORD index; + WNDCLASSEXA wc; + BOOL success; + HRESULT hr; + HWND hWnd; + HWND hOleMainWnd; + MSG msg; + + const WCHAR wszAptWinClass[] = {'O','l','e','M','a','i','n','T','h','r','e','a','d','W','n','d','C','l','a','s','s',' ', + '0','x','#','#','#','#','#','#','#','#',' ',0}; + const WCHAR wszAptWinClass2[] = {'O','l','e','M','a','i','n','T','h','r','e','a','d','W','n','d','C','l','a','s','s',0}; + + hOleMainWnd = FindWindowW(wszAptWinClass, NULL); + if (NULL == hOleMainWnd) + { + /*test on windows*/ + hOleMainWnd = FindWindowW(wszAptWinClass2, NULL); + } + ok(hOleMainWnd!=NULL, "Expected OLE apartment window is valid!\n"); + + hr = pCoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + ok(hr == S_OK, "CoInitializeEx failed with error 0x%08x\n", hr); + + memset(&wc, 0, sizeof(wc)); + wc.cbSize = sizeof(wc); + wc.style = CS_VREDRAW | CS_HREDRAW; + wc.hInstance = GetModuleHandleA(0); + wc.hCursor = LoadCursorA(NULL, (LPCSTR)IDC_ARROW); + wc.hbrBackground = NULL; + wc.lpszClassName = cls_name; + wc.lpfnWndProc = DefWindowProcA; + success = RegisterClassExA(&wc) != 0; + ok(success, "RegisterClassExA failed %u\n", GetLastError()); + + hWnd = CreateWindowExA(0, cls_name, "Test", WS_TILEDWINDOW, 0, 0, 640, 480, 0, 0, 0, 0); + ok(hWnd != 0, "CreateWindowExA failed %u\n", GetLastError()); + + g_rpc_event = CreateEventA(NULL, FALSE, FALSE, NULL); + ok(g_rpc_event != NULL, "CreateEventA Failed:%d\n", GetLastError()); + + PostQuitMessage(42); + PostMessageA(hOleMainWnd, DM_EXECUTERPC_FAKE, 0, 0xdeadbeef); + PostMessageA(hOleMainWnd, WM_DDE_FIRST, 0, 0); + PostMessageA(hWnd, WM_DDE_FIRST, 0, 0); + PostMessageA(hWnd, DM_EXECUTERPC_FAKE, 0, 0xdeadbeef); + + hr = CoWaitForMultipleHandles(0, 500, 1, &g_rpc_event, &index); + ok(hr == RPC_S_CALLPENDING, "expected RPC_S_CALLPENDING, got 0x%08x\n", hr); + do + { + success = PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE); + trace("ole=%p, hwnd=%p, msg=%x,%x,%x\n", hOleMainWnd, msg.hwnd, msg.message, msg.wParam, msg.lParam); + }while (success); + + CloseHandle(g_rpc_event); + + DestroyWindow(hWnd); + + success = UnregisterClassA(cls_name, GetModuleHandleA(0)); + ok(success, "UnregisterClass failed %u\n", GetLastError()); + + CoUninitialize(); +} + +static DWORD WINAPI RpcThread(LPVOID lpParameter) +{ + HWND hWnd = 0; + const WCHAR wszAptWinClass[] = {'O','l','e','M','a','i','n','T','h','r','e','a','d','W','n','d','C','l','a','s','s',' ', + '0','x','#','#','#','#','#','#','#','#',' ',0}; + + const WCHAR wszAptWinClass2[] = {'O','l','e','M','a','i','n','T','h','r','e','a','d','W','n','d','C','l','a','s','s',0}; + + hWnd = FindWindowW(wszAptWinClass, NULL); + if (NULL == hWnd) + { + /*test on windows*/ + hWnd = FindWindowW(wszAptWinClass2, NULL); + } + ok(hWnd!=NULL, "Expected OLE apartment window is valid!\n"); + + /*Wait for CoWaitForMultipleHandles to receive a WM_QUIT message, so we can reproduce the RPC Hang*/ + Sleep (3000); + + /*Wait for a RPC request to be processed. + This is a very simple demo, + the RPC process flow is very complex in fact if we saw the backtrace in wine. + */ + SendMessageA(hWnd, DM_EXECUTERPC_FAKE, 0, 0xdeadbeef); + + g_rpc_handled = TRUE ; + + return 0; +} + +static DWORD WINAPI MonThread(LPVOID lpParameter) +{ + DWORD wait_count = 0; + + /*wait 10 seconds for the RPC request*/ + while (wait_count < 10) + { + Sleep(1000); + + if (g_rpc_handled) + break; + + ++wait_count; + trace("sleep %d second\n", wait_count); + } + + if (wait_count >= 10) + { + ok(0, "The RPC request is hanging!\n"); + } + else + { + trace("The RPC request is processed OK!\n"); + } + + SetEvent(g_rpc_event); + + return 0; +} + +static void test_CoWaitForMultipleHandlesHangRpc(void) +{ + static const char cls_name[] = "cowait_test_class"; + HANDLE thread; + DWORD index; + WNDCLASSEXA wc; + BOOL success; + HRESULT hr; + HWND hWnd; + + hr = pCoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + ok(hr == S_OK, "CoInitializeEx failed with error 0x%08x\n", hr); + + memset(&wc, 0, sizeof(wc)); + wc.cbSize = sizeof(wc); + wc.style = CS_VREDRAW | CS_HREDRAW; + wc.hInstance = GetModuleHandleA(0); + wc.hCursor = LoadCursorA(NULL, (LPCSTR)IDC_ARROW); + wc.hbrBackground = NULL; + wc.lpszClassName = cls_name; + wc.lpfnWndProc = DefWindowProcA; + success = RegisterClassExA(&wc) != 0; + ok(success, "RegisterClassExA failed %u\n", GetLastError()); + + hWnd = CreateWindowExA(0, cls_name, "Test", WS_TILEDWINDOW, 0, 0, 640, 480, 0, 0, 0, 0); + ok(hWnd != 0, "CreateWindowExA failed %u\n", GetLastError()); + + g_rpc_event = CreateEventA(NULL, FALSE, FALSE, NULL); + ok(g_rpc_event != NULL, "CreateEventA Failed:%d\n", GetLastError()); + + PostQuitMessage(0); + + thread = CreateThread(NULL, 0, RpcThread, NULL, 0, NULL); + ok(thread != NULL, "CreateThread failed, error %u\n", GetLastError()); + CloseHandle(thread); + + thread = CreateThread(NULL, 0, MonThread, NULL, 0, NULL); + ok(thread != NULL, "CreateThread failed, error %u\n", GetLastError()); + CloseHandle(thread); + + hr = CoWaitForMultipleHandles(0, INFINITE, 1, &g_rpc_event, &index); + ok(hr == S_OK, "Expected S_OK, got 0x%08x\n", hr); + + CloseHandle(g_rpc_event); + + DestroyWindow(hWnd); + + success = UnregisterClassA(cls_name, GetModuleHandleA(0)); + ok(success, "UnregisterClass failed %u\n", GetLastError()); + + CoUninitialize(); +} + static void init_funcs(void) { HMODULE hOle32 = GetModuleHandleA("ole32"); @@ -2344,4 +2528,6 @@ START_TEST(compobj) test_OleRegGetMiscStatus(); test_CoCreateGuid(); test_CoWaitForMultipleHandles(); + test_CoWaitForMultipleHandlesDispatchMsg(); + test_CoWaitForMultipleHandlesHangRpc(); } -- 1.9.4.msysgit.1