For React Native apps.
From: Zhiyi Zhang zzhang@codeweavers.com
--- include/objidlbase.idl | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+)
diff --git a/include/objidlbase.idl b/include/objidlbase.idl index bd5a9b12409..9a4eed4acb3 100644 --- a/include/objidlbase.idl +++ b/include/objidlbase.idl @@ -92,6 +92,26 @@ interface IAgileObject : IUnknown { }
+[ + local, + object, + uuid(c03f6a43-65a4-9818-987e-e0b810d2a6f2), + pointer_default(unique) +] +interface IAgileReference : IUnknown +{ + HRESULT Resolve([in] REFIID riid, [out, retval, iid_is(riid)] void **object); +} + +[ + local, + object, + uuid(ecc8691b-c1db-4dc0-855e-65f6c551af49) +] +interface INoMarshal : IUnknown +{ +} + [ local, object,
From: Zhiyi Zhang zzhang@codeweavers.com
--- dlls/combase/combase.spec | 2 +- dlls/combase/combase_private.h | 1 + dlls/combase/roapi.c | 182 +++++++++++++++++++++++++++++++++ dlls/ole32/ole32.spec | 1 + include/combaseapi.h | 7 ++ 5 files changed, 192 insertions(+), 1 deletion(-)
diff --git a/dlls/combase/combase.spec b/dlls/combase/combase.spec index b3f4d25b42c..4bdc14334e2 100644 --- a/dlls/combase/combase.spec +++ b/dlls/combase/combase.spec @@ -292,7 +292,7 @@ @ stub RoFreeParameterizedTypeExtra @ stub RoGetActivatableClassRegistration @ stdcall RoGetActivationFactory(ptr ptr ptr) -@ stub RoGetAgileReference +@ stdcall RoGetAgileReference(long ptr ptr ptr) @ stdcall RoGetApartmentIdentifier(ptr) @ stub RoGetErrorReportingFlags @ stub RoGetMatchingRestrictedErrorInfo diff --git a/dlls/combase/combase_private.h b/dlls/combase/combase_private.h index 04d251962ec..ca79356e8eb 100644 --- a/dlls/combase/combase_private.h +++ b/dlls/combase/combase_private.h @@ -96,6 +96,7 @@ struct tlsdata };
extern HRESULT WINAPI InternalTlsAllocData(struct tlsdata **data); +extern BOOL WINAPI InternalIsProcessInitialized(void);
static inline HRESULT com_get_tlsdata(struct tlsdata **data) { diff --git a/dlls/combase/roapi.c b/dlls/combase/roapi.c index 02d9e0b536e..0f8a9438821 100644 --- a/dlls/combase/roapi.c +++ b/dlls/combase/roapi.c @@ -233,6 +233,188 @@ HRESULT WINAPI RoActivateInstance(HSTRING classid, IInspectable **instance) return hr; }
+struct agile_reference +{ + IAgileReference IAgileReference_iface; + enum AgileReferenceOptions option; + IStream *marshal_stream; + CRITICAL_SECTION cs; + IUnknown *obj; + LONG ref; +}; + +static HRESULT marshal_object_in_agile_reference(struct agile_reference *ref, REFIID riid, IUnknown *obj) +{ + HRESULT hr; + + hr = CreateStreamOnHGlobal(0, TRUE, &ref->marshal_stream); + if (FAILED(hr)) + return hr; + + hr = CoMarshalInterface(ref->marshal_stream, riid, obj, MSHCTX_INPROC, NULL, MSHLFLAGS_TABLESTRONG); + if (FAILED(hr)) + { + IStream_Release(ref->marshal_stream); + ref->marshal_stream = NULL; + } + return hr; +} + +static inline struct agile_reference *impl_from_IAgileReference(IAgileReference *iface) +{ + return CONTAINING_RECORD(iface, struct agile_reference, IAgileReference_iface); +} + +static HRESULT WINAPI agile_ref_QueryInterface(IAgileReference *iface, REFIID riid, void **obj) +{ + TRACE("(%p, %s, %p)\n", iface, debugstr_guid(riid), obj); + + if (!riid || !obj) return E_INVALIDARG; + + if (IsEqualGUID(riid, &IID_IUnknown) + || IsEqualGUID(riid, &IID_IAgileObject) + || IsEqualGUID(riid, &IID_IAgileReference)) + { + IUnknown_AddRef(iface); + *obj = iface; + return S_OK; + } + + *obj = NULL; + FIXME("interface %s is not implemented\n", debugstr_guid(riid)); + return E_NOINTERFACE; +} + +static ULONG WINAPI agile_ref_AddRef(IAgileReference *iface) +{ + struct agile_reference *impl = impl_from_IAgileReference(iface); + return InterlockedIncrement(&impl->ref); +} + +static ULONG WINAPI agile_ref_Release(IAgileReference *iface) +{ + struct agile_reference *impl = impl_from_IAgileReference(iface); + LONG ref = InterlockedDecrement(&impl->ref); + + if (!ref) + { + TRACE("destroying %p\n", iface); + + if (impl->obj) + IUnknown_Release(impl->obj); + + if (impl->marshal_stream) + { + LARGE_INTEGER zero = {0}; + + IStream_Seek(impl->marshal_stream, zero, STREAM_SEEK_SET, NULL); + CoReleaseMarshalData(impl->marshal_stream); + IStream_Release(impl->marshal_stream); + } + DeleteCriticalSection(&impl->cs); + free(impl); + } + + return ref; +} + +static HRESULT WINAPI agile_ref_Resolve(IAgileReference *iface, REFIID riid, void **obj) +{ + struct agile_reference *impl = impl_from_IAgileReference(iface); + LARGE_INTEGER zero = {0}; + HRESULT hr; + + TRACE("(%p, %s, %p)\n", iface, debugstr_guid(riid), obj); + + EnterCriticalSection(&impl->cs); + if (impl->option == AGILEREFERENCE_DELAYEDMARSHAL && impl->marshal_stream == NULL) + { + if (FAILED(hr = marshal_object_in_agile_reference(impl, riid, impl->obj))) + { + LeaveCriticalSection(&impl->cs); + return hr; + } + + IUnknown_Release(impl->obj); + impl->obj = NULL; + } + + if (SUCCEEDED(hr = IStream_Seek(impl->marshal_stream, zero, STREAM_SEEK_SET, NULL))) + hr = CoUnmarshalInterface(impl->marshal_stream, riid, obj); + + LeaveCriticalSection(&impl->cs); + return hr; +} + +static const IAgileReferenceVtbl agile_ref_vtbl = +{ + agile_ref_QueryInterface, + agile_ref_AddRef, + agile_ref_Release, + agile_ref_Resolve, +}; + +/*********************************************************************** + * RoGetAgileReference (combase.@) + */ +HRESULT WINAPI RoGetAgileReference(enum AgileReferenceOptions option, REFIID riid, IUnknown *obj, + IAgileReference **agile_reference) +{ + struct agile_reference *impl; + IUnknown *unknown; + HRESULT hr; + + TRACE("(%d, %s, %p, %p).\n", option, debugstr_guid(riid), obj, agile_reference); + + if (option != AGILEREFERENCE_DEFAULT && option != AGILEREFERENCE_DELAYEDMARSHAL) + return E_INVALIDARG; + + if (!InternalIsProcessInitialized()) + { + ERR("Apartment not initialized\n"); + return CO_E_NOTINITIALIZED; + } + + hr = IUnknown_QueryInterface(obj, riid, (void **)&unknown); + if (FAILED(hr)) + return E_NOINTERFACE; + IUnknown_Release(unknown); + + hr = IUnknown_QueryInterface(obj, &IID_INoMarshal, (void **)&unknown); + if (SUCCEEDED(hr)) + { + IUnknown_Release(unknown); + return CO_E_NOT_SUPPORTED; + } + + impl = calloc(1, sizeof(*impl)); + if (!impl) + return E_OUTOFMEMORY; + + impl->IAgileReference_iface.lpVtbl = &agile_ref_vtbl; + impl->option = option; + impl->ref = 1; + + if (option == AGILEREFERENCE_DEFAULT) + { + if (FAILED(hr = marshal_object_in_agile_reference(impl, riid, obj))) + { + free(impl); + return hr; + } + } + else if (option == AGILEREFERENCE_DELAYEDMARSHAL) + { + impl->obj = obj; + IUnknown_AddRef(impl->obj); + } + + InitializeCriticalSection(&impl->cs); + + *agile_reference = &impl->IAgileReference_iface; + return S_OK; +} + /*********************************************************************** * RoGetApartmentIdentifier (combase.@) */ diff --git a/dlls/ole32/ole32.spec b/dlls/ole32/ole32.spec index e9982036eeb..7ec85fc963f 100644 --- a/dlls/ole32/ole32.spec +++ b/dlls/ole32/ole32.spec @@ -254,6 +254,7 @@ @ stdcall RegisterDragDrop(long ptr) @ stdcall ReleaseStgMedium(ptr) @ stdcall RevokeDragDrop(long) +@ stdcall RoGetAgileReference(long ptr ptr ptr) combase.RoGetAgileReference @ stdcall SNB_UserFree(ptr ptr) @ stdcall SNB_UserMarshal(ptr ptr ptr) @ stdcall SNB_UserSize(ptr long ptr) diff --git a/include/combaseapi.h b/include/combaseapi.h index 4b8cb457328..41461f22e98 100644 --- a/include/combaseapi.h +++ b/include/combaseapi.h @@ -44,7 +44,14 @@ typedef struct tagServerInformation UINT64 ui64ServerAddress; } ServerInformation, *PServerInformation;
+enum AgileReferenceOptions +{ + AGILEREFERENCE_DEFAULT, + AGILEREFERENCE_DELAYEDMARSHAL +}; + HRESULT WINAPI CoDecodeProxy(DWORD client_pid, UINT64 proxy_addr, ServerInformation *server_info); +HRESULT WINAPI RoGetAgileReference(enum AgileReferenceOptions options, REFIID riid, IUnknown *obj, IAgileReference **agile_reference);
#ifdef __cplusplus }
From: Zhiyi Zhang zzhang@codeweavers.com
--- dlls/combase/tests/Makefile.in | 2 +- dlls/combase/tests/roapi.c | 227 +++++++++++++++++++++++++++++++++ 2 files changed, 228 insertions(+), 1 deletion(-)
diff --git a/dlls/combase/tests/Makefile.in b/dlls/combase/tests/Makefile.in index 3d09ab822d5..21597b38d51 100644 --- a/dlls/combase/tests/Makefile.in +++ b/dlls/combase/tests/Makefile.in @@ -1,5 +1,5 @@ TESTDLL = combase.dll -IMPORTS = combase +IMPORTS = combase uuid user32
SOURCES = \ combase.rc \ diff --git a/dlls/combase/tests/roapi.c b/dlls/combase/tests/roapi.c index abc03176db0..7606c61c165 100644 --- a/dlls/combase/tests/roapi.c +++ b/dlls/combase/tests/roapi.c @@ -28,6 +28,32 @@
#include "wine/test.h"
+#define EXPECT_REF(obj,ref) _expect_ref((IUnknown*)obj, ref, __LINE__) +static void _expect_ref(IUnknown* obj, ULONG ref, int line) +{ + ULONG rc; + IUnknown_AddRef(obj); + rc = IUnknown_Release(obj); + ok_(__FILE__,line)(rc == ref, "expected refcount %ld, got %ld\n", ref, rc); +} + +static void flush_events(void) +{ + int diff = 200; + DWORD time; + MSG msg; + + time = GetTickCount() + diff; + while (diff > 0) + { + if (MsgWaitForMultipleObjects(0, NULL, FALSE, 100, QS_ALLINPUT) == WAIT_TIMEOUT) + break; + while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) + DispatchMessageA(&msg); + diff = time - GetTickCount(); + } +} + static void load_resource(const WCHAR *filename) { DWORD written; @@ -311,6 +337,206 @@ static void test_implicit_mta(void) WindowsDeleteString(str); }
+struct unk_impl +{ + IUnknown IUnknown_iface; + LONG ref; +}; + +static inline struct unk_impl *impl_from_IUnknown(IUnknown *iface) +{ + return CONTAINING_RECORD(iface, struct unk_impl, IUnknown_iface); +} + +static HRESULT WINAPI unk_QueryInterface(IUnknown *iface, REFIID riid, void **ppv) +{ + if (IsEqualGUID(riid, &IID_IUnknown)) + { + *ppv = iface; + IUnknown_AddRef(iface); + return S_OK; + } + + return E_NOINTERFACE; +} + +static HRESULT WINAPI unk_no_marshal_QueryInterface(IUnknown *iface, REFIID riid, void **ppv) +{ + if (IsEqualGUID(riid, &IID_IUnknown) + || IsEqualGUID(riid, &IID_INoMarshal)) + { + *ppv = iface; + IUnknown_AddRef(iface); + return S_OK; + } + + return E_NOINTERFACE; +} + +static ULONG WINAPI unk_AddRef(IUnknown *iface) +{ + struct unk_impl *impl = impl_from_IUnknown(iface); + return InterlockedIncrement(&impl->ref); +} + +static ULONG WINAPI unk_Release(IUnknown *iface) +{ + struct unk_impl *impl = impl_from_IUnknown(iface); + return InterlockedDecrement(&impl->ref); +} + +static const IUnknownVtbl unk_vtbl = +{ + unk_QueryInterface, + unk_AddRef, + unk_Release +}; + +static const IUnknownVtbl unk_no_marshal_vtbl = +{ + unk_no_marshal_QueryInterface, + unk_AddRef, + unk_Release +}; + +struct test_RoGetAgileReference_thread_param +{ + enum AgileReferenceOptions option; + RO_INIT_TYPE from_type; + RO_INIT_TYPE to_type; + IAgileReference *agile_reference; + IUnknown *unk_obj; +}; + +static DWORD CALLBACK test_RoGetAgileReference_thread_proc(void *arg) +{ + struct test_RoGetAgileReference_thread_param *param = (struct test_RoGetAgileReference_thread_param *)arg; + IUnknown *unknown; + HRESULT hr; + + winetest_push_context("%d %d %d", param->option, param->from_type, param->to_type); + + RoInitialize(param->to_type); + + unknown = NULL; + hr = IAgileReference_Resolve(param->agile_reference, &IID_IUnknown, (void **)&unknown); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(!!unknown, "Expected pointer not NULL.\n"); + if (param->from_type == RO_INIT_MULTITHREADED && param->to_type == RO_INIT_MULTITHREADED) + { + ok(unknown == param->unk_obj, "Expected the same object.\n"); + todo_wine_if(param->option == AGILEREFERENCE_DEFAULT) + EXPECT_REF(param->unk_obj, param->option == AGILEREFERENCE_DEFAULT ? 5 : 6); + } + else + { + ok(unknown != param->unk_obj, "Expected a different object pointer.\n"); + todo_wine_if(param->option == AGILEREFERENCE_DEFAULT) + EXPECT_REF(param->unk_obj, param->option == AGILEREFERENCE_DEFAULT ? 4 : 5); + EXPECT_REF(unknown, 1); + } + IUnknown_Release(unknown); + + RoUninitialize(); + winetest_pop_context(); + 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}; + enum AgileReferenceOptions option; + IAgileReference *agile_reference; + RO_INIT_TYPE from_type, to_type; + IUnknown *unknown, *unknown2; + IAgileObject *agile_object; + HANDLE thread; + HRESULT hr; + DWORD ret; + + for (option = AGILEREFERENCE_DEFAULT; option <= AGILEREFERENCE_DELAYEDMARSHAL; option++) + { + for (from_type = RO_INIT_SINGLETHREADED; from_type <= RO_INIT_MULTITHREADED; from_type++) + { + winetest_push_context("%d %d", option, from_type); + + hr = RoGetAgileReference(option, &IID_IUnknown, &unk_obj.IUnknown_iface, &agile_reference); + ok(hr == CO_E_NOTINITIALIZED, "Got unexpected hr %#lx.\n", hr); + + RoInitialize(from_type); + + agile_reference = NULL; + EXPECT_REF(&unk_obj, 1); + + /* Invalid option */ + hr = RoGetAgileReference(AGILEREFERENCE_DELAYEDMARSHAL + 1, &IID_IUnknown, &unk_obj.IUnknown_iface, &agile_reference); + ok(hr == E_INVALIDARG, "Got unexpected hr %#lx.\n", hr); + + /* Non-existent interface for the object */ + hr = RoGetAgileReference(option, &IID_IActivationFactory, &unk_obj.IUnknown_iface, &agile_reference); + ok(hr == E_NOINTERFACE, "Got unexpected hr %#lx.\n", hr); + + /* Objects that implements INoMarshal */ + hr = RoGetAgileReference(option, &IID_IUnknown, &unk_no_marshal_obj.IUnknown_iface, &agile_reference); + ok(hr == CO_E_NOT_SUPPORTED, "Got unexpected hr %#lx.\n", hr); + + /* Create agile reference object */ + hr = RoGetAgileReference(option, &IID_IUnknown, &unk_obj.IUnknown_iface, &agile_reference); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(!!agile_reference, "Got unexpected agile_reference.\n"); + todo_wine_if(option == AGILEREFERENCE_DEFAULT) + EXPECT_REF(&unk_obj, 2); + + /* Check the created agile reference object has IAgileObject */ + hr = IAgileReference_QueryInterface(agile_reference, &IID_IAgileObject, (void **)&agile_object); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + IAgileObject_Release(agile_object); + + /* Resolve once */ + unknown = NULL; + hr = IAgileReference_Resolve(agile_reference, &IID_IUnknown, (void **)&unknown); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(!!unknown, "Expected pointer not NULL.\n"); + ok(unknown == &unk_obj.IUnknown_iface, "Expected the same object.\n"); + todo_wine + EXPECT_REF(&unk_obj, 3); + + /* Resolve twice */ + unknown2 = NULL; + hr = IAgileReference_Resolve(agile_reference, &IID_IUnknown, (void **)&unknown2); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + ok(!!unknown2, "Expected pointer not NULL.\n"); + ok(unknown2 == &unk_obj.IUnknown_iface, "Expected the same object.\n"); + todo_wine + EXPECT_REF(&unk_obj, 4); + + /* Resolve in another apartment */ + for (to_type = RO_INIT_SINGLETHREADED; to_type <= RO_INIT_MULTITHREADED; to_type++) + { + param.option = option; + param.from_type = from_type; + param.to_type = to_type; + param.agile_reference = agile_reference; + param.unk_obj = &unk_obj.IUnknown_iface; + thread = CreateThread(NULL, 0, test_RoGetAgileReference_thread_proc, ¶m, 0, NULL); + flush_events(); + ret = WaitForSingleObject(thread, 100); + ok(!ret, "WaitForSingleObject failed, error %ld.\n", GetLastError()); + } + + IUnknown_Release(unknown2); + IUnknown_Release(unknown); + IAgileReference_Release(agile_reference); + EXPECT_REF(&unk_obj, 1); + + RoUninitialize(); + winetest_pop_context(); + } + } +} + START_TEST(roapi) { BOOL ret; @@ -319,6 +545,7 @@ START_TEST(roapi)
test_implicit_mta(); test_ActivationFactories(); + test_RoGetAgileReference();
SetLastError(0xdeadbeef); ret = DeleteFileW(L"wine.combase.test.dll");
This merge request was approved by Huw Davies.