Commit b4f99546 authored by Connor McAdams's avatar Connor McAdams Committed by Alexandre Julliard

uiautomationcore: Add support for returning multiple HUIANODEs from UiaFind.

parent 8719b8de
......@@ -8677,16 +8677,14 @@ static void test_UiaFind(void)
AutomationElementMode_Full);
set_find_params(&find_params, -1, FALSE, FALSE, (struct UiaCondition *)&UiaTrueCondition);
hr = UiaFind(node, &find_params, &cache_req, &out_req, &offsets, &tree_structs);
todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
todo_wine ok(Provider_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child.ref);
todo_wine ok(Provider_child_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child_child.ref);
todo_wine ok(Provider_child_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child_child2.ref);
todo_wine ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref);
todo_wine ok(Provider_child2_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child2_child.ref);
todo_wine ok(Provider_child2_child_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child2_child_child.ref);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(Provider_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child.ref);
ok(Provider_child_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child_child.ref);
ok(Provider_child_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child_child2.ref);
ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref);
ok(Provider_child2_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child2_child.ref);
ok(Provider_child2_child_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child2_child_child.ref);
if (SUCCEEDED(hr))
{
add_provider_desc(&exp_node_desc[0], L"Main", L"Provider", TRUE);
add_provider_desc(&exp_node_desc[1], L"Main", L"Provider_child", TRUE);
add_provider_desc(&exp_node_desc[2], L"Main", L"Provider_child_child", TRUE);
......@@ -8707,7 +8705,6 @@ static void test_UiaFind(void)
}
test_find_sa_results(tree_structs, offsets, exp_elems[0], exp_tree_struct, exp_offset);
ok_method_sequence(find_seq1, "find_seq1");
}
SafeArrayDestroy(out_req);
SafeArrayDestroy(offsets);
......@@ -8723,12 +8720,10 @@ static void test_UiaFind(void)
AutomationElementMode_Full);
set_find_params(&find_params, 1, FALSE, FALSE, (struct UiaCondition *)&UiaTrueCondition);
hr = UiaFind(node, &find_params, &cache_req, &out_req, &offsets, &tree_structs);
todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
todo_wine ok(Provider_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child.ref);
todo_wine ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(Provider_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child.ref);
ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref);
if (SUCCEEDED(hr))
{
add_provider_desc(&exp_node_desc[0], L"Main", L"Provider", TRUE);
add_provider_desc(&exp_node_desc[1], L"Main", L"Provider_child", TRUE);
add_provider_desc(&exp_node_desc[2], L"Main", L"Provider_child2", TRUE);
......@@ -8746,7 +8741,6 @@ static void test_UiaFind(void)
test_find_sa_results(tree_structs, offsets, exp_elems[0], exp_tree_struct, exp_offset);
ok_method_sequence(find_seq2, "find_seq2");
}
SafeArrayDestroy(out_req);
SafeArrayDestroy(offsets);
......@@ -8762,12 +8756,10 @@ static void test_UiaFind(void)
AutomationElementMode_Full);
set_find_params(&find_params, 1, FALSE, TRUE, (struct UiaCondition *)&UiaTrueCondition);
hr = UiaFind(node, &find_params, &cache_req, &out_req, &offsets, &tree_structs);
todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
todo_wine ok(Provider_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child.ref);
todo_wine ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(Provider_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child.ref);
ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref);
if (SUCCEEDED(hr))
{
add_provider_desc(&exp_node_desc[0], L"Main", L"Provider_child", TRUE);
add_provider_desc(&exp_node_desc[1], L"Main", L"Provider_child2", TRUE);
exp_lbound[0] = exp_lbound[1] = 0;
......@@ -8783,7 +8775,6 @@ static void test_UiaFind(void)
}
test_find_sa_results(tree_structs, offsets, exp_elems[0], exp_tree_struct, exp_offset);
ok_method_sequence(find_seq3, "find_seq3");
}
SafeArrayDestroy(out_req);
SafeArrayDestroy(offsets);
......@@ -8843,11 +8834,9 @@ static void test_UiaFind(void)
AutomationElementMode_Full);
set_find_params(&find_params, 0, FALSE, FALSE, (struct UiaCondition *)&UiaTrueCondition);
hr = UiaFind(node2, &find_params, &cache_req, &out_req, &offsets, &tree_structs);
todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
todo_wine ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref);
if (SUCCEEDED(hr))
{
add_provider_desc(&exp_node_desc[0], L"Main", L"Provider_child", TRUE);
add_provider_desc(&exp_node_desc[1], L"Main", L"Provider_child2", TRUE);
exp_lbound[0] = exp_lbound[1] = 0;
......@@ -8863,7 +8852,6 @@ static void test_UiaFind(void)
}
test_find_sa_results(tree_structs, offsets, exp_elems[0], exp_tree_struct, exp_offset);
ok_method_sequence(find_seq5, "find_seq5");
}
SafeArrayDestroy(out_req);
SafeArrayDestroy(offsets);
......@@ -8883,10 +8871,8 @@ static void test_UiaFind(void)
AutomationElementMode_Full);
set_find_params(&find_params, 0, FALSE, FALSE, (struct UiaCondition *)&UiaTrueCondition);
hr = UiaFind(node2, &find_params, &cache_req, &out_req, &offsets, &tree_structs);
todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
if (SUCCEEDED(hr))
{
add_provider_desc(&exp_node_desc[0], L"Main", L"Provider_child", TRUE);
exp_lbound[0] = exp_lbound[1] = 0;
exp_elems[0] = exp_elems[1] = 1;
......@@ -8900,7 +8886,6 @@ static void test_UiaFind(void)
}
test_find_sa_results(tree_structs, offsets, exp_elems[0], exp_tree_struct, exp_offset);
ok_method_sequence(find_seq6, "find_seq6");
}
SafeArrayDestroy(out_req);
SafeArrayDestroy(offsets);
......@@ -8938,13 +8923,11 @@ static void test_UiaFind(void)
AutomationElementMode_Full);
set_find_params(&find_params, 1, FALSE, FALSE, (struct UiaCondition *)&prop_cond[1]);
hr = UiaFind(node, &find_params, &cache_req, &out_req, &offsets, &tree_structs);
todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
todo_wine ok(Provider_child_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child_child.ref);
todo_wine ok(Provider_child_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child_child2.ref);
todo_wine ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(Provider_child_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child_child.ref);
ok(Provider_child_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child_child2.ref);
ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref);
if (SUCCEEDED(hr))
{
add_provider_desc(&exp_node_desc[0], L"Main", L"Provider", TRUE);
add_provider_desc(&exp_node_desc[1], L"Main", L"Provider_child_child", TRUE);
add_provider_desc(&exp_node_desc[2], L"Main", L"Provider_child_child2", TRUE);
......@@ -8962,7 +8945,6 @@ static void test_UiaFind(void)
}
test_find_sa_results(tree_structs, offsets, exp_elems[0], exp_tree_struct, exp_offset);
ok_method_sequence(find_seq7, "find_seq7");
}
SafeArrayDestroy(out_req);
SafeArrayDestroy(offsets);
......@@ -8994,13 +8976,11 @@ static void test_UiaFind(void)
AutomationElementMode_Full);
set_find_params(&find_params, 1, FALSE, FALSE, (struct UiaCondition *)&prop_cond[1]);
hr = UiaFind(node, &find_params, &cache_req, &out_req, &offsets, &tree_structs);
todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
todo_wine ok(Provider_child_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child_child.ref);
todo_wine ok(Provider_child_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child_child2.ref);
todo_wine ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(Provider_child_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child_child.ref);
ok(Provider_child_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child_child2.ref);
ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref);
if (SUCCEEDED(hr))
{
add_provider_desc(&exp_node_desc[0], L"Main", L"Provider", TRUE);
add_provider_desc(&exp_node_desc[1], L"Main", L"Provider_child_child", TRUE);
add_provider_desc(&exp_node_desc[2], L"Main", L"Provider_child_child2", TRUE);
......@@ -9028,7 +9008,6 @@ static void test_UiaFind(void)
}
test_find_sa_results(tree_structs, offsets, exp_elems[0], exp_tree_struct, exp_offset);
ok_method_sequence(find_seq8, "find_seq8");
}
SafeArrayDestroy(out_req);
SafeArrayDestroy(offsets);
......@@ -9064,12 +9043,10 @@ static void test_UiaFind(void)
AutomationElementMode_Full);
set_find_params(&find_params, 1, FALSE, FALSE, (struct UiaCondition *)&prop_cond[1]);
hr = UiaFind(node2, &find_params, &cache_req, &out_req, &offsets, &tree_structs);
todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
todo_wine ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref);
todo_wine ok(Provider_child2_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child2_child.ref);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref);
ok(Provider_child2_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child2_child.ref);
if (SUCCEEDED(hr))
{
add_provider_desc(&exp_node_desc[0], L"Main", L"Provider_child_child2", TRUE);
add_provider_desc(&exp_node_desc[1], L"Main", L"Provider_child2", TRUE);
add_provider_desc(&exp_node_desc[2], L"Main", L"Provider_child2_child", TRUE);
......@@ -9086,7 +9063,6 @@ static void test_UiaFind(void)
}
test_find_sa_results(tree_structs, offsets, exp_elems[0], exp_tree_struct, exp_offset);
ok_method_sequence(find_seq9, "find_seq9");
}
SafeArrayDestroy(out_req);
SafeArrayDestroy(offsets);
......@@ -9096,7 +9072,7 @@ static void test_UiaFind(void)
for (i = 0; i < ARRAY_SIZE(exp_node_desc); i++)
init_node_provider_desc(&exp_node_desc[i], GetCurrentProcessId(), NULL);
todo_wine ok(UiaNodeRelease(node2), "UiaNodeRelease returned FALSE\n");
ok(UiaNodeRelease(node2), "UiaNodeRelease returned FALSE\n");
ok(Provider_child_child2.ref == 1, "Unexpected refcnt %ld\n", Provider_child_child2.ref);
/*
......@@ -9122,11 +9098,9 @@ static void test_UiaFind(void)
AutomationElementMode_Full);
set_find_params(&find_params, 1, FALSE, TRUE, (struct UiaCondition *)&prop_cond[1]);
hr = UiaFind(node, &find_params, &cache_req, &out_req, &offsets, &tree_structs);
todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
todo_wine ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref);
if (SUCCEEDED(hr))
{
add_provider_desc(&exp_node_desc[0], L"Main", L"Provider_child2", TRUE);
exp_lbound[0] = exp_lbound[1] = 0;
exp_elems[0] = 1;
......@@ -9142,7 +9116,6 @@ static void test_UiaFind(void)
test_find_sa_results(tree_structs, offsets, exp_elems[0], exp_tree_struct, exp_offset);
ok_method_sequence(find_seq10, "find_seq10");
}
SafeArrayDestroy(out_req);
SafeArrayDestroy(offsets);
......
......@@ -89,25 +89,17 @@ static HRESULT add_node_to_node_array(struct uia_node_array *out_nodes, HUIANODE
return S_OK;
}
static HRESULT get_safearray_bounds(SAFEARRAY *sa, LONG *lbound, LONG *elems)
static HRESULT get_safearray_dim_bounds(SAFEARRAY *sa, UINT dim, LONG *lbound, LONG *elems)
{
LONG ubound;
HRESULT hr;
UINT dims;
*lbound = *elems = 0;
dims = SafeArrayGetDim(sa);
if (dims != 1)
{
WARN("Invalid dimensions %d for safearray.\n", dims);
return E_FAIL;
}
hr = SafeArrayGetLBound(sa, 1, lbound);
hr = SafeArrayGetLBound(sa, dim, lbound);
if (FAILED(hr))
return hr;
hr = SafeArrayGetUBound(sa, 1, &ubound);
hr = SafeArrayGetUBound(sa, dim, &ubound);
if (FAILED(hr))
return hr;
......@@ -115,6 +107,21 @@ static HRESULT get_safearray_bounds(SAFEARRAY *sa, LONG *lbound, LONG *elems)
return S_OK;
}
static HRESULT get_safearray_bounds(SAFEARRAY *sa, LONG *lbound, LONG *elems)
{
UINT dims;
*lbound = *elems = 0;
dims = SafeArrayGetDim(sa);
if (dims != 1)
{
WARN("Invalid dimensions %d for safearray.\n", dims);
return E_FAIL;
}
return get_safearray_dim_bounds(sa, 1, lbound, elems);
}
int uia_compare_safearrays(SAFEARRAY *sa1, SAFEARRAY *sa2, int prop_type)
{
LONG i, idx, lbound[2], elems[2];
......@@ -1031,8 +1038,8 @@ static HRESULT traverse_uia_node_tree_siblings(HUIANODE huianode, struct UiaCond
*/
static HRESULT traverse_uia_node_tree(HUIANODE huianode, struct UiaCondition *view_cond,
struct UiaCondition *search_cond, struct UiaCondition *pre_sibling_nav_stop_cond,
struct UiaCondition *ascending_stop_cond, int traversal_opts, BOOL at_root_level, BOOL *root_found,
int max_depth, int *cur_depth, struct uia_node_array *out_nodes)
struct UiaCondition *ascending_stop_cond, int traversal_opts, BOOL at_root_level, BOOL find_first,
BOOL *root_found, int max_depth, int *cur_depth, struct uia_node_array *out_nodes)
{
HUIANODE node = huianode;
HRESULT hr;
......@@ -1067,10 +1074,13 @@ static HRESULT traverse_uia_node_tree(HUIANODE huianode, struct UiaCondition *vi
if (FAILED(hr))
break;
if (find_first)
{
hr = S_FALSE;
break;
}
}
}
*root_found = TRUE;
}
......@@ -1088,7 +1098,7 @@ static HRESULT traverse_uia_node_tree(HUIANODE huianode, struct UiaCondition *vi
if (SUCCEEDED(hr) && node2)
hr = traverse_uia_node_tree(node2, view_cond, search_cond, pre_sibling_nav_stop_cond, ascending_stop_cond,
traversal_opts, FALSE, root_found, max_depth, cur_depth, out_nodes);
traversal_opts, FALSE, find_first, root_found, max_depth, cur_depth, out_nodes);
if (FAILED(hr) || hr == S_FALSE)
break;
......@@ -2812,6 +2822,50 @@ HRESULT WINAPI UiaNavigate(HUIANODE huianode, enum NavigateDirection dir, struct
return hr;
}
/* Combine multiple cache requests into a single SAFEARRAY. */
static HRESULT uia_cache_request_combine(SAFEARRAY **reqs, int reqs_count, SAFEARRAY *out_req)
{
LONG idx[2], lbound[2], elems[2], cur_offset;
int i, x, y;
HRESULT hr;
VARIANT v;
for (i = cur_offset = 0; i < reqs_count; i++)
{
if (!reqs[i])
continue;
for (x = 0; x < 2; x++)
{
hr = get_safearray_dim_bounds(reqs[i], 1 + x, &lbound[x], &elems[x]);
if (FAILED(hr))
return hr;
}
for (x = 0; x < elems[0]; x++)
{
for (y = 0; y < elems[1]; y++)
{
idx[0] = x + lbound[0];
idx[1] = y + lbound[1];
hr = SafeArrayGetElement(reqs[i], idx, &v);
if (FAILED(hr))
return hr;
idx[0] = x + cur_offset;
idx[1] = y;
hr = SafeArrayPutElement(out_req, idx, &v);
if (FAILED(hr))
return hr;
}
}
cur_offset += elems[0];
}
return S_OK;
}
/***********************************************************************
* UiaFind (uiautomationcore.@)
*/
......@@ -2820,14 +2874,15 @@ HRESULT WINAPI UiaFind(HUIANODE huianode, struct UiaFindParams *find_params, str
{
struct UiaPropertyCondition prop_cond = { ConditionType_Property, UIA_RuntimeIdPropertyId };
struct uia_node *node = unsafe_impl_from_IWineUiaNode((IWineUiaNode *)huianode);
SAFEARRAY *runtime_id, *req, *offsets, *tree_structs;
SAFEARRAY *runtime_id, *req, *offsets, *tree_structs, **tmp_reqs;
struct UiaCondition *sibling_stop_cond;
struct uia_node_array nodes = { 0 };
int cur_depth = 0;
LONG idx, lbound, elems, cur_offset;
SAFEARRAYBOUND sabound[2];
int i, cur_depth = 0;
BSTR tree_struct;
BOOL root_found;
HRESULT hr;
LONG idx;
TRACE("(%p, %p, %p, %p, %p, %p)\n", huianode, find_params, cache_req, out_req, out_offsets, out_tree_structs);
......@@ -2835,12 +2890,7 @@ HRESULT WINAPI UiaFind(HUIANODE huianode, struct UiaFindParams *find_params, str
return E_INVALIDARG;
*out_tree_structs = *out_offsets = *out_req = tree_structs = offsets = req = NULL;
if (!find_params->FindFirst)
{
FIXME("Finding more than one HUIANODE is currently unimplemented.\n");
return E_NOTIMPL;
}
tmp_reqs = NULL;
/*
* If the initial node has a runtime ID, we'll use it as a stop
......@@ -2863,36 +2913,81 @@ HRESULT WINAPI UiaFind(HUIANODE huianode, struct UiaFindParams *find_params, str
IWineUiaNode_AddRef(&node->IWineUiaNode_iface);
hr = traverse_uia_node_tree(huianode, cache_req->pViewCondition, find_params->pFindCondition, sibling_stop_cond,
cache_req->pViewCondition, TreeTraversalOptions_Default, TRUE, &root_found, find_params->MaxDepth,
&cur_depth, &nodes);
cache_req->pViewCondition, TreeTraversalOptions_Default, TRUE, find_params->FindFirst, &root_found,
find_params->MaxDepth, &cur_depth, &nodes);
if (FAILED(hr) || !nodes.node_count)
goto exit;
if (!(offsets = SafeArrayCreateVector(VT_I4, 0, 1)))
if (!(offsets = SafeArrayCreateVector(VT_I4, 0, nodes.node_count)))
{
hr = E_FAIL;
goto exit;
}
if (!(tree_structs = SafeArrayCreateVector(VT_BSTR, 0, 1)))
if (!(tree_structs = SafeArrayCreateVector(VT_BSTR, 0, nodes.node_count)))
{
hr = E_FAIL;
goto exit;
}
hr = UiaGetUpdatedCache(nodes.nodes[0], cache_req, NormalizeState_None, NULL, &req, &tree_struct);
if (!(tmp_reqs = heap_alloc_zero(sizeof(*tmp_reqs) * nodes.node_count)))
{
hr = E_OUTOFMEMORY;
goto exit;
}
/*
* Get a count of how many total nodes we'll need to return, as well as
* set the tree structure strings and cache request offsets for our final
* combined SAFEARRAY.
*/
for (i = cur_offset = 0; i < nodes.node_count; i++)
{
hr = UiaGetUpdatedCache(nodes.nodes[i], cache_req, NormalizeState_None, NULL, &tmp_reqs[i], &tree_struct);
if (FAILED(hr))
goto exit;
idx = 0;
idx = i;
hr = SafeArrayPutElement(tree_structs, &idx, tree_struct);
SysFreeString(tree_struct);
if (FAILED(hr))
goto exit;
hr = SafeArrayPutElement(offsets, &idx, &idx);
hr = SafeArrayPutElement(offsets, &idx, &cur_offset);
if (FAILED(hr))
goto exit;
if (!tmp_reqs[i])
continue;
hr = get_safearray_dim_bounds(tmp_reqs[i], 1, &lbound, &elems);
if (FAILED(hr))
goto exit;
cur_offset += elems;
}
if (nodes.node_count == 1)
{
req = tmp_reqs[0];
heap_free(tmp_reqs);
tmp_reqs = NULL;
}
else
{
sabound[0].lLbound = sabound[1].lLbound = 0;
sabound[0].cElements = cur_offset;
sabound[1].cElements = 1 + cache_req->cProperties + cache_req->cPatterns;
if (!(req = SafeArrayCreate(VT_VARIANT, 2, sabound)))
{
hr = E_FAIL;
goto exit;
}
hr = uia_cache_request_combine(tmp_reqs, nodes.node_count, req);
if (FAILED(hr))
goto exit;
}
*out_tree_structs = tree_structs;
*out_offsets = offsets;
......@@ -2902,6 +2997,13 @@ exit:
VariantClear(&prop_cond.Value);
clear_node_array(&nodes);
if (tmp_reqs)
{
for (i = 0; i < nodes.node_count; i++)
SafeArrayDestroy(tmp_reqs[i]);
heap_free(tmp_reqs);
}
if (FAILED(hr))
{
SafeArrayDestroy(tree_structs);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment