[PATCH 5/6] ole32: Allow unmarshalling objects into an implicit MTA.
Zebediah Figura
z.figura12 at gmail.com
Mon Apr 9 21:15:51 CDT 2018
Signed-off-by: Zebediah Figura <z.figura12 at 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);
--
2.7.4
More information about the wine-devel
mailing list