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 ...@@ -1101,6 +1101,7 @@ static struct Provider
enum ProviderOptions prov_opts; enum ProviderOptions prov_opts;
HWND hwnd; HWND hwnd;
BOOL ret_invalid_prop_type; BOOL ret_invalid_prop_type;
DWORD expected_tid;
} Provider, Provider2, Provider_child, Provider_child2; } Provider, Provider2, Provider_child, Provider_child2;
static const WCHAR *uia_bstr_prop_str = L"uia-string"; static const WCHAR *uia_bstr_prop_str = L"uia-string";
...@@ -1443,6 +1444,8 @@ HRESULT WINAPI ProviderSimple_get_ProviderOptions(IRawElementProviderSimple *ifa ...@@ -1443,6 +1444,8 @@ HRESULT WINAPI ProviderSimple_get_ProviderOptions(IRawElementProviderSimple *ifa
struct Provider *This = impl_from_ProviderSimple(iface); struct Provider *This = impl_from_ProviderSimple(iface);
add_method_call(This, PROV_GET_PROVIDER_OPTIONS); 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; *ret_val = 0;
if (This->prov_opts) if (This->prov_opts)
...@@ -1467,6 +1470,8 @@ HRESULT WINAPI ProviderSimple_GetPropertyValue(IRawElementProviderSimple *iface, ...@@ -1467,6 +1470,8 @@ HRESULT WINAPI ProviderSimple_GetPropertyValue(IRawElementProviderSimple *iface,
struct Provider *This = impl_from_ProviderSimple(iface); struct Provider *This = impl_from_ProviderSimple(iface);
add_method_call(This, PROV_GET_PROPERTY_VALUE); 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); VariantInit(ret_val);
switch (prop_id) switch (prop_id)
...@@ -1654,6 +1659,8 @@ HRESULT WINAPI ProviderSimple_get_HostRawElementProvider(IRawElementProviderSimp ...@@ -1654,6 +1659,8 @@ HRESULT WINAPI ProviderSimple_get_HostRawElementProvider(IRawElementProviderSimp
struct Provider *This = impl_from_ProviderSimple(iface); struct Provider *This = impl_from_ProviderSimple(iface);
add_method_call(This, PROV_GET_HOST_RAW_ELEMENT_PROVIDER); 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; *ret_val = NULL;
if (This->hwnd) if (This->hwnd)
...@@ -1702,6 +1709,8 @@ static HRESULT WINAPI ProviderFragment_Navigate(IRawElementProviderFragment *ifa ...@@ -1702,6 +1709,8 @@ static HRESULT WINAPI ProviderFragment_Navigate(IRawElementProviderFragment *ifa
struct Provider *This = impl_from_ProviderFragment(iface); struct Provider *This = impl_from_ProviderFragment(iface);
add_method_call(This, FRAG_NAVIGATE); add_method_call(This, FRAG_NAVIGATE);
if (This->expected_tid)
ok(This->expected_tid == GetCurrentThreadId(), "Unexpected tid %ld\n", GetCurrentThreadId());
*ret_val = NULL; *ret_val = NULL;
if ((direction == NavigateDirection_Parent) && This->parent) if ((direction == NavigateDirection_Parent) && This->parent)
...@@ -3759,6 +3768,108 @@ static const struct prov_method_sequence node_from_prov8[] = { ...@@ -3759,6 +3768,108 @@ static const struct prov_method_sequence node_from_prov8[] = {
{ 0 } { 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) static void test_UiaNodeFromProvider(void)
{ {
WNDCLASSA cls; WNDCLASSA cls;
...@@ -3784,6 +3895,9 @@ static void test_UiaNodeFromProvider(void) ...@@ -3784,6 +3895,9 @@ static void test_UiaNodeFromProvider(void)
hwnd = CreateWindowA("UiaNodeFromProvider class", "Test window", WS_OVERLAPPEDWINDOW, hwnd = CreateWindowA("UiaNodeFromProvider class", "Test window", WS_OVERLAPPEDWINDOW,
0, 0, 100, 100, NULL, NULL, NULL, NULL); 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); CoInitializeEx(NULL, COINIT_MULTITHREADED);
hr = UiaNodeFromProvider(NULL, &node); hr = UiaNodeFromProvider(NULL, &node);
...@@ -4352,6 +4466,18 @@ static void test_UiaGetPropertyValue(void) ...@@ -4352,6 +4466,18 @@ static void test_UiaGetPropertyValue(void)
START_TEST(uiautomation) START_TEST(uiautomation)
{ {
HMODULE uia_dll = LoadLibraryA("uiautomationcore.dll"); 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_UiaHostProviderFromHwnd();
test_uia_reserved_value_ifaces(); test_uia_reserved_value_ifaces();
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/ */
#pragma makedep header #pragma makedep regtypelib
import "oaidl.idl"; import "oaidl.idl";
...@@ -27,21 +27,31 @@ struct uia_prop_info { ...@@ -27,21 +27,31 @@ struct uia_prop_info {
}; };
[ [
object, version(1.0),
uuid(57865755-6c05-4522-98df-4ca658b768ef), uuid(8a9ca8eb-856b-43d9-abd7-4a590054064f),
pointer_default(unique),
] ]
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, object,
uuid(bccb6799-d831-4057-bd50-6425823ff1a3), uuid(57865755-6c05-4522-98df-4ca658b768ef),
pointer_default(unique), pointer_default(unique),
] oleautomation,
interface IWineUiaNode : IUnknown ]
{ interface IWineUiaProvider : IUnknown
HRESULT get_provider([out, retval]IWineUiaProvider **out_prov); {
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: ...@@ -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. * IWineUiaNode interface.
*/ */
...@@ -156,6 +168,7 @@ struct uia_node { ...@@ -156,6 +168,7 @@ struct uia_node {
LONG ref; LONG ref;
IWineUiaProvider *prov; IWineUiaProvider *prov;
DWORD git_cookie;
}; };
static inline struct uia_node *impl_from_IWineUiaNode(IWineUiaNode *iface) static inline struct uia_node *impl_from_IWineUiaNode(IWineUiaNode *iface)
...@@ -192,6 +205,20 @@ static ULONG WINAPI uia_node_Release(IWineUiaNode *iface) ...@@ -192,6 +205,20 @@ static ULONG WINAPI uia_node_Release(IWineUiaNode *iface)
TRACE("%p, refcount %ld\n", node, ref); TRACE("%p, refcount %ld\n", node, ref);
if (!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); IWineUiaProvider_Release(node->prov);
heap_free(node); heap_free(node);
} }
...@@ -203,8 +230,30 @@ static HRESULT WINAPI uia_node_get_provider(IWineUiaNode *iface, IWineUiaProvide ...@@ -203,8 +230,30 @@ static HRESULT WINAPI uia_node_get_provider(IWineUiaNode *iface, IWineUiaProvide
{ {
struct uia_node *node = impl_from_IWineUiaNode(iface); struct uia_node *node = impl_from_IWineUiaNode(iface);
*out_prov = node->prov; if (node->git_cookie)
IWineUiaProvider_AddRef(node->prov); {
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; return S_OK;
} }
...@@ -377,7 +426,7 @@ static const IWineUiaProviderVtbl uia_provider_vtbl = { ...@@ -377,7 +426,7 @@ static const IWineUiaProviderVtbl uia_provider_vtbl = {
static HRESULT create_wine_uia_provider(struct uia_node *node, IRawElementProviderSimple *elprov) 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; enum ProviderOptions prov_opts;
struct uia_provider *prov; struct uia_provider *prov;
HRESULT hr; HRESULT hr;
...@@ -395,10 +444,36 @@ static HRESULT create_wine_uia_provider(struct uia_node *node, IRawElementProvid ...@@ -395,10 +444,36 @@ static HRESULT create_wine_uia_provider(struct uia_node *node, IRawElementProvid
prov->IWineUiaProvider_iface.lpVtbl = &uia_provider_vtbl; prov->IWineUiaProvider_iface.lpVtbl = &uia_provider_vtbl;
prov->elprov = elprov; prov->elprov = elprov;
IRawElementProviderSimple_AddRef(elprov);
prov->ref = 1; prov->ref = 1;
node->prov = &prov->IWineUiaProvider_iface; 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; return S_OK;
} }
......
...@@ -16,9 +16,11 @@ ...@@ -16,9 +16,11 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/ */
#define COBJMACROS
#include "combaseapi.h"
#include "initguid.h" #include "initguid.h"
#include "uia_private.h" #include "uia_private.h"
#include "ocidl.h"
#include "wine/debug.h" #include "wine/debug.h"
#include "wine/heap.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