Module: wine Branch: master Commit: 844037ab01a8682f8c91ab7942f6decf23574d6f URL: http://source.winehq.org/git/wine.git/?a=commit;h=844037ab01a8682f8c91ab7942...
Author: Rob Shearman rob@codeweavers.com Date: Sat Dec 23 15:51:27 2006 +0000
ole32: Performing a COM call from within the processing of a sent message during a wait for completion of another COM call is not allowed.
Add a test for the behaviour where RPC_E_CANTCALLOUT_ININPUTSYNCCALL is returned.
---
dlls/ole32/compobj.c | 6 ++- dlls/ole32/compobj_private.h | 3 +- dlls/ole32/rpc.c | 22 ++++++++- dlls/ole32/tests/marshal.c | 100 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 125 insertions(+), 6 deletions(-)
diff --git a/dlls/ole32/compobj.c b/dlls/ole32/compobj.c index 81236c7..ca51a9a 100644 --- a/dlls/ole32/compobj.c +++ b/dlls/ole32/compobj.c @@ -3190,10 +3190,12 @@ HRESULT WINAPI CoWaitForMultipleHandles(
if (COM_CurrentApt()->filter) { + PENDINGTYPE pendingtype = + COM_CurrentInfo()->pending_call_count_server ? + PENDINGTYPE_NESTED : PENDINGTYPE_TOPLEVEL; DWORD be_handled = IMessageFilter_MessagePending( COM_CurrentApt()->filter, 0 /* FIXME */, - now - start_time, - COM_CurrentInfo()->pending_call_count ? PENDINGTYPE_NESTED : PENDINGTYPE_TOPLEVEL); + now - start_time, pendingtype); TRACE("IMessageFilter_MessagePending returned %d\n", be_handled); switch (be_handled) { diff --git a/dlls/ole32/compobj_private.h b/dlls/ole32/compobj_private.h index 276d82e..6abfdfd 100644 --- a/dlls/ole32/compobj_private.h +++ b/dlls/ole32/compobj_private.h @@ -169,7 +169,8 @@ struct oletls IUnknown *state; /* see CoSetState */ DWORD inits; /* number of times CoInitializeEx called */ GUID causality_id; /* unique identifier for each COM call */ - LONG pending_call_count; /* number of calls pending */ + LONG pending_call_count_client; /* number of client calls pending */ + LONG pending_call_count_server; /* number of server calls pending */ };
diff --git a/dlls/ole32/rpc.c b/dlls/ole32/rpc.c index eec961d..c31aa4a 100644 --- a/dlls/ole32/rpc.c +++ b/dlls/ole32/rpc.c @@ -566,6 +566,18 @@ static HRESULT WINAPI ClientRpcChannelBu wine_dbgstr_longlong(This->oxid)); return RPC_E_WRONG_THREAD; } + /* this situation should be impossible in multi-threaded apartments, + * because the calling thread isn't re-entrable. + * Note: doing a COM call during the processing of a sent message is + * only disallowed if a client call is already being waited for + * completion */ + if (!COM_CurrentApt()->multi_threaded && + COM_CurrentInfo()->pending_call_count_client && + InSendMessage()) + { + ERR("can't make an outgoing COM call in response to a sent message\n"); + return RPC_E_CANTCALLOUT_ININPUTSYNCCALL; + }
params = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*params)); if (!params) return E_OUTOFMEMORY; @@ -632,7 +644,11 @@ static HRESULT WINAPI ClientRpcChannelBu if (hr == S_OK) { if (WaitForSingleObject(params->handle, 0)) + { + COM_CurrentInfo()->pending_call_count_client++; hr = CoWaitForMultipleHandles(0, INFINITE, 1, ¶ms->handle, &index); + COM_CurrentInfo()->pending_call_count_client--; + } } ClientRpcChannelBuffer_ReleaseEventHandle(This, params->handle);
@@ -986,7 +1002,7 @@ void RPC_ExecuteCall(struct dispatch_par
if (IsEqualGUID(&orpcthis.cid, &COM_CurrentInfo()->causality_id)) calltype = CALLTYPE_NESTED; - else if (COM_CurrentInfo()->pending_call_count == 0) + else if (COM_CurrentInfo()->pending_call_count_server == 0) calltype = CALLTYPE_TOPLEVEL; else calltype = CALLTYPE_TOPLEVEL_CALLPENDING; @@ -1023,9 +1039,9 @@ void RPC_ExecuteCall(struct dispatch_par * this call - this should be checked with what Windows does */ old_causality_id = COM_CurrentInfo()->causality_id; COM_CurrentInfo()->causality_id = orpcthis.cid; - COM_CurrentInfo()->pending_call_count++; + COM_CurrentInfo()->pending_call_count_server++; params->hr = IRpcStubBuffer_Invoke(params->stub, params->msg, params->chan); - COM_CurrentInfo()->pending_call_count--; + COM_CurrentInfo()->pending_call_count_server--; COM_CurrentInfo()->causality_id = old_causality_id;
message_state = (struct message_state *)msg->Handle; diff --git a/dlls/ole32/tests/marshal.c b/dlls/ole32/tests/marshal.c index f573a47..beb6597 100644 --- a/dlls/ole32/tests/marshal.c +++ b/dlls/ole32/tests/marshal.c @@ -1614,6 +1614,38 @@ static LRESULT CALLBACK window_proc(HWND
return 0; } + case WM_USER+2: + { + HRESULT hr; + IStream *pStream = NULL; + IClassFactory *proxy = NULL; + IUnknown *object; + DWORD tid; + HANDLE thread; + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, CreateStreamOnHGlobal); + tid = start_host_object(pStream, &IID_IClassFactory, (IUnknown*)&Test_ClassFactory, MSHLFLAGS_NORMAL, &thread); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&proxy); + ok_ole_success(hr, CoReleaseMarshalData); + IStream_Release(pStream); + + /* shows that COM calls executed during the processing of sent + * messages should fail */ + hr = IClassFactory_CreateInstance(proxy, NULL, &IID_IUnknown, (void **)&object); + ok(hr == RPC_E_CANTCALLOUT_ININPUTSYNCCALL, + "COM call during processing of sent message should return RPC_E_CANTCALLOUT_ININPUTSYNCCALL instead of 0x%08x\n", hr); + + IClassFactory_Release(proxy); + + end_host_object(tid, thread); + + PostQuitMessage(0); + + return 0; + } default: return DefWindowProc(hwnd, msg, wparam, lparam); } @@ -1642,6 +1674,73 @@ static void test_message_reentrancy(void } }
+static HRESULT WINAPI TestMsg_IClassFactory_CreateInstance( + LPCLASSFACTORY iface, + LPUNKNOWN pUnkOuter, + REFIID riid, + LPVOID *ppvObj) +{ + *ppvObj = NULL; + SendMessage(hwnd_app, WM_USER+2, 0, 0); + return S_OK; +} + +static IClassFactoryVtbl TestMsgClassFactory_Vtbl = +{ + Test_IClassFactory_QueryInterface, + Test_IClassFactory_AddRef, + Test_IClassFactory_Release, + TestMsg_IClassFactory_CreateInstance, + Test_IClassFactory_LockServer +}; + +IClassFactory TestMsg_ClassFactory = { &TestMsgClassFactory_Vtbl }; + +static void test_call_from_message(void) +{ + MSG msg; + IStream *pStream; + HRESULT hr; + IClassFactory *proxy; + DWORD tid; + HANDLE thread; + IUnknown *object; + + 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"); + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, CreateStreamOnHGlobal); + tid = start_host_object(pStream, &IID_IClassFactory, (IUnknown*)&TestMsg_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 = CoRegisterMessageFilter(&MessageFilter, NULL); + + /* start message re-entrancy test */ + hr = IClassFactory_CreateInstance(proxy, NULL, &IID_IUnknown, (void **)&object); + ok_ole_success(hr, IClassFactory_CreateInstance); + + IClassFactory_Release(proxy); + + ok_no_locks(); + + end_host_object(tid, thread); + + while (GetMessage(&msg, NULL, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } +} + static void test_WM_QUIT_handling(void) { MSG msg; @@ -2131,6 +2230,7 @@ START_TEST(marshal) test_stubbuffer(&IID_IClassFactory); test_proxybuffer(&IID_IClassFactory); test_message_reentrancy(); + test_call_from_message(); test_WM_QUIT_handling(); test_freethreadedmarshaler();