From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/Makefile.in | 3 + dlls/uiautomationcore/tests/uia_classes.idl | 36 +++++ dlls/uiautomationcore/tests/uiautomation.c | 80 +++++++++ dlls/uiautomationcore/uia_classes.idl | 15 ++ dlls/uiautomationcore/uia_com_client.c | 169 +++++++++++++++++++- 5 files changed, 299 insertions(+), 4 deletions(-) create mode 100644 dlls/uiautomationcore/tests/uia_classes.idl
diff --git a/dlls/uiautomationcore/tests/Makefile.in b/dlls/uiautomationcore/tests/Makefile.in index 53ed6f6e380..7cb4af9e7ff 100644 --- a/dlls/uiautomationcore/tests/Makefile.in +++ b/dlls/uiautomationcore/tests/Makefile.in @@ -3,3 +3,6 @@ IMPORTS = uiautomationcore user32 ole32 oleaut32 oleacc
C_SRCS = \ uiautomation.c + +IDL_SRCS = \ + uia_classes.idl diff --git a/dlls/uiautomationcore/tests/uia_classes.idl b/dlls/uiautomationcore/tests/uia_classes.idl new file mode 100644 index 00000000000..24addb2b820 --- /dev/null +++ b/dlls/uiautomationcore/tests/uia_classes.idl @@ -0,0 +1,36 @@ +/* + * Copyright 2023 Connor McAdams for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#pragma makedep header + +import "oaidl.idl"; + +/* + * Undocumented interface used by UI Automation to get the struct UiaCondition + * from an IUIAutomationCondition interface. + */ +[ + local, + object, + uuid(554a6cee-b482-4e17-9a13-7afb8b0d27e1), + pointer_default(unique), +] +interface IUIAutomationConditionInternal : IUnknown +{ + HRESULT get_condition_struct([out, retval]struct UiaCondition **ret_val); +} diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index b33a09d4f11..ca2529d87af 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -25,6 +25,7 @@ #include "uiautomation.h" #include "ocidl.h" #include "wine/iaccessible2.h" +#include "uia_classes.h"
#include "wine/test.h"
@@ -10089,6 +10090,84 @@ static void test_CUIAutomation_value_conversion(IUIAutomation *uia_iface) SafeArrayDestroy(sa); }
+static void test_CUIAutomation_condition_ifaces(IUIAutomation *uia_iface) +{ + IUIAutomationConditionInternal *internal_cond; + IUIAutomationBoolCondition *bool_cond; + struct UiaCondition *cond_struct; + IUIAutomationCondition *cond; + BOOL tmp_b; + HRESULT hr; + + hr = IUIAutomation_CreateTrueCondition(uia_iface, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + cond = NULL; + hr = IUIAutomation_CreateTrueCondition(uia_iface, &cond); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!cond, "cond == NULL\n"); + + internal_cond = NULL; + hr = IUIAutomationCondition_QueryInterface(cond, &IID_IUIAutomationConditionInternal, (void **)&internal_cond); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!internal_cond, "internal_cond == NULL\n"); + + cond_struct = NULL; + hr = IUIAutomationConditionInternal_get_condition_struct(internal_cond, &cond_struct); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!cond_struct, "cond_struct == NULL\n"); + ok(cond_struct->ConditionType == ConditionType_True, "Unexpected ConditionType %#x.\n", cond_struct->ConditionType); + IUIAutomationConditionInternal_Release(internal_cond); + + hr = IUIAutomationCondition_QueryInterface(cond, &IID_IUIAutomationBoolCondition, (void **)&bool_cond); + IUIAutomationCondition_Release(cond); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!bool_cond, "bool_cond == NULL\n"); + + hr = IUIAutomationBoolCondition_get_BooleanValue(bool_cond, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + tmp_b = FALSE; + hr = IUIAutomationBoolCondition_get_BooleanValue(bool_cond, &tmp_b); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(tmp_b == TRUE, "tmp_b != TRUE\n"); + IUIAutomationBoolCondition_Release(bool_cond); + + hr = IUIAutomation_CreateFalseCondition(uia_iface, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + cond = NULL; + hr = IUIAutomation_CreateFalseCondition(uia_iface, &cond); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!cond, "cond == NULL\n"); + + internal_cond = NULL; + hr = IUIAutomationCondition_QueryInterface(cond, &IID_IUIAutomationConditionInternal, (void **)&internal_cond); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!internal_cond, "internal_cond == NULL\n"); + + cond_struct = NULL; + hr = IUIAutomationConditionInternal_get_condition_struct(internal_cond, &cond_struct); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!cond_struct, "cond_struct == NULL\n"); + ok(cond_struct->ConditionType == ConditionType_False, "Unexpected ConditionType %#x.\n", cond_struct->ConditionType); + IUIAutomationConditionInternal_Release(internal_cond); + + hr = IUIAutomationCondition_QueryInterface(cond, &IID_IUIAutomationBoolCondition, (void **)&bool_cond); + IUIAutomationCondition_Release(cond); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!bool_cond, "bool_cond == NULL\n"); + + hr = IUIAutomationBoolCondition_get_BooleanValue(bool_cond, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + tmp_b = TRUE; + hr = IUIAutomationBoolCondition_get_BooleanValue(bool_cond, &tmp_b); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(tmp_b == FALSE, "tmp_b != FALSE\n"); + IUIAutomationBoolCondition_Release(bool_cond); +} + struct uia_com_classes { const GUID *clsid; const GUID *iid; @@ -10187,6 +10266,7 @@ static void test_CUIAutomation(void) IUnknown_Release(unk1); IUnknown_Release(unk2);
+ test_CUIAutomation_condition_ifaces(uia_iface); test_CUIAutomation_value_conversion(uia_iface); test_ElementFromHandle(uia_iface, has_cui8); test_Element_GetPropertyValue(uia_iface); diff --git a/dlls/uiautomationcore/uia_classes.idl b/dlls/uiautomationcore/uia_classes.idl index 7272a09bfd6..21dd0739388 100644 --- a/dlls/uiautomationcore/uia_classes.idl +++ b/dlls/uiautomationcore/uia_classes.idl @@ -45,6 +45,21 @@ struct uia_control_type_info { int control_type_id; };
+/* + * Undocumented interface used by UI Automation to get the struct UiaCondition + * from an IUIAutomationCondition interface. + */ +[ + local, + object, + uuid(554a6cee-b482-4e17-9a13-7afb8b0d27e1), + pointer_default(unique), +] +interface IUIAutomationConditionInternal : IUnknown +{ + HRESULT get_condition_struct([out, retval]struct UiaCondition **ret_val); +} + [ version(1.0), uuid(8a9ca8eb-856b-43d9-abd7-4a590054064f), diff --git a/dlls/uiautomationcore/uia_com_client.c b/dlls/uiautomationcore/uia_com_client.c index f8fdfaa6505..71c356305e4 100644 --- a/dlls/uiautomationcore/uia_com_client.c +++ b/dlls/uiautomationcore/uia_com_client.c @@ -23,6 +23,165 @@
WINE_DEFAULT_DEBUG_CHANNEL(uiautomation);
+/* + * Undocumented COM interface shared by all IUIAutomationCondition interfaces, + * used to get struct UiaCondition. + */ +struct uia_condition_internal { + IUIAutomationConditionInternal IUIAutomationConditionInternal_iface; + IUIAutomationCondition *condition; + + struct UiaCondition *condition_struct; +}; + +static inline struct uia_condition_internal *impl_from_IUIAutomationConditionInternal(IUIAutomationConditionInternal *iface) +{ + return CONTAINING_RECORD(iface, struct uia_condition_internal, IUIAutomationConditionInternal_iface); +} + +static HRESULT WINAPI uia_condition_internal_QueryInterface(IUIAutomationConditionInternal *iface, REFIID riid, void **ppv) +{ + struct uia_condition_internal *condition_internal = impl_from_IUIAutomationConditionInternal(iface); + return IUIAutomationCondition_QueryInterface(condition_internal->condition, riid, ppv); +} + +static ULONG WINAPI uia_condition_internal_AddRef(IUIAutomationConditionInternal *iface) +{ + struct uia_condition_internal *condition_internal = impl_from_IUIAutomationConditionInternal(iface); + return IUIAutomationCondition_AddRef(condition_internal->condition); +} + +static ULONG WINAPI uia_condition_internal_Release(IUIAutomationConditionInternal *iface) +{ + struct uia_condition_internal *condition_internal = impl_from_IUIAutomationConditionInternal(iface); + return IUIAutomationCondition_Release(condition_internal->condition); +} + +static HRESULT WINAPI uia_condition_internal_get_condition_struct(IUIAutomationConditionInternal *iface, + struct UiaCondition **ret_val) +{ + struct uia_condition_internal *condition_internal = impl_from_IUIAutomationConditionInternal(iface); + + TRACE("%p, %p\n", iface, ret_val); + + *ret_val = condition_internal->condition_struct; + + return S_OK; +} + +static IUIAutomationConditionInternalVtbl uia_condition_internal_vtbl = { + uia_condition_internal_QueryInterface, + uia_condition_internal_AddRef, + uia_condition_internal_Release, + uia_condition_internal_get_condition_struct, +}; + +static void uia_condition_internal_init(struct uia_condition_internal *internal, struct UiaCondition *condition_struct, + IUIAutomationCondition *condition) +{ + internal->IUIAutomationConditionInternal_iface.lpVtbl = &uia_condition_internal_vtbl; + internal->condition_struct = condition_struct; + internal->condition = condition; +} + +/* + * IUIAutomationBoolCondition interface. + */ +struct uia_bool_condition { + IUIAutomationBoolCondition IUIAutomationBoolCondition_iface; + LONG ref; + + struct UiaCondition condition; + struct uia_condition_internal condition_internal; +}; + +static inline struct uia_bool_condition *impl_from_IUIAutomationBoolCondition(IUIAutomationBoolCondition *iface) +{ + return CONTAINING_RECORD(iface, struct uia_bool_condition, IUIAutomationBoolCondition_iface); +} + +static HRESULT WINAPI uia_bool_condition_QueryInterface(IUIAutomationBoolCondition *iface, REFIID riid, void **ppv) +{ + struct uia_bool_condition *uia_bool_condition = impl_from_IUIAutomationBoolCondition(iface); + + *ppv = NULL; + if (IsEqualIID(riid, &IID_IUIAutomationBoolCondition) || IsEqualIID(riid, &IID_IUIAutomationCondition) || + IsEqualIID(riid, &IID_IUnknown)) + *ppv = iface; + else if (IsEqualIID(riid, &IID_IUIAutomationConditionInternal)) + *ppv = &uia_bool_condition->condition_internal.IUIAutomationConditionInternal_iface; + else + return E_NOINTERFACE; + + IUIAutomationBoolCondition_AddRef(iface); + return S_OK; +} + +static ULONG WINAPI uia_bool_condition_AddRef(IUIAutomationBoolCondition *iface) +{ + struct uia_bool_condition *uia_bool_condition = impl_from_IUIAutomationBoolCondition(iface); + ULONG ref = InterlockedIncrement(&uia_bool_condition->ref); + + TRACE("%p, refcount %ld\n", uia_bool_condition, ref); + return ref; +} + +static ULONG WINAPI uia_bool_condition_Release(IUIAutomationBoolCondition *iface) +{ + struct uia_bool_condition *uia_bool_condition = impl_from_IUIAutomationBoolCondition(iface); + ULONG ref = InterlockedDecrement(&uia_bool_condition->ref); + + TRACE("%p, refcount %ld\n", uia_bool_condition, ref); + if (!ref) + heap_free(uia_bool_condition); + return ref; +} + +static HRESULT WINAPI uia_bool_condition_get_BooleanValue(IUIAutomationBoolCondition *iface, BOOL *ret_val) +{ + struct uia_bool_condition *uia_bool_condition = impl_from_IUIAutomationBoolCondition(iface); + + TRACE("%p, %p\n", iface, ret_val); + + if (!ret_val) + return E_POINTER; + + if (uia_bool_condition->condition.ConditionType == ConditionType_True) + *ret_val = TRUE; + else + *ret_val = FALSE; + + return S_OK; +} + +static const IUIAutomationBoolConditionVtbl uia_bool_condition_vtbl = { + uia_bool_condition_QueryInterface, + uia_bool_condition_AddRef, + uia_bool_condition_Release, + uia_bool_condition_get_BooleanValue, +}; + +static HRESULT create_uia_bool_condition_iface(IUIAutomationCondition **out_cond, enum ConditionType cond_type) +{ + struct uia_bool_condition *uia_bool_condition; + + if (!out_cond) + return E_POINTER; + + uia_bool_condition = heap_alloc_zero(sizeof(*uia_bool_condition)); + if (!uia_bool_condition) + return E_OUTOFMEMORY; + + uia_bool_condition->IUIAutomationBoolCondition_iface.lpVtbl = &uia_bool_condition_vtbl; + uia_bool_condition->condition.ConditionType = cond_type; + uia_bool_condition->ref = 1; + uia_condition_internal_init(&uia_bool_condition->condition_internal, &uia_bool_condition->condition, + (IUIAutomationCondition *)&uia_bool_condition->IUIAutomationBoolCondition_iface); + + *out_cond = uia_bool_condition->condition_internal.condition; + return S_OK; +} + /* * IUIAutomationElement interface. */ @@ -1217,14 +1376,16 @@ static HRESULT WINAPI uia_iface_CreateCacheRequest(IUIAutomation6 *iface, IUIAut
static HRESULT WINAPI uia_iface_CreateTrueCondition(IUIAutomation6 *iface, IUIAutomationCondition **out_condition) { - FIXME("%p, %p: stub\n", iface, out_condition); - return E_NOTIMPL; + TRACE("%p, %p\n", iface, out_condition); + + return create_uia_bool_condition_iface(out_condition, ConditionType_True); }
static HRESULT WINAPI uia_iface_CreateFalseCondition(IUIAutomation6 *iface, IUIAutomationCondition **out_condition) { - FIXME("%p, %p: stub\n", iface, out_condition); - return E_NOTIMPL; + TRACE("%p, %p\n", iface, out_condition); + + return create_uia_bool_condition_iface(out_condition, ConditionType_False); }
static HRESULT WINAPI uia_iface_CreatePropertyCondition(IUIAutomation6 *iface, PROPERTYID prop_id, VARIANT val,