From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 130 +++++++++++-- dlls/uiautomationcore/uia_com_client.c | 207 ++++++++++++++++++++- 2 files changed, 311 insertions(+), 26 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 1c3f1d6c15c..66221a84d44 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -5184,27 +5184,70 @@ static void check_uia_prop_val(PROPERTYID prop_id, enum UIAutomationType type, V }
case UIAutomationType_ElementArray: - ok(V_VT(v) == (VT_ARRAY | VT_UNKNOWN), "Unexpected VT %d\n", V_VT(v)); - if (V_VT(v) != (VT_ARRAY | VT_UNKNOWN)) - break; - ok(Provider_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child.ref); ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref); - for (idx = 0; idx < ARRAY_SIZE(uia_unk_arr_prop_val); idx++) + if (from_com) { - HUIANODE tmp_node; + IUIAutomationElementArray *elem_arr = NULL; HRESULT hr; - VARIANT v1; + int len; + + ok(V_VT(v) == VT_UNKNOWN, "Unexpected VT %d\n", V_VT(v)); + hr = IUnknown_QueryInterface(V_UNKNOWN(v), &IID_IUIAutomationElementArray, (void **)&elem_arr); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + ok(!!elem_arr, "elem_arr == NULL\n"); + if (!elem_arr) + { + VariantClear(v); + break; + }
- SafeArrayGetElement(V_ARRAY(v), &idx, &tmp_node); + hr = IUIAutomationElementArray_get_Length(elem_arr, &len); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + ok(len == ARRAY_SIZE(uia_unk_arr_prop_val), "Unexpected length %d\n", len);
- hr = UiaGetPropertyValue(tmp_node, UIA_ControlTypePropertyId, &v1); - ok(hr == S_OK, "node[%ld] Unexpected hr %#lx\n", idx, hr); - ok(V_VT(&v1) == VT_I4, "node[%ld] Unexpected VT %d\n", idx, V_VT(&v1)); - ok(V_I4(&v1) == uia_i4_prop_val, "node[%ld] Unexpected I4 %#lx\n", idx, V_I4(&v1)); + for (idx = 0; idx < ARRAY_SIZE(uia_unk_arr_prop_val); idx++) + { + IUIAutomationElement *tmp_elem = NULL; + VARIANT v1; + + hr = IUIAutomationElementArray_GetElement(elem_arr, idx, &tmp_elem); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + ok(!!tmp_elem, "tmp_elem == NULL\n"); + + hr = IUIAutomationElement_GetCurrentPropertyValueEx(tmp_elem, UIA_ControlTypePropertyId, TRUE, &v1); + ok(hr == S_OK, "elem[%ld] Unexpected hr %#lx\n", idx, hr); + ok(V_VT(&v1) == VT_I4, "elem[%ld] Unexpected VT %d\n", idx, V_VT(&v1)); + ok(V_I4(&v1) == uia_i4_prop_val, "elem[%ld] Unexpected I4 %#lx\n", idx, V_I4(&v1));
- ok(UiaNodeRelease(tmp_node), "Failed to release node[%ld]\n", idx); - VariantClear(&v1); + IUIAutomationElement_Release(tmp_elem); + VariantClear(&v1); + } + + IUIAutomationElementArray_Release(elem_arr); + } + else + { + ok(V_VT(v) == (VT_ARRAY | VT_UNKNOWN), "Unexpected VT %d\n", V_VT(v)); + if (V_VT(v) != (VT_ARRAY | VT_UNKNOWN)) + break; + + for (idx = 0; idx < ARRAY_SIZE(uia_unk_arr_prop_val); idx++) + { + HUIANODE tmp_node; + HRESULT hr; + VARIANT v1; + + SafeArrayGetElement(V_ARRAY(v), &idx, &tmp_node); + + hr = UiaGetPropertyValue(tmp_node, UIA_ControlTypePropertyId, &v1); + ok(hr == S_OK, "node[%ld] Unexpected hr %#lx\n", idx, hr); + ok(V_VT(&v1) == VT_I4, "node[%ld] Unexpected VT %d\n", idx, V_VT(&v1)); + ok(V_I4(&v1) == uia_i4_prop_val, "node[%ld] Unexpected I4 %#lx\n", idx, V_I4(&v1)); + + ok(UiaNodeRelease(tmp_node), "Failed to release node[%ld]\n", idx); + VariantClear(&v1); + } }
VariantClear(v); @@ -9835,7 +9878,8 @@ static void test_Element_GetPropertyValue(IUIAutomation *uia_iface) const struct uia_element_property *elem_prop; struct Provider_prop_override prop_override; IUIAutomationElement *element, *element2; - int i, prop_id, tmp_int; + IUIAutomationElementArray *element_arr; + int i, len, prop_id, tmp_int; struct UiaRect uia_rect; IUnknown *unk_ns; BSTR tmp_bstr; @@ -9864,10 +9908,6 @@ static void test_Element_GetPropertyValue(IUIAutomation *uia_iface) { elem_prop = &element_properties[i];
- /* Skip ElementArray properties for now. */ - if (elem_prop->type == UIAutomationType_ElementArray) - continue; - Provider.ret_invalid_prop_type = FALSE; VariantClear(&v); if (!(prop_id = UiaLookupId(AutomationIdentifierType_Property, elem_prop->prop_guid))) @@ -9902,6 +9942,58 @@ static void test_Element_GetPropertyValue(IUIAutomation *uia_iface) winetest_pop_context(); }
+ /* Test IUIAutomationElementArray interface behavior. */ + Provider.ret_invalid_prop_type = FALSE; + hr = IUIAutomationElement_GetCurrentPropertyValueEx(element, UIA_ControllerForPropertyId, TRUE, &v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&v) == VT_UNKNOWN, "Unexpected vt %d\n", V_VT(&v)); + ok(Provider_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child.ref); + ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref); + + element_arr = NULL; + hr = IUnknown_QueryInterface(V_UNKNOWN(&v), &IID_IUIAutomationElementArray, (void **)&element_arr); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + ok(!!element_arr, "element_arr == NULL\n"); + VariantClear(&v); + + hr = IUIAutomationElementArray_get_Length(element_arr, &len); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + ok(len == ARRAY_SIZE(uia_unk_arr_prop_val), "Unexpected length %d\n", len); + + /* Invalid argument tests. */ + hr = IUIAutomationElementArray_get_Length(element_arr, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx\n", hr); + + element2 = (void *)0xdeadbeef; + hr = IUIAutomationElementArray_GetElement(element_arr, len, &element2); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx\n", hr); + /* Pointer isn't cleared. */ + ok(!!element2, "element2 == NULL\n"); + + hr = IUIAutomationElementArray_GetElement(element_arr, 0, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx\n", hr); + + for (i = 0; i < len; i++) + { + element2 = NULL; + hr = IUIAutomationElementArray_GetElement(element_arr, i, &element2); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + ok(!!element2, "element2 == NULL\n"); + + hr = IUIAutomationElement_GetCurrentPropertyValueEx(element2, UIA_ControlTypePropertyId, TRUE, &v); + ok(hr == S_OK, "element[%d] Unexpected hr %#lx\n", i, hr); + ok(V_VT(&v) == VT_I4, "element[%d] Unexpected VT %d\n", i, V_VT(&v)); + ok(V_I4(&v) == uia_i4_prop_val, "element[%d] Unexpected I4 %#lx\n", i, V_I4(&v)); + + IUIAutomationElement_Release(element2); + VariantClear(&v); + } + + IUIAutomationElementArray_Release(element_arr); + ok(Provider_child.ref == 1, "Unexpected refcnt %ld\n", Provider_child.ref); + ok(Provider_child2.ref == 1, "Unexpected refcnt %ld\n", Provider_child2.ref); + ok_method_sequence(get_elem_arr_prop_seq, NULL); + /* * IUIAutomationElement_get_CurrentControlType tests. If the value * returned for UIA_ControlTypePropertyId is not a registered control diff --git a/dlls/uiautomationcore/uia_com_client.c b/dlls/uiautomationcore/uia_com_client.c index e5fdc049342..f07e15817cc 100644 --- a/dlls/uiautomationcore/uia_com_client.c +++ b/dlls/uiautomationcore/uia_com_client.c @@ -593,6 +593,129 @@ static HRESULT get_uia_condition_struct_from_iface(IUIAutomationCondition *condi return S_OK; }
+/* + * IUIAutomationElementArray interface. + */ +struct uia_element_array { + IUIAutomationElementArray IUIAutomationElementArray_iface; + LONG ref; + + IUIAutomationElement **elements; + int elements_count; +}; + +static inline struct uia_element_array *impl_from_IUIAutomationElementArray(IUIAutomationElementArray *iface) +{ + return CONTAINING_RECORD(iface, struct uia_element_array, IUIAutomationElementArray_iface); +} + +static HRESULT WINAPI uia_element_array_QueryInterface(IUIAutomationElementArray *iface, REFIID riid, void **ppv) +{ + if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IUIAutomationElementArray)) + *ppv = iface; + else + return E_NOINTERFACE; + + IUIAutomationElementArray_AddRef(iface); + return S_OK; +} + +static ULONG WINAPI uia_element_array_AddRef(IUIAutomationElementArray *iface) +{ + struct uia_element_array *element = impl_from_IUIAutomationElementArray(iface); + ULONG ref = InterlockedIncrement(&element->ref); + + TRACE("%p, refcount %ld\n", element, ref); + return ref; +} + +static ULONG WINAPI uia_element_array_Release(IUIAutomationElementArray *iface) +{ + struct uia_element_array *element_arr = impl_from_IUIAutomationElementArray(iface); + ULONG ref = InterlockedDecrement(&element_arr->ref); + + TRACE("%p, refcount %ld\n", element_arr, ref); + if (!ref) + { + if (element_arr->elements_count) + { + int i; + + for (i = 0; i < element_arr->elements_count; i++) + { + if (element_arr->elements[i]) + IUIAutomationElement_Release(element_arr->elements[i]); + } + } + heap_free(element_arr->elements); + heap_free(element_arr); + } + + return ref; +} + +static HRESULT WINAPI uia_element_array_get_Length(IUIAutomationElementArray *iface, int *out_length) +{ + struct uia_element_array *element_arr = impl_from_IUIAutomationElementArray(iface); + + TRACE("%p, %p\n", iface, out_length); + + if (!out_length) + return E_POINTER; + + *out_length = element_arr->elements_count; + + return S_OK; +} + +static HRESULT WINAPI uia_element_array_GetElement(IUIAutomationElementArray *iface, int idx, + IUIAutomationElement **out_elem) +{ + struct uia_element_array *element_arr = impl_from_IUIAutomationElementArray(iface); + + TRACE("%p, %p\n", iface, out_elem); + + if (!out_elem) + return E_POINTER; + + if (idx >= element_arr->elements_count) + return E_INVALIDARG; + + *out_elem = element_arr->elements[idx]; + IUIAutomationElement_AddRef(element_arr->elements[idx]); + + return S_OK; +} + +static const IUIAutomationElementArrayVtbl uia_element_array_vtbl = { + uia_element_array_QueryInterface, + uia_element_array_AddRef, + uia_element_array_Release, + uia_element_array_get_Length, + uia_element_array_GetElement, +}; + +static HRESULT create_uia_element_array_iface(IUIAutomationElementArray **iface, int elements_count) +{ + struct uia_element_array *element_arr = heap_alloc_zero(sizeof(*element_arr)); + + *iface = NULL; + if (!element_arr) + return E_OUTOFMEMORY; + + element_arr->IUIAutomationElementArray_iface.lpVtbl = &uia_element_array_vtbl; + element_arr->ref = 1; + element_arr->elements_count = elements_count; + if (!(element_arr->elements = heap_alloc_zero(sizeof(*element_arr->elements) * elements_count))) + { + heap_free(element_arr); + return E_OUTOFMEMORY; + } + + *iface = &element_arr->IUIAutomationElementArray_iface; + return S_OK; +} + /* * IUIAutomationElement interface. */ @@ -705,6 +828,49 @@ static HRESULT WINAPI uia_element_GetCurrentPropertyValue(IUIAutomationElement9 }
static HRESULT create_uia_element(IUIAutomationElement **iface, BOOL from_cui8, HUIANODE node); +static HRESULT create_element_array_from_node_array(SAFEARRAY *sa, BOOL from_cui8, + IUIAutomationElementArray **out_elem_arr) +{ + struct uia_element_array *elem_arr_data; + IUIAutomationElementArray *elem_arr; + LONG idx, lbound, elems, i; + HRESULT hr; + + *out_elem_arr = NULL; + hr = get_safearray_bounds(sa, &lbound, &elems); + if (FAILED(hr)) + return hr; + + hr = create_uia_element_array_iface(&elem_arr, elems); + if (FAILED(hr)) + return hr; + + elem_arr_data = impl_from_IUIAutomationElementArray(elem_arr); + for (i = 0; i < elems; i++) + { + HUIANODE node; + + idx = lbound + i; + hr = SafeArrayGetElement(sa, &idx, &node); + if (FAILED(hr)) + break; + + hr = create_uia_element(&elem_arr_data->elements[i], from_cui8, node); + if (FAILED(hr)) + { + UiaNodeRelease(node); + break; + } + } + + if (SUCCEEDED(hr)) + *out_elem_arr = elem_arr; + else + IUIAutomationElementArray_Release(elem_arr); + + return hr; +} + static HRESULT WINAPI uia_element_GetCurrentPropertyValueEx(IUIAutomationElement9 *iface, PROPERTYID prop_id, BOOL ignore_default, VARIANT *ret_val) { @@ -724,18 +890,20 @@ static HRESULT WINAPI uia_element_GetCurrentPropertyValueEx(IUIAutomationElement if (!ignore_default) FIXME("Default values currently unimplemented\n");
- if (prop_info->type == UIAutomationType_ElementArray) - { - FIXME("ElementArray property types currently unsupported for IUIAutomationElement\n"); - return E_NOTIMPL; - } - hr = UiaGetPropertyValue(element->node, prop_id, ret_val); - if ((prop_info->type == UIAutomationType_Element) && (V_VT(ret_val) != VT_UNKNOWN)) + if (FAILED(hr)) + return hr; + + switch (prop_info->type) + { + case UIAutomationType_Element: { IUIAutomationElement *out_elem; HUIANODE node;
+ if (V_VT(ret_val) == VT_UNKNOWN) + break; + hr = UiaHUiaNodeFromVariant(ret_val, &node); VariantClear(ret_val); if (FAILED(hr)) @@ -747,6 +915,31 @@ static HRESULT WINAPI uia_element_GetCurrentPropertyValueEx(IUIAutomationElement V_VT(ret_val) = VT_UNKNOWN; V_UNKNOWN(ret_val) = (IUnknown *)out_elem; } + else + UiaNodeRelease(node); + + break; + } + + case UIAutomationType_ElementArray: + { + IUIAutomationElementArray *out_elem_arr; + + if (V_VT(ret_val) == VT_UNKNOWN) + break; + + hr = create_element_array_from_node_array(V_ARRAY(ret_val), element->from_cui8, &out_elem_arr); + VariantClear(ret_val); + if (SUCCEEDED(hr)) + { + V_VT(ret_val) = VT_UNKNOWN; + V_UNKNOWN(ret_val) = (IUnknown *)out_elem_arr; + } + break; + } + + default: + break; }
return hr;