Authors: Rob Shearman <rob@codeweavers.com>, Mike Hearn <mh@codeweavers.com>

- Add re-entrancy tests to the test suite.
- Run RPCs on a new thread client side so we can pump the message
  loop.
This commit is contained in:
Alexandre Julliard 2005-03-04 12:30:47 +00:00
parent 901bdbf2d1
commit 53f3d4c65a
2 changed files with 266 additions and 4 deletions

View file

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

View file

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