Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/ole32/compobj.c | 2 +- dlls/ole32/compobj_private.h | 3 +- dlls/ole32/marshal.c | 32 ++++++++---- dlls/ole32/rpc.c | 12 +++-- dlls/ole32/tests/marshal.c | 114 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 148 insertions(+), 15 deletions(-)
diff --git a/dlls/ole32/compobj.c b/dlls/ole32/compobj.c index 9cbfaee..fd5cbde 100644 --- a/dlls/ole32/compobj.c +++ b/dlls/ole32/compobj.c @@ -736,7 +736,7 @@ static APARTMENT *apartment_find_mta(void)
/* Return the current apartment if it exists, or, failing that, the MTA. Caller * must free the returned apartment in either case. */ -static APARTMENT *apartment_get_current_or_mta(void) +APARTMENT *apartment_get_current_or_mta(void) { APARTMENT *apt = COM_CurrentApt(); if (apt) diff --git a/dlls/ole32/compobj_private.h b/dlls/ole32/compobj_private.h index 12413f7..dc09d20 100644 --- a/dlls/ole32/compobj_private.h +++ b/dlls/ole32/compobj_private.h @@ -212,7 +212,7 @@ void RPC_StartRemoting(struct apartment *apt) DECLSPEC_HIDDEN; HRESULT RPC_CreateClientChannel(const OXID *oxid, const IPID *ipid, const OXID_INFO *oxid_info, DWORD dest_context, void *dest_context_data, - IRpcChannelBuffer **chan) DECLSPEC_HIDDEN; + IRpcChannelBuffer **chan, APARTMENT *apt) DECLSPEC_HIDDEN; HRESULT RPC_CreateServerChannel(DWORD dest_context, void *dest_context_data, IRpcChannelBuffer **chan) DECLSPEC_HIDDEN; void RPC_ExecuteCall(struct dispatch_params *params) DECLSPEC_HIDDEN; HRESULT RPC_RegisterInterface(REFIID riid) DECLSPEC_HIDDEN; @@ -248,6 +248,7 @@ HRESULT apartment_createwindowifneeded(struct apartment *apt) DECLSPEC_HIDDEN; HWND apartment_getwindow(const struct apartment *apt) DECLSPEC_HIDDEN; HRESULT enter_apartment(struct oletls *info, DWORD model) DECLSPEC_HIDDEN; void leave_apartment(struct oletls *info) DECLSPEC_HIDDEN; +APARTMENT *apartment_get_current_or_mta(void) DECLSPEC_HIDDEN;
/* DCOM messages used by the apartment window (not compatible with native) */ #define DM_EXECUTERPC (WM_USER + 0) /* WPARAM = 0, LPARAM = (struct dispatch_params *) */ diff --git a/dlls/ole32/marshal.c b/dlls/ole32/marshal.c index b39dac0..822d06e 100644 --- a/dlls/ole32/marshal.c +++ b/dlls/ole32/marshal.c @@ -313,13 +313,15 @@ static HRESULT WINAPI ClientIdentity_QueryMultipleInterfaces(IMultiQI *iface, UL * the interfaces were returned */ if (SUCCEEDED(hr)) { + APARTMENT *apt = apartment_get_current_or_mta(); + /* try to unmarshal each object returned to us */ for (i = 0; i < nonlocal_mqis; i++) { ULONG index = mapping[i]; HRESULT hrobj = qiresults[i].hResult; if (hrobj == S_OK) - hrobj = unmarshal_object(&qiresults[i].std, COM_CurrentApt(), + hrobj = unmarshal_object(&qiresults[i].std, apt, This->dest_context, This->dest_context_data, pMQIs[index].pIID, &This->oxid_info, @@ -331,6 +333,8 @@ static HRESULT WINAPI ClientIdentity_QueryMultipleInterfaces(IMultiQI *iface, UL ERR("Failed to get pointer to interface %s\n", debugstr_guid(pMQIs[index].pIID)); pMQIs[index].hr = hrobj; } + + apartment_release(apt); }
/* free the memory allocated by the proxy */ @@ -1010,8 +1014,7 @@ static HRESULT proxy_manager_get_remunknown(struct proxy_manager * This, IRemUnk if (This->sorflags & SORFP_NOLIFETIMEMGMT) return S_FALSE;
- apt = COM_CurrentApt(); - if (!apt) + if (!(apt = apartment_get_current_or_mta())) return CO_E_NOTINITIALIZED;
called_in_original_apt = This->parent && (This->parent->oxid == apt->oxid); @@ -1046,7 +1049,7 @@ static HRESULT proxy_manager_get_remunknown(struct proxy_manager * This, IRemUnk stdobjref.ipid = This->oxid_info.ipidRemUnknown;
/* do the unmarshal */ - hr = unmarshal_object(&stdobjref, COM_CurrentApt(), This->dest_context, + hr = unmarshal_object(&stdobjref, apt, This->dest_context, This->dest_context_data, &IID_IRemUnknown, &This->oxid_info, (void**)remunk); if (hr == S_OK && called_in_original_apt) @@ -1056,6 +1059,7 @@ static HRESULT proxy_manager_get_remunknown(struct proxy_manager * This, IRemUnk } } LeaveCriticalSection(&This->cs); + apartment_release(apt);
TRACE("got IRemUnknown* pointer %p, hr = 0x%08x\n", *remunk, hr);
@@ -1288,7 +1292,7 @@ static HRESULT unmarshal_object(const STDOBJREF *stdobjref, APARTMENT *apt, &proxy_manager->oxid_info, proxy_manager->dest_context, proxy_manager->dest_context_data, - &chanbuf); + &chanbuf, apt); if (hr == S_OK) hr = proxy_manager_create_ifproxy(proxy_manager, stdobjref, riid, chanbuf, &ifproxy); @@ -1324,14 +1328,14 @@ StdMarshalImpl_UnmarshalInterface(IMarshal *iface, IStream *pStm, REFIID riid, v STDOBJREF stdobjref; ULONG res; HRESULT hres; - APARTMENT *apt = COM_CurrentApt(); + APARTMENT *apt; APARTMENT *stub_apt; OXID oxid;
TRACE("(...,%s,....)\n", debugstr_guid(riid));
/* we need an apartment to unmarshal into */ - if (!apt) + if (!(apt = apartment_get_current_or_mta())) { ERR("Apartment not initialized\n"); return CO_E_NOTINITIALIZED; @@ -1339,10 +1343,18 @@ StdMarshalImpl_UnmarshalInterface(IMarshal *iface, IStream *pStm, REFIID riid, v
/* read STDOBJREF from wire */ hres = IStream_Read(pStm, &stdobjref, sizeof(stdobjref), &res); - if (hres != S_OK) return STG_E_READFAULT; + if (hres != S_OK) + { + apartment_release(apt); + return STG_E_READFAULT; + }
hres = apartment_getoxid(apt, &oxid); - if (hres != S_OK) return hres; + if (hres != S_OK) + { + apartment_release(apt); + return hres; + }
/* check if we're marshalling back to ourselves */ if ((oxid == stdobjref.oxid) && (stubmgr = get_stub_manager(apt, stdobjref.oid))) @@ -1357,6 +1369,7 @@ StdMarshalImpl_UnmarshalInterface(IMarshal *iface, IStream *pStm, REFIID riid, v stub_manager_ext_release(stubmgr, stdobjref.cPublicRefs, stdobjref.flags & SORFP_TABLEWEAK, FALSE);
stub_manager_int_release(stubmgr); + apartment_release(apt); return hres; }
@@ -1395,6 +1408,7 @@ StdMarshalImpl_UnmarshalInterface(IMarshal *iface, IStream *pStm, REFIID riid, v if (hres != S_OK) WARN("Failed with error 0x%08x\n", hres); else TRACE("Successfully created proxy %p\n", *ppv);
+ apartment_release(apt); return hres; }
diff --git a/dlls/ole32/rpc.c b/dlls/ole32/rpc.c index 8d8276e..5a7626b 100644 --- a/dlls/ole32/rpc.c +++ b/dlls/ole32/rpc.c @@ -830,14 +830,16 @@ static HRESULT WINAPI ClientRpcChannelBuffer_SendReceive(LPRPCCHANNELBUFFER ifac ORPC_EXTENT_ARRAY orpc_ext_array; WIRE_ORPC_EXTENT *first_wire_orpc_extent = NULL; HRESULT hrFault = S_OK; + APARTMENT *apt = apartment_get_current_or_mta();
TRACE("(%p) iMethod=%d\n", olemsg, olemsg->iMethod);
- hr = ClientRpcChannelBuffer_IsCorrectApartment(This, COM_CurrentApt()); + hr = ClientRpcChannelBuffer_IsCorrectApartment(This, apt); if (hr != S_OK) { ERR("called from wrong apartment, should have been 0x%s\n", wine_dbgstr_longlong(This->oxid)); + if (apt) apartment_release(apt); return RPC_E_WRONG_THREAD; } /* This situation should be impossible in multi-threaded apartments, @@ -845,11 +847,12 @@ static HRESULT WINAPI ClientRpcChannelBuffer_SendReceive(LPRPCCHANNELBUFFER ifac * 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 && + if (!apt->multi_threaded && COM_CurrentInfo()->pending_call_count_client && InSendMessage()) { ERR("can't make an outgoing COM call in response to a sent message\n"); + apartment_release(apt); return RPC_E_CANTCALLOUT_ININPUTSYNCCALL; }
@@ -967,6 +970,7 @@ static HRESULT WINAPI ClientRpcChannelBuffer_SendReceive(LPRPCCHANNELBUFFER ifac
TRACE("-- 0x%08x\n", hr);
+ apartment_release(apt); return hr; }
@@ -1094,7 +1098,7 @@ static const IRpcChannelBufferVtbl ServerRpcChannelBufferVtbl = HRESULT RPC_CreateClientChannel(const OXID *oxid, const IPID *ipid, const OXID_INFO *oxid_info, DWORD dest_context, void *dest_context_data, - IRpcChannelBuffer **chan) + IRpcChannelBuffer **chan, APARTMENT *apt) { ClientRpcChannelBuffer *This; WCHAR endpoint[200]; @@ -1148,7 +1152,7 @@ HRESULT RPC_CreateClientChannel(const OXID *oxid, const IPID *ipid, This->super.dest_context = dest_context; This->super.dest_context_data = dest_context_data; This->bind = bind; - apartment_getoxid(COM_CurrentApt(), &This->oxid); + apartment_getoxid(apt, &This->oxid); This->server_pid = oxid_info->dwPid; This->event = NULL;
diff --git a/dlls/ole32/tests/marshal.c b/dlls/ole32/tests/marshal.c index 12c46e9..c5c69f0 100644 --- a/dlls/ole32/tests/marshal.c +++ b/dlls/ole32/tests/marshal.c @@ -3419,6 +3419,119 @@ static void test_manualresetevent(void) ok(!ref, "Got nonzero ref: %d\n", ref); }
+static DWORD CALLBACK implicit_mta_unmarshal_proc(void *param) +{ + IStream *stream = param; + IClassFactory *cf; + IUnknown *proxy; + HRESULT hr; + + IStream_Seek(stream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoUnmarshalInterface(stream, &IID_IClassFactory, (void **)&cf); + ok_ole_success(hr, CoUnmarshalInterface); + + hr = IClassFactory_CreateInstance(cf, NULL, &IID_IUnknown, (void **)&proxy); + ok_ole_success(hr, IClassFactory_CreateInstance); + + IUnknown_Release(proxy); + + /* But if we initialize an STA in this apartment, it becomes the wrong one. */ + CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + + hr = IClassFactory_CreateInstance(cf, NULL, &IID_IUnknown, (void **)&proxy); + ok(hr == RPC_E_WRONG_THREAD, "got %#x\n", hr); + + CoUninitialize(); + + ok_more_than_one_lock(); + ok_non_zero_external_conn(); + + IClassFactory_Release(cf); + + ok_no_locks(); + ok_zero_external_conn(); + ok_last_release_closes(TRUE); + return 0; +} + +static DWORD CALLBACK implicit_mta_use_proc(void *param) +{ + IClassFactory *cf = param; + IUnknown *proxy; + HRESULT hr; + + hr = IClassFactory_CreateInstance(cf, NULL, &IID_IUnknown, (void **)&proxy); + ok_ole_success(hr, IClassFactory_CreateInstance); + + IUnknown_Release(proxy); + + /* But if we initialize an STA in this apartment, it becomes the wrong one. */ + CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + + hr = IClassFactory_CreateInstance(cf, NULL, &IID_IUnknown, (void **)&proxy); + ok(hr == RPC_E_WRONG_THREAD, "got %#x\n", hr); + + CoUninitialize(); + return 0; +} + +static void test_implicit_mta(void) +{ + HANDLE host_thread, thread; + IClassFactory *cf; + IStream *stream; + HRESULT hr; + DWORD tid; + + cLocks = 0; + external_connections = 0; + + CoInitializeEx(NULL, COINIT_MULTITHREADED); + + /* Firstly: we can unmarshal and use an object while in the implicit MTA. */ + hr = CreateStreamOnHGlobal(NULL, TRUE, &stream); + ok_ole_success(hr, CreateStreamOnHGlobal); + tid = start_host_object(stream, &IID_IClassFactory, (IUnknown *)&Test_ClassFactory, MSHLFLAGS_NORMAL, &host_thread); + + ok_more_than_one_lock(); + ok_non_zero_external_conn(); + + thread = CreateThread(NULL, 0, implicit_mta_unmarshal_proc, stream, 0, NULL); + ok(!WaitForSingleObject(thread, 1000), "wait failed\n"); + CloseHandle(thread); + + IStream_Release(stream); + end_host_object(tid, host_thread); + + /* Secondly: we can unmarshal an object into the real MTA and then use it + * from the implicit MTA. */ + hr = CreateStreamOnHGlobal(NULL, TRUE, &stream); + ok_ole_success(hr, CreateStreamOnHGlobal); + tid = start_host_object(stream, &IID_IClassFactory, (IUnknown *)&Test_ClassFactory, MSHLFLAGS_NORMAL, &host_thread); + + ok_more_than_one_lock(); + ok_non_zero_external_conn(); + + IStream_Seek(stream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoUnmarshalInterface(stream, &IID_IClassFactory, (void **)&cf); + ok_ole_success(hr, CoUnmarshalInterface); + + thread = CreateThread(NULL, 0, implicit_mta_use_proc, cf, 0, NULL); + ok(!WaitForSingleObject(thread, 1000), "wait failed\n"); + CloseHandle(thread); + + IClassFactory_Release(cf); + IStream_Release(stream); + + ok_no_locks(); + ok_non_zero_external_conn(); + ok_last_release_closes(TRUE); + + end_host_object(tid, host_thread); + + CoUninitialize(); +} + static const char *debugstr_iid(REFIID riid) { static char name[256]; @@ -3765,6 +3878,7 @@ START_TEST(marshal) register_test_window();
test_cocreateinstance_proxy(); + test_implicit_mta();
pCoInitializeEx(NULL, COINIT_APARTMENTTHREADED);