From 53f3d4c65a0974918e03df75aaa3c516357470f8 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Fri, 4 Mar 2005 12:30:47 +0000 Subject: [PATCH] Authors: Rob Shearman , Mike Hearn - Add re-entrancy tests to the test suite. - Run RPCs on a new thread client side so we can pump the message loop. --- dlls/ole32/rpc.c | 86 ++++++++++++++++- dlls/ole32/tests/marshal.c | 184 ++++++++++++++++++++++++++++++++++++- 2 files changed, 266 insertions(+), 4 deletions(-) diff --git a/dlls/ole32/rpc.c b/dlls/ole32/rpc.c index 9ffd5a18f01..07e0d46b2f9 100644 --- a/dlls/ole32/rpc.c +++ b/dlls/ole32/rpc.c @@ -187,18 +187,98 @@ static HRESULT WINAPI ClientRpcChannelBuffer_GetBuffer(LPRPCCHANNELBUFFER iface, return HRESULT_FROM_WIN32(status); } +struct rpc_sendreceive_params +{ + RPC_MESSAGE *msg; + RPC_STATUS status; +}; + +/* this thread runs an outgoing RPC */ +static DWORD WINAPI rpc_sendreceive_thread(LPVOID param) +{ + struct rpc_sendreceive_params *data = (struct rpc_sendreceive_params *) param; + + TRACE("starting up\n"); + + /* FIXME: trap and rethrow RPC exceptions in app thread */ + data->status = I_RpcSendReceive(data->msg); + + TRACE("completed with status 0x%lx\n", data->status); + + return 0; +} + static HRESULT WINAPI RpcChannelBuffer_SendReceive(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE *olemsg, ULONG *pstatus) { RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg; + HRESULT hr = S_OK; + HANDLE thread; + struct rpc_sendreceive_params *params; + DWORD tid, res; RPC_STATUS status; - HRESULT hr; - + TRACE("(%p)\n", msg); - status = I_RpcSendReceive(msg); + params = HeapAlloc(GetProcessHeap(), 0, sizeof(*params)); + if (!params) return E_OUTOFMEMORY; + + params->msg = msg; + /* we use a separate thread here because we need to be able to + * pump the message loop in the application thread: if we do not, + * any windows created by this thread will hang and RPCs that try + * and re-enter this STA from an incoming server thread will + * deadlock. InstallShield is an example of that. + */ + + thread = CreateThread(NULL, 0, rpc_sendreceive_thread, params, 0, &tid); + if (!thread) + { + ERR("Could not create RpcSendReceive thread, error %lx\n", GetLastError()); + return E_UNEXPECTED; + } + + while (TRUE) + { + TRACE("waiting for rpc completion or window message\n"); + res = MsgWaitForMultipleObjectsEx(1, &thread, INFINITE, QS_ALLINPUT, 0); + + if (res == WAIT_OBJECT_0 + 1) /* messages available */ + { + MSG message; + while (PeekMessageW(&message, NULL, 0, 0, PM_REMOVE)) + { + /* FIXME: filter the messages here */ + if (message.message == DM_EXECUTERPC) + TRACE("received DM_EXECUTRPC dispatch request, re-entering ...\n"); + else + TRACE("received message whilst waiting for RPC: 0x%x\n", message.message); + TranslateMessage(&message); + DispatchMessageW(&message); + } + } + else if (res == WAIT_OBJECT_0) + { + break; /* RPC is completed */ + } + else + { + ERR("Unexpected wait termination: %ld, %ld\n", res, GetLastError()); + hr = E_UNEXPECTED; + break; + } + } + + CloseHandle(thread); + + status = params->status; + HeapFree(GetProcessHeap(), 0, params); + params = NULL; + if (hr) return hr; + if (pstatus) *pstatus = status; + TRACE("RPC call status: 0x%lx\n", status); if (status == RPC_S_OK) hr = S_OK; else if (status == RPC_S_CALL_FAILED) diff --git a/dlls/ole32/tests/marshal.c b/dlls/ole32/tests/marshal.c index ec0290370f3..dd67a038a57 100644 --- a/dlls/ole32/tests/marshal.c +++ b/dlls/ole32/tests/marshal.c @@ -1203,6 +1203,97 @@ static void test_stubbuffer(REFIID riid) ok(refs == 0, "Ref-count leak of %ld on IRpcProxyBuffer\n", refs); } +static HWND hwnd_app; + +static HRESULT WINAPI TestRE_IClassFactory_CreateInstance( + LPCLASSFACTORY iface, + LPUNKNOWN pUnkOuter, + REFIID riid, + LPVOID *ppvObj) +{ + DWORD res; + BOOL ret = SendMessageTimeout(hwnd_app, WM_NULL, 0, 0, SMTO_BLOCK, 5000, &res); + ok(ret, "Timed out sending a message to originating window during RPC call\n"); + return S_FALSE; +} + +static IClassFactoryVtbl TestREClassFactory_Vtbl = +{ + Test_IClassFactory_QueryInterface, + Test_IClassFactory_AddRef, + Test_IClassFactory_Release, + TestRE_IClassFactory_CreateInstance, + Test_IClassFactory_LockServer +}; + +IClassFactory TestRE_ClassFactory = { &TestREClassFactory_Vtbl }; + +static LRESULT CALLBACK window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + switch (msg) + { + case WM_USER: + { + HRESULT hr; + IStream *pStream = NULL; + IClassFactory *proxy = NULL; + IUnknown *object; + DWORD tid; + HANDLE thread; + + cLocks = 0; + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, CreateStreamOnHGlobal); + tid = start_host_object(pStream, &IID_IClassFactory, (IUnknown*)&TestRE_ClassFactory, MSHLFLAGS_NORMAL, &thread); + + ok_more_than_one_lock(); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&proxy); + ok_ole_success(hr, CoReleaseMarshalData); + IStream_Release(pStream); + + ok_more_than_one_lock(); + + hr = IClassFactory_CreateInstance(proxy, NULL, &IID_IUnknown, (void **)&object); + + IClassFactory_Release(proxy); + + ok_no_locks(); + + end_host_object(tid, thread); + + PostMessage(hwnd, WM_QUIT, 0, 0); + + return 0; + } + default: + return DefWindowProc(hwnd, msg, wparam, lparam); + } +} + +static void test_message_reentrancy() +{ + WNDCLASS wndclass; + MSG msg; + + memset(&wndclass, 0, sizeof(wndclass)); + wndclass.lpfnWndProc = window_proc; + wndclass.lpszClassName = "WineCOMTest"; + RegisterClass(&wndclass); + + hwnd_app = CreateWindow("WineCOMTest", NULL, 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, NULL, 0); + ok(hwnd_app != NULL, "Window creation failed\n"); + + PostMessage(hwnd_app, WM_USER, 0, 0); + + while (GetMessage(&msg, NULL, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } +} /* doesn't pass with Win9x COM DLLs (even though Essential COM says it should) */ #if 0 @@ -1222,6 +1313,97 @@ static void UnlockModuleOOP() SetEvent(heventShutdown); } +static HWND hwnd_app; + +static HRESULT WINAPI TestRE_IClassFactory_CreateInstance( + LPCLASSFACTORY iface, + LPUNKNOWN pUnkOuter, + REFIID riid, + LPVOID *ppvObj) +{ + DWORD res; + BOOL ret = SendMessageTimeout(hwnd_app, WM_NULL, 0, 0, SMTO_BLOCK, 500, &res); + todo_wine { ok(ret, "Timed out sending a message to originating window during RPC call\n"); } + return S_FALSE; +} + +static IClassFactoryVtbl TestREClassFactory_Vtbl = +{ + Test_IClassFactory_QueryInterface, + Test_IClassFactory_AddRef, + Test_IClassFactory_Release, + TestRE_IClassFactory_CreateInstance, + Test_IClassFactory_LockServer +}; + +IClassFactory TestRE_ClassFactory = { &TestREClassFactory_Vtbl }; + +static LRESULT CALLBACK window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + switch (msg) + { + case WM_USER: + { + HRESULT hr; + IStream *pStream = NULL; + IClassFactory *proxy = NULL; + IUnknown *object; + DWORD tid; + HANDLE thread; + + cLocks = 0; + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, CreateStreamOnHGlobal); + tid = start_host_object(pStream, &IID_IClassFactory, (IUnknown*)&TestRE_ClassFactory, MSHLFLAGS_NORMAL, &thread); + + ok_more_than_one_lock(); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&proxy); + ok_ole_success(hr, CoReleaseMarshalData); + IStream_Release(pStream); + + ok_more_than_one_lock(); + + hr = IClassFactory_CreateInstance(proxy, NULL, &IID_IUnknown, (void **)&object); + + IClassFactory_Release(proxy); + + ok_no_locks(); + + end_host_object(tid, thread); + + PostMessage(hwnd, WM_QUIT, 0, 0); + + return 0; + } + default: + return DefWindowProc(hwnd, msg, wparam, lparam); + } +} + +static void test_message_reentrancy() +{ + WNDCLASS wndclass; + MSG msg; + + memset(&wndclass, 0, sizeof(wndclass)); + wndclass.lpfnWndProc = window_proc; + wndclass.lpszClassName = "WineCOMTest"; + RegisterClass(&wndclass); + + hwnd_app = CreateWindow("WineCOMTest", NULL, 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, NULL, 0); + ok(hwnd_app != NULL, "Window creation failed\n"); + + PostMessage(hwnd_app, WM_USER, 0, 0); + + while (GetMessage(&msg, NULL, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } +} static HRESULT WINAPI TestOOP_IClassFactory_QueryInterface( LPCLASSFACTORY iface, @@ -1394,7 +1576,7 @@ START_TEST(marshal) test_proxy_interfaces(); test_stubbuffer(&IID_IClassFactory); /* FIXME: test GIT */ - /* FIXME: test COM re-entrancy */ + test_message_reentrancy(); /* test_out_of_process_com(); */ CoUninitialize();