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

uiautomationcore: Add support for ProviderOptions_UseComThreading flag.

parent 0cea0119
......@@ -1101,6 +1101,7 @@ static struct Provider
enum ProviderOptions prov_opts;
HWND hwnd;
BOOL ret_invalid_prop_type;
DWORD expected_tid;
} Provider, Provider2, Provider_child, Provider_child2;
static const WCHAR *uia_bstr_prop_str = L"uia-string";
......@@ -1443,6 +1444,8 @@ HRESULT WINAPI ProviderSimple_get_ProviderOptions(IRawElementProviderSimple *ifa
struct Provider *This = impl_from_ProviderSimple(iface);
add_method_call(This, PROV_GET_PROVIDER_OPTIONS);
if (This->expected_tid)
ok(This->expected_tid == GetCurrentThreadId(), "Unexpected tid %ld\n", GetCurrentThreadId());
*ret_val = 0;
if (This->prov_opts)
......@@ -1467,6 +1470,8 @@ HRESULT WINAPI ProviderSimple_GetPropertyValue(IRawElementProviderSimple *iface,
struct Provider *This = impl_from_ProviderSimple(iface);
add_method_call(This, PROV_GET_PROPERTY_VALUE);
if (This->expected_tid)
ok(This->expected_tid == GetCurrentThreadId(), "Unexpected tid %ld\n", GetCurrentThreadId());
VariantInit(ret_val);
switch (prop_id)
......@@ -1654,6 +1659,8 @@ HRESULT WINAPI ProviderSimple_get_HostRawElementProvider(IRawElementProviderSimp
struct Provider *This = impl_from_ProviderSimple(iface);
add_method_call(This, PROV_GET_HOST_RAW_ELEMENT_PROVIDER);
if (This->expected_tid)
ok(This->expected_tid == GetCurrentThreadId(), "Unexpected tid %ld\n", GetCurrentThreadId());
*ret_val = NULL;
if (This->hwnd)
......@@ -1702,6 +1709,8 @@ static HRESULT WINAPI ProviderFragment_Navigate(IRawElementProviderFragment *ifa
struct Provider *This = impl_from_ProviderFragment(iface);
add_method_call(This, FRAG_NAVIGATE);
if (This->expected_tid)
ok(This->expected_tid == GetCurrentThreadId(), "Unexpected tid %ld\n", GetCurrentThreadId());
*ret_val = NULL;
if ((direction == NavigateDirection_Parent) && This->parent)
......@@ -3759,6 +3768,108 @@ static const struct prov_method_sequence node_from_prov8[] = {
{ 0 }
};
static void check_uia_prop_val(PROPERTYID prop_id, enum UIAutomationType type, VARIANT *v);
static DWORD WINAPI uia_node_from_provider_test_com_thread(LPVOID param)
{
HUIANODE node = param;
HRESULT hr;
VARIANT v;
/*
* Since this is a node representing an IRawElementProviderSimple with
* ProviderOptions_UseComThreading set, it is only usable in threads that
* have initialized COM.
*/
hr = UiaGetPropertyValue(node, UIA_ProcessIdPropertyId, &v);
ok(hr == CO_E_NOTINITIALIZED, "Unexpected hr %#lx\n", hr);
CoInitializeEx(NULL, COINIT_MULTITHREADED);
hr = UiaGetPropertyValue(node, UIA_ProcessIdPropertyId, &v);
ok(hr == S_OK, "Unexpected hr %#lx\n", hr);
check_uia_prop_val(UIA_ProcessIdPropertyId, UIAutomationType_Int, &v);
/*
* When retrieving a UIAutomationType_Element property, if UseComThreading
* is set, we'll get an HUIANODE that will make calls inside of the
* apartment of the node it is retrieved from. I.e, if we received a node
* with UseComThreading set from another node with UseComThreading set
* inside of an STA, the returned node will have all of its methods called
* from the STA thread.
*/
Provider_child.prov_opts = ProviderOptions_UseComThreading | ProviderOptions_ServerSideProvider;
Provider_child.expected_tid = Provider.expected_tid;
hr = UiaGetPropertyValue(node, UIA_LabeledByPropertyId, &v);
ok(hr == S_OK, "Unexpected hr %#lx\n", hr);
check_uia_prop_val(UIA_LabeledByPropertyId, UIAutomationType_Element, &v);
/* Unset ProviderOptions_UseComThreading. */
Provider_child.prov_opts = ProviderOptions_ServerSideProvider;
hr = UiaGetPropertyValue(node, UIA_LabeledByPropertyId, &v);
ok(hr == S_OK, "Unexpected hr %#lx\n", hr);
/*
* ProviderOptions_UseComThreading not set, GetPropertyValue will be
* called on the current thread.
*/
Provider_child.expected_tid = GetCurrentThreadId();
check_uia_prop_val(UIA_LabeledByPropertyId, UIAutomationType_Element, &v);
CoUninitialize();
return 0;
}
static void test_uia_node_from_prov_com_threading(void)
{
HANDLE thread;
HUIANODE node;
HRESULT hr;
/* Test ProviderOptions_UseComThreading. */
Provider.hwnd = NULL;
prov_root = NULL;
Provider.prov_opts = ProviderOptions_ServerSideProvider | ProviderOptions_UseComThreading;
hr = UiaNodeFromProvider(&Provider.IRawElementProviderSimple_iface, &node);
ok_method_sequence(node_from_prov8, "node_from_prov8");
/*
* On Windows versions prior to Windows 10, UiaNodeFromProvider ignores the
* ProviderOptions_UseComThreading flag.
*/
if (hr == S_OK)
{
win_skip("Skipping ProviderOptions_UseComThreading tests for UiaNodeFromProvider.\n");
UiaNodeRelease(node);
return;
}
ok(hr == CO_E_NOTINITIALIZED, "Unexpected hr %#lx.\n", hr);
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
hr = UiaNodeFromProvider(&Provider.IRawElementProviderSimple_iface, &node);
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
ok(Provider.ref == 2, "Unexpected refcnt %ld\n", Provider.ref);
ok_method_sequence(node_from_prov8, "node_from_prov8");
Provider.expected_tid = GetCurrentThreadId();
thread = CreateThread(NULL, 0, uia_node_from_provider_test_com_thread, (void *)node, 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);
ok(UiaNodeRelease(node), "UiaNodeRelease returned FALSE\n");
ok(Provider.ref == 1, "Unexpected refcnt %ld\n", Provider.ref);
Provider_child.expected_tid = Provider.expected_tid = 0;
CoUninitialize();
}
static void test_UiaNodeFromProvider(void)
{
WNDCLASSA cls;
......@@ -3784,6 +3895,9 @@ static void test_UiaNodeFromProvider(void)
hwnd = CreateWindowA("UiaNodeFromProvider class", "Test window", WS_OVERLAPPEDWINDOW,
0, 0, 100, 100, NULL, NULL, NULL, NULL);
/* Run these tests early, we end up in an implicit MTA later. */
test_uia_node_from_prov_com_threading();
CoInitializeEx(NULL, COINIT_MULTITHREADED);
hr = UiaNodeFromProvider(NULL, &node);
......@@ -4352,6 +4466,18 @@ static void test_UiaGetPropertyValue(void)
START_TEST(uiautomation)
{
HMODULE uia_dll = LoadLibraryA("uiautomationcore.dll");
BOOL (WINAPI *pImmDisableIME)(DWORD);
HMODULE hModuleImm32;
/* Make sure COM isn't initialized by imm32. */
hModuleImm32 = LoadLibraryA("imm32.dll");
if (hModuleImm32) {
pImmDisableIME = (void *)GetProcAddress(hModuleImm32, "ImmDisableIME");
if (pImmDisableIME)
pImmDisableIME(0);
}
pImmDisableIME = NULL;
FreeLibrary(hModuleImm32);
test_UiaHostProviderFromHwnd();
test_uia_reserved_value_ifaces();
......
......@@ -16,7 +16,7 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#pragma makedep header
#pragma makedep regtypelib
import "oaidl.idl";
......@@ -27,21 +27,31 @@ struct uia_prop_info {
};
[
object,
uuid(57865755-6c05-4522-98df-4ca658b768ef),
pointer_default(unique),
version(1.0),
uuid(8a9ca8eb-856b-43d9-abd7-4a590054064f),
]
interface IWineUiaProvider : IUnknown
library UIA_wine_private
{
HRESULT get_prop_val([in]const struct uia_prop_info *prop_info, [out, retval]VARIANT *ret_val);
}
importlib("stdole2.tlb");
[
object,
uuid(bccb6799-d831-4057-bd50-6425823ff1a3),
pointer_default(unique),
]
interface IWineUiaNode : IUnknown
{
HRESULT get_provider([out, retval]IWineUiaProvider **out_prov);
[
object,
uuid(57865755-6c05-4522-98df-4ca658b768ef),
pointer_default(unique),
oleautomation,
]
interface IWineUiaProvider : IUnknown
{
HRESULT get_prop_val([in]const struct uia_prop_info *prop_info, [out, retval]VARIANT *ret_val);
}
[
object,
uuid(bccb6799-d831-4057-bd50-6425823ff1a3),
pointer_default(unique),
]
interface IWineUiaNode : IUnknown
{
HRESULT get_provider([out, retval]IWineUiaProvider **out_prov);
}
}
......@@ -148,6 +148,18 @@ exit:
}
}
static HRESULT get_global_interface_table(IGlobalInterfaceTable **git)
{
HRESULT hr;
hr = CoCreateInstance(&CLSID_StdGlobalInterfaceTable, NULL,
CLSCTX_INPROC_SERVER, &IID_IGlobalInterfaceTable, (void **)git);
if (FAILED(hr))
WARN("Failed to get GlobalInterfaceTable, hr %#lx\n", hr);
return hr;
}
/*
* IWineUiaNode interface.
*/
......@@ -156,6 +168,7 @@ struct uia_node {
LONG ref;
IWineUiaProvider *prov;
DWORD git_cookie;
};
static inline struct uia_node *impl_from_IWineUiaNode(IWineUiaNode *iface)
......@@ -192,6 +205,20 @@ static ULONG WINAPI uia_node_Release(IWineUiaNode *iface)
TRACE("%p, refcount %ld\n", node, ref);
if (!ref)
{
if (node->git_cookie)
{
IGlobalInterfaceTable *git;
HRESULT hr;
hr = get_global_interface_table(&git);
if (SUCCEEDED(hr))
{
hr = IGlobalInterfaceTable_RevokeInterfaceFromGlobal(git, node->git_cookie);
if (FAILED(hr))
WARN("Failed to get revoke provider interface from Global Interface Table, hr %#lx\n", hr);
}
}
IWineUiaProvider_Release(node->prov);
heap_free(node);
}
......@@ -203,8 +230,30 @@ static HRESULT WINAPI uia_node_get_provider(IWineUiaNode *iface, IWineUiaProvide
{
struct uia_node *node = impl_from_IWineUiaNode(iface);
*out_prov = node->prov;
IWineUiaProvider_AddRef(node->prov);
if (node->git_cookie)
{
IGlobalInterfaceTable *git;
IWineUiaProvider *prov;
HRESULT hr;
hr = get_global_interface_table(&git);
if (FAILED(hr))
return hr;
hr = IGlobalInterfaceTable_GetInterfaceFromGlobal(git, node->git_cookie,
&IID_IWineUiaProvider, (void **)&prov);
if (FAILED(hr))
{
ERR("Failed to get provider interface from GlobalInterfaceTable, hr %#lx\n", hr);
return hr;
}
*out_prov = prov;
}
else
{
*out_prov = node->prov;
IWineUiaProvider_AddRef(node->prov);
}
return S_OK;
}
......@@ -377,7 +426,7 @@ static const IWineUiaProviderVtbl uia_provider_vtbl = {
static HRESULT create_wine_uia_provider(struct uia_node *node, IRawElementProviderSimple *elprov)
{
static const int supported_prov_opts = ProviderOptions_ServerSideProvider;
static const int supported_prov_opts = ProviderOptions_ServerSideProvider | ProviderOptions_UseComThreading;
enum ProviderOptions prov_opts;
struct uia_provider *prov;
HRESULT hr;
......@@ -395,10 +444,36 @@ static HRESULT create_wine_uia_provider(struct uia_node *node, IRawElementProvid
prov->IWineUiaProvider_iface.lpVtbl = &uia_provider_vtbl;
prov->elprov = elprov;
IRawElementProviderSimple_AddRef(elprov);
prov->ref = 1;
node->prov = &prov->IWineUiaProvider_iface;
/*
* If the UseComThreading ProviderOption is specified, all calls to the
* provided IRawElementProviderSimple need to respect the apartment type
* of the thread that creates the HUIANODE. i.e, if it's created in an
* STA, and the HUIANODE is used in an MTA, we need to provide a proxy.
*/
if (prov_opts & ProviderOptions_UseComThreading)
{
IGlobalInterfaceTable *git;
hr = get_global_interface_table(&git);
if (FAILED(hr))
{
heap_free(prov);
return hr;
}
hr = IGlobalInterfaceTable_RegisterInterfaceInGlobal(git, (IUnknown *)&prov->IWineUiaProvider_iface,
&IID_IWineUiaProvider, &node->git_cookie);
if (FAILED(hr))
{
heap_free(prov);
return hr;
}
}
IRawElementProviderSimple_AddRef(elprov);
return S_OK;
}
......
......@@ -16,9 +16,11 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#define COBJMACROS
#include "combaseapi.h"
#include "initguid.h"
#include "uia_private.h"
#include "ocidl.h"
#include "wine/debug.h"
#include "wine/heap.h"
......
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