Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/Makefile.in | 1 + dlls/uiautomationcore/uia_main.c | 131 +++++++++++++++++++++++++++++- 2 files changed, 130 insertions(+), 2 deletions(-)
diff --git a/dlls/uiautomationcore/Makefile.in b/dlls/uiautomationcore/Makefile.in index 71ea7b99c94..5a72ea144c4 100644 --- a/dlls/uiautomationcore/Makefile.in +++ b/dlls/uiautomationcore/Makefile.in @@ -1,5 +1,6 @@ MODULE = uiautomationcore.dll IMPORTLIB = uiautomationcore +IMPORTS = uuid ole32
EXTRADLLFLAGS = -Wb,--prefer-native
diff --git a/dlls/uiautomationcore/uia_main.c b/dlls/uiautomationcore/uia_main.c index 2dada95af80..77cacb7c4a4 100644 --- a/dlls/uiautomationcore/uia_main.c +++ b/dlls/uiautomationcore/uia_main.c @@ -16,12 +16,134 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#define COBJMACROS + #include "uiautomation.h"
#include "wine/debug.h" +#include "wine/heap.h"
WINE_DEFAULT_DEBUG_CHANNEL(uiautomation);
+struct uia_object_wrapper +{ + IUnknown IUnknown_iface; + LONG refcount; + + IUnknown *marshaler; + IUnknown *marshal_object; +}; + +static struct uia_object_wrapper *impl_uia_object_wrapper_from_IUnknown(IUnknown *iface) +{ + return CONTAINING_RECORD(iface, struct uia_object_wrapper, IUnknown_iface); +} + +static HRESULT WINAPI uia_object_wrapper_QueryInterface(IUnknown *iface, + REFIID riid, void **ppv) +{ + struct uia_object_wrapper *wrapper = impl_uia_object_wrapper_from_IUnknown(iface); + return IUnknown_QueryInterface(wrapper->marshal_object, riid, ppv); +} + +static ULONG WINAPI uia_object_wrapper_AddRef(IUnknown *iface) +{ + struct uia_object_wrapper *wrapper = impl_uia_object_wrapper_from_IUnknown(iface); + ULONG refcount = InterlockedIncrement(&wrapper->refcount); + + TRACE("%p, refcount %d\n", iface, refcount); + + return refcount; +} + +static ULONG WINAPI uia_object_wrapper_Release(IUnknown *iface) +{ + struct uia_object_wrapper *wrapper = impl_uia_object_wrapper_from_IUnknown(iface); + ULONG refcount = InterlockedDecrement(&wrapper->refcount); + + TRACE("%p, refcount %d\n", iface, refcount); + if (!refcount) + { + IUnknown_Release(wrapper->marshaler); + heap_free(wrapper); + } + + return refcount; +} + +static const IUnknownVtbl uia_object_wrapper_vtbl = { + uia_object_wrapper_QueryInterface, + uia_object_wrapper_AddRef, + uia_object_wrapper_Release, +}; + +/* + * When passing the ReservedNotSupportedValue/ReservedMixedAttributeValue + * interface pointers across apartments within the same process, create a free + * threaded marshaler so that the pointer value is preserved. + */ +static HRESULT create_uia_object_wrapper(IUnknown *reserved, void **ppv) +{ + struct uia_object_wrapper *wrapper; + HRESULT hr; + + TRACE("%p, %p\n", reserved, ppv); + + wrapper = heap_alloc(sizeof(*wrapper)); + if (!wrapper) + return E_OUTOFMEMORY; + + wrapper->IUnknown_iface.lpVtbl = &uia_object_wrapper_vtbl; + wrapper->marshal_object = reserved; + wrapper->refcount = 1; + + if (FAILED(hr = CoCreateFreeThreadedMarshaler(&wrapper->IUnknown_iface, &wrapper->marshaler))) + { + heap_free(wrapper); + return hr; + } + + hr = IUnknown_QueryInterface(wrapper->marshaler, &IID_IMarshal, ppv); + IUnknown_Release(&wrapper->IUnknown_iface); + + return hr; +} + +/* + * UiaReservedNotSupportedValue object. + */ +static HRESULT WINAPI uia_reserved_obj_QueryInterface(IUnknown *iface, + REFIID riid, void **ppv) +{ + *ppv = NULL; + if (IsEqualIID(riid, &IID_IUnknown)) + *ppv = iface; + else if (IsEqualIID(riid, &IID_IMarshal)) + return create_uia_object_wrapper(iface, ppv); + else + return E_NOINTERFACE; + + return S_OK; +} + +static ULONG WINAPI uia_reserved_obj_AddRef(IUnknown *iface) +{ + return 1; +} + +static ULONG WINAPI uia_reserved_obj_Release(IUnknown *iface) +{ + return 1; +} + +static const IUnknownVtbl uia_reserved_obj_vtbl = { + uia_reserved_obj_QueryInterface, + uia_reserved_obj_AddRef, + uia_reserved_obj_Release, +}; + +static IUnknown uia_reserved_ns_iface = {&uia_reserved_obj_vtbl}; + /*********************************************************************** * UiaClientsAreListening (uiautomationcore.@) */ @@ -46,8 +168,13 @@ HRESULT WINAPI UiaGetReservedMixedAttributeValue(IUnknown **value) */ HRESULT WINAPI UiaGetReservedNotSupportedValue(IUnknown **value) { - FIXME("(%p) stub!\n", value); - *value = NULL; + TRACE("(%p)\n", value); + + if (!value) + return E_INVALIDARG; + + *value = &uia_reserved_ns_iface; + return S_OK; }
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/uia_main.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/dlls/uiautomationcore/uia_main.c b/dlls/uiautomationcore/uia_main.c index 77cacb7c4a4..174e4756920 100644 --- a/dlls/uiautomationcore/uia_main.c +++ b/dlls/uiautomationcore/uia_main.c @@ -110,7 +110,7 @@ static HRESULT create_uia_object_wrapper(IUnknown *reserved, void **ppv) }
/* - * UiaReservedNotSupportedValue object. + * UiaReservedNotSupportedValue/UiaReservedMixedAttributeValue object. */ static HRESULT WINAPI uia_reserved_obj_QueryInterface(IUnknown *iface, REFIID riid, void **ppv) @@ -143,6 +143,7 @@ static const IUnknownVtbl uia_reserved_obj_vtbl = { };
static IUnknown uia_reserved_ns_iface = {&uia_reserved_obj_vtbl}; +static IUnknown uia_reserved_ma_iface = {&uia_reserved_obj_vtbl};
/*********************************************************************** * UiaClientsAreListening (uiautomationcore.@) @@ -158,8 +159,13 @@ BOOL WINAPI UiaClientsAreListening(void) */ HRESULT WINAPI UiaGetReservedMixedAttributeValue(IUnknown **value) { - FIXME("(%p) stub!\n", value); - *value = NULL; + TRACE("(%p)\n", value); + + if (!value) + return E_INVALIDARG; + + *value = &uia_reserved_ma_iface; + return S_OK; }
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/Makefile.in | 2 +- dlls/uiautomationcore/tests/uiautomation.c | 133 +++++++++++++++++++++ 2 files changed, 134 insertions(+), 1 deletion(-)
diff --git a/dlls/uiautomationcore/tests/Makefile.in b/dlls/uiautomationcore/tests/Makefile.in index c39b062b6fd..1d08e19093f 100644 --- a/dlls/uiautomationcore/tests/Makefile.in +++ b/dlls/uiautomationcore/tests/Makefile.in @@ -1,5 +1,5 @@ TESTDLL = uiautomationcore.dll -IMPORTS = uiautomationcore user32 +IMPORTS = uiautomationcore user32 ole32
C_SRCS = \ uiautomation.c diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index cbcba1af294..52a6648dad2 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -86,7 +86,140 @@ if (hr == S_OK) UnregisterClassA("HostProviderFromHwnd class", NULL); }
+static DWORD WINAPI uia_reserved_val_iface_marshal_thread(LPVOID param) +{ + IStream **stream = param; + IUnknown *unk_ns, *unk_ns2, *unk_ma, *unk_ma2; + HRESULT hr; + + CoInitializeEx(NULL, COINIT_MULTITHREADED); + + hr = CoGetInterfaceAndReleaseStream(stream[0], &IID_IUnknown, (void **)&unk_ns); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + + hr = CoGetInterfaceAndReleaseStream(stream[1], &IID_IUnknown, (void **)&unk_ma); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + + hr = UiaGetReservedNotSupportedValue(&unk_ns2); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + + hr = UiaGetReservedMixedAttributeValue(&unk_ma2); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + + ok(unk_ns2 == unk_ns, "UiaGetReservedNotSupported pointer mismatch, unk_ns2 %p, unk_ns %p\n", unk_ns2, unk_ns); + ok(unk_ma2 == unk_ma, "UiaGetReservedMixedAttribute pointer mismatch, unk_ma2 %p, unk_ma %p\n", unk_ma2, unk_ma); + + CoUninitialize(); + + return 0; +} + +static void test_uia_reserved_value_ifaces(void) +{ + IUnknown *unk_ns, *unk_ns2, *unk_ma, *unk_ma2; + IStream *stream[2]; + IMarshal *marshal; + HANDLE thread; + ULONG refcnt; + HRESULT hr; + + /* ReservedNotSupportedValue. */ + hr = UiaGetReservedNotSupportedValue(NULL); + ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr); + + hr = UiaGetReservedNotSupportedValue(&unk_ns); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + ok(unk_ns != NULL, "UiaGetReservedNotSupportedValue returned NULL interface.\n"); + + refcnt = IUnknown_AddRef(unk_ns); + ok(refcnt == 1, "Expected refcnt %d, got %d\n", 1, refcnt); + + refcnt = IUnknown_AddRef(unk_ns); + ok(refcnt == 1, "Expected refcnt %d, got %d\n", 1, refcnt); + + refcnt = IUnknown_Release(unk_ns); + ok(refcnt == 1, "Expected refcnt %d, got %d\n", 1, refcnt); + + hr = UiaGetReservedNotSupportedValue(&unk_ns2); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + ok(unk_ns2 != NULL, "UiaGetReservedNotSupportedValue returned NULL interface."); + ok(unk_ns2 == unk_ns, "UiaGetReservedNotSupported pointer mismatch, unk_ns2 %p, unk_ns %p\n", unk_ns2, unk_ns); + + marshal = NULL; + hr = IUnknown_QueryInterface(unk_ns, &IID_IMarshal, (void **)&marshal); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + ok(marshal != NULL, "Failed to get IMarshal interface.\n"); + + refcnt = IMarshal_AddRef(marshal); + ok(refcnt == 2, "Expected refcnt %d, got %d\n", 2, refcnt); + + refcnt = IMarshal_Release(marshal); + ok(refcnt == 1, "Expected refcnt %d, got %d\n", 1, refcnt); + + refcnt = IMarshal_Release(marshal); + ok(refcnt == 0, "Expected refcnt %d, got %d\n", 0, refcnt); + + /* ReservedMixedAttributeValue. */ + hr = UiaGetReservedMixedAttributeValue(NULL); + ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr); + + hr = UiaGetReservedMixedAttributeValue(&unk_ma); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + ok(unk_ma != NULL, "UiaGetReservedMixedAttributeValue returned NULL interface."); + + refcnt = IUnknown_AddRef(unk_ma); + ok(refcnt == 1, "Expected refcnt %d, got %d\n", 1, refcnt); + + refcnt = IUnknown_AddRef(unk_ma); + ok(refcnt == 1, "Expected refcnt %d, got %d\n", 1, refcnt); + + refcnt = IUnknown_Release(unk_ma); + ok(refcnt == 1, "Expected refcnt %d, got %d\n", 1, refcnt); + + hr = UiaGetReservedMixedAttributeValue(&unk_ma2); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + ok(unk_ma2 != NULL, "UiaGetReservedMixedAttributeValue returned NULL interface."); + ok(unk_ma2 == unk_ma, "UiaGetReservedMixedAttribute pointer mismatch, unk_ma2 %p, unk_ma %p\n", unk_ma2, unk_ma); + + marshal = NULL; + hr = IUnknown_QueryInterface(unk_ma, &IID_IMarshal, (void **)&marshal); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + ok(marshal != NULL, "Failed to get IMarshal interface.\n"); + + refcnt = IMarshal_AddRef(marshal); + ok(refcnt == 2, "Expected refcnt %d, got %d\n", 2, refcnt); + + refcnt = IMarshal_Release(marshal); + ok(refcnt == 1, "Expected refcnt %d, got %d\n", 1, refcnt); + + refcnt = IMarshal_Release(marshal); + ok(refcnt == 0, "Expected refcnt %d, got %d\n", 0, refcnt); + + /* Test cross-thread marshaling behavior. */ + CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + + hr = CoMarshalInterThreadInterfaceInStream(&IID_IUnknown, unk_ns, &stream[0]); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + hr = CoMarshalInterThreadInterfaceInStream(&IID_IUnknown, unk_ma, &stream[1]); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + + thread = CreateThread(NULL, 0, uia_reserved_val_iface_marshal_thread, (void *)stream, 0, NULL); + while (MsgWaitForMultipleObjects(1, &thread, FALSE, INFINITE, QS_ALLINPUT) != WAIT_OBJECT_0) + { + MSG msg; + while(PeekMessageW(&msg, 0, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + } + CloseHandle(thread); + + CoUninitialize(); +} + START_TEST(uiautomation) { test_UiaHostProviderFromHwnd(); + test_uia_reserved_value_ifaces(); }
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com