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:
parent
901bdbf2d1
commit
53f3d4c65a
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue