From: Vibhav Pant vibhavp@gmail.com
When RoGetAgileReference is used with AGILEREFERENCE_DELAYEDMARSHAL, the returned IAgileReference also contains a reference to the apartment/context the object belongs to. Thus, when IAgileReference::Resolve() gets called, the object will get marshaled inside the owning apartment/context, not the caller's apartment. --- dlls/combase/tests/roapi.c | 177 +++++++++++++++++++++++++++++++++++++ include/roapi.h | 1 + 2 files changed, 178 insertions(+)
diff --git a/dlls/combase/tests/roapi.c b/dlls/combase/tests/roapi.c index e4100cb11bc..e9e320f2489 100644 --- a/dlls/combase/tests/roapi.c +++ b/dlls/combase/tests/roapi.c @@ -450,6 +450,120 @@ static const IUnknownVtbl unk_agile_vtbl = unk_Release };
+/* IUnknown implementation that has affinity to the STA it was created in. */ +struct unk_ctx_impl +{ + IUnknown IUnknown_iface; + BOOL todo; + UINT64 id; /* Identifier of the apartment this object belongs to. */ + ULONG_PTR context; /* The COM context this object belongs to. */ + APTTYPE type; /* APTTYPE of the apartment this object belongs to. */ + LONG ref; +}; + +static inline struct unk_ctx_impl *impl_unk_ctx_impl_from_IUnknown(IUnknown *iface) +{ + return CONTAINING_RECORD(iface, struct unk_ctx_impl, IUnknown_iface); +} + +#define test_apartment_context(impl) test_apartment_context_(__LINE__, impl) +static void test_apartment_context_(int line, const struct unk_ctx_impl *impl) +{ + APTTYPEQUALIFIER qualifier; + ULONG_PTR cur_ctx; + UINT64 cur_id; + APTTYPE type; + HRESULT hr; + + hr = CoGetContextToken(&cur_ctx); + ok_(__FILE__, line)(hr == S_OK, "CoGetContextToken failed, got hr %#lx\n", hr); + /* As this object has apartment-affinity, its methods can only be called from the apartment and context it was + * created in. */ + todo_wine_if(impl->todo) ok_(__FILE__, line)(cur_ctx == impl->context, "got cur_ctx %#Ix != %#Ix\n", cur_ctx, impl->context); + + hr = CoGetApartmentType(&type, &qualifier); + ok_(__FILE__, line)(hr == S_OK, "CoGetApartmentType failed, got hr %#lx\n", hr); + todo_wine_if(impl->todo) ok_(__FILE__, line)(type == impl->type, "got type %d\n", type); + + hr = RoGetApartmentIdentifier(&cur_id); + ok_(__FILE__, line)(hr == S_OK, "RoGetApartmentIdentifier failed, got hr %#lx\n", hr); + ok_(__FILE__, line)(cur_id == impl->id, "got cur_id %#I64x != %#I64x\n", cur_id, impl->id); +} + +static HRESULT WINAPI unk_ctx_impl_QueryInterface(IUnknown *iface, const GUID *iid, void **out) +{ + struct unk_ctx_impl *impl = impl_unk_ctx_impl_from_IUnknown(iface); + + if (winetest_debug > 1) + trace("(%p, %s, %p)\n", iface, debugstr_guid(iid), out); + + test_apartment_context(impl); + if (IsEqualGUID(iid, &IID_IUnknown) || IsEqualGUID(iid, &IID_IUnknown)) + { + IUnknown_AddRef((IUnknown *)(*out = &impl->IUnknown_iface)); + return S_OK; + } + + *out = NULL; + if (winetest_debug > 1) + trace("%s not implemeneted, returning E_NOINTERFACE\n", debugstr_guid(iid)); + return E_NOINTERFACE; +} + +static ULONG WINAPI unk_ctx_impl_AddRef(IUnknown *iface) +{ + struct unk_ctx_impl *impl = impl_unk_ctx_impl_from_IUnknown(iface); + + test_apartment_context(impl); + return InterlockedIncrement(&impl->ref); +} + +static ULONG WINAPI unk_ctx_impl_Release(IUnknown *iface) +{ + struct unk_ctx_impl *impl = impl_unk_ctx_impl_from_IUnknown(iface); + ULONG ref = InterlockedDecrement(&impl->ref); + + test_apartment_context(impl); + if (!ref) free(impl); + return ref; +} + +static const IUnknownVtbl unk_ctx_impl_IUnknown_vtbl = +{ + unk_ctx_impl_QueryInterface, + unk_ctx_impl_AddRef, + unk_ctx_impl_Release, +}; + +static HRESULT unk_ctx_impl_create(IUnknown **out) +{ + APTTYPEQUALIFIER qualifier; + struct unk_ctx_impl *impl; + HRESULT hr; + + if (!(impl = calloc(1, sizeof(*impl)))) return E_OUTOFMEMORY; + + impl->IUnknown_iface.lpVtbl = &unk_ctx_impl_IUnknown_vtbl; + if (FAILED(hr = CoGetContextToken(&impl->context))) + { + free(impl); + return hr; + } + if (FAILED(hr = CoGetApartmentType(&impl->type, &qualifier))) + { + free(impl); + return hr; + } + if (FAILED(hr = RoGetApartmentIdentifier(&impl->id))) + { + free(impl); + return hr; + } + impl->ref = 1; + *out = &impl->IUnknown_iface; + return S_OK; +} + struct test_RoGetAgileReference_thread_param { enum AgileReferenceOptions option; @@ -499,12 +613,39 @@ static DWORD CALLBACK test_RoGetAgileReference_thread_proc(void *arg) return 0; }
+struct test_agile_resolve_context_params +{ + RO_INIT_TYPE from_type; + RO_INIT_TYPE to_type; + IAgileReference *ref; +}; + +static DWORD CALLBACK test_agile_resolve_context(void *arg) +{ + struct test_agile_resolve_context_params *params = arg; + IUnknown *unknown; + HRESULT hr; + + RoInitialize(params->to_type); + + winetest_push_context("from_type=%d, to_type=%d", params->from_type, params->to_type); + hr = IAgileReference_Resolve(params->ref, &IID_IUnknown, (void **)&unknown); + todo_wine_if(params->to_type == RO_INIT_MULTITHREADED) ok(hr == S_OK, "got hr %#lx\n", hr); + if (SUCCEEDED(hr)) + IUnknown_Release(unknown); + winetest_pop_context(); + + RoUninitialize(); + return 0; +} + static void test_RoGetAgileReference(void) { struct test_RoGetAgileReference_thread_param param; struct unk_impl unk_no_marshal_obj = {{&unk_no_marshal_vtbl}, 1}; struct unk_impl unk_obj = {{&unk_vtbl}, 1}; struct unk_impl unk_agile_obj = {{&unk_agile_vtbl}, 1}; + struct unk_ctx_impl *unk_ctx_impl; enum AgileReferenceOptions option; IAgileReference *agile_reference; RO_INIT_TYPE from_type, to_type; @@ -624,6 +765,42 @@ static void test_RoGetAgileReference(void) winetest_pop_context(); } } + + /* Tests specific to delayed marshaling */ + for (from_type = RO_INIT_SINGLETHREADED; from_type <= RO_INIT_MULTITHREADED; from_type++) + { + winetest_push_context("from_type=%d", from_type); + RoInitialize(from_type); + + hr = unk_ctx_impl_create(&unknown); + ok(hr == S_OK, "got hr %#lx\n", hr); + + unk_ctx_impl = impl_unk_ctx_impl_from_IUnknown(unknown); + hr = RoGetAgileReference(AGILEREFERENCE_DELAYEDMARSHAL, &IID_IUnknown, unknown, &agile_reference); + ok(hr == S_OK, "got hr %#lx\n", hr); + EXPECT_REF(unknown, 2); + + for (to_type = RO_INIT_SINGLETHREADED; to_type <= RO_INIT_MULTITHREADED; to_type++) + { + struct test_agile_resolve_context_params params = {from_type, to_type, agile_reference}; + + winetest_push_context("to_type=%d", to_type); + unk_ctx_impl->todo = TRUE; + thread = CreateThread(NULL, 0, test_agile_resolve_context, ¶ms, 0, NULL); + flush_events(); + ret = WaitForSingleObject(thread, 100); + ok(!ret, "got ret %lu\n", ret); + CloseHandle(thread); + unk_ctx_impl->todo = FALSE; + winetest_pop_context(); + } + + IAgileReference_Release(agile_reference); + EXPECT_REF(unknown, 1); + IUnknown_Release(unknown); + RoUninitialize(); + winetest_pop_context(); + } }
static void test_RoGetErrorReportingFlags(void) diff --git a/include/roapi.h b/include/roapi.h index bcaa8e9fea0..18baf9e2017 100644 --- a/include/roapi.h +++ b/include/roapi.h @@ -46,6 +46,7 @@ HRESULT WINAPI RoActivateInstance(HSTRING classid, IInspectable **instance); HRESULT WINAPI RoGetActivationFactory(HSTRING classid, REFIID iid, void **class_factory); HRESULT WINAPI RoInitialize(RO_INIT_TYPE type); void WINAPI RoUninitialize(void); +HRESULT WINAPI RoGetApartmentIdentifier(UINT64 *identifier);
#ifdef __cplusplus }