From: Connor McAdams conmanx360@gmail.com
When RPC is done in a multi-threaded apartment environment, this method may be called in a thread other than the one that the object was created in. To account for this, make sure to check the UI thread the HWND belongs to rather than the one belonging to the current thread.
Signed-off-by: Connor McAdams conmanx360@gmail.com --- dlls/oleacc/client.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/dlls/oleacc/client.c b/dlls/oleacc/client.c index 3b33be55a21..a726c607109 100644 --- a/dlls/oleacc/client.c +++ b/dlls/oleacc/client.c @@ -227,6 +227,7 @@ static HRESULT WINAPI Client_get_accRole(IAccessible *iface, VARIANT varID, VARI static HRESULT WINAPI Client_get_accState(IAccessible *iface, VARIANT varID, VARIANT *pvarState) { Client *This = impl_from_Client(iface); + GUITHREADINFO info; LONG style;
TRACE("(%p)->(%s %p)\n", This, debugstr_variant(&varID), pvarState); @@ -244,7 +245,10 @@ static HRESULT WINAPI Client_get_accState(IAccessible *iface, VARIANT varID, VAR V_I4(pvarState) |= STATE_SYSTEM_UNAVAILABLE; else if(IsWindow(This->hwnd)) V_I4(pvarState) |= STATE_SYSTEM_FOCUSABLE; - if(GetFocus() == This->hwnd) + + info.cbSize = sizeof(info); + if(GetGUIThreadInfo(GetWindowThreadProcessId(This->hwnd, NULL), &info) && + info.hwndFocus == This->hwnd) V_I4(pvarState) |= STATE_SYSTEM_FOCUSED; if(!(style & WS_VISIBLE)) V_I4(pvarState) |= STATE_SYSTEM_INVISIBLE;
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/oleacc/tests/main.c | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-)
diff --git a/dlls/oleacc/tests/main.c b/dlls/oleacc/tests/main.c index 5823b4336ba..b81d7d61288 100644 --- a/dlls/oleacc/tests/main.c +++ b/dlls/oleacc/tests/main.c @@ -1698,6 +1698,23 @@ static void test_AccessibleChildren(IAccessible *acc) ok(V_VT(children+2) == VT_EMPTY, "V_VT(children+2) = %d\n", V_VT(children+2)); }
+static DWORD WINAPI acc_focus_test_thread(LPVOID lpParameter) +{ + IAccessible *acc = (IAccessible *)lpParameter; + VARIANT vid, v; + HRESULT hr; + + V_VT(&vid) = VT_I4; + V_I4(&vid) = CHILDID_SELF; + hr = IAccessible_get_accState(acc, vid, &v); + ok(hr == S_OK, "got %x\n", hr); + ok(V_VT(&v) == VT_I4, "V_VT(&v) = %d\n", V_VT(&v)); + ok(V_I4(&v) == (STATE_SYSTEM_FOCUSABLE|STATE_SYSTEM_INVISIBLE|STATE_SYSTEM_FOCUSED), + "V_I4(&v) = %x\n", V_I4(&v)); + + return 0; +} + static void test_default_client_accessible_object(void) { IAccessible *acc; @@ -1712,6 +1729,7 @@ static void test_default_client_accessible_object(void) RECT rect; LONG l, left, top, width, height; ULONG fetched; + HANDLE hthread;
hwnd = CreateWindowA("oleacc_test", "wnd &t &junk", WS_OVERLAPPEDWINDOW, 0, 0, 100, 100, NULL, NULL, NULL, NULL); @@ -1745,7 +1763,6 @@ static void test_default_client_accessible_object(void)
IAccessible_Release(acc);
- /* Test the button */ hr = CreateStdAccessibleObject(btn, OBJID_CLIENT, &IID_IAccessible, (void**)&acc); ok(hr == S_OK, "got %x\n", hr); @@ -1764,7 +1781,6 @@ static void test_default_client_accessible_object(void)
IAccessible_Release(acc);
- /* Now we can test and destroy the top-level window */ hr = CreateStdAccessibleObject(hwnd, OBJID_CLIENT, &IID_IAccessible, (void**)&acc); ok(hr == S_OK, "got %x\n", hr); @@ -1870,6 +1886,21 @@ static void test_default_client_accessible_object(void) ok(V_I4(&v) == (STATE_SYSTEM_FOCUSABLE|STATE_SYSTEM_INVISIBLE), "V_I4(&v) = %x\n", V_I4(&v));
+ /* + * Test that STATE_SYSTEM_FOCUSED works, and is specific to the UI Thread + * of the HWND the IAccessible represents. + */ + SetFocus(hwnd); + hr = IAccessible_get_accState(acc, vid, &v); + ok(hr == S_OK, "got %x\n", hr); + ok(V_VT(&v) == VT_I4, "V_VT(&v) = %d\n", V_VT(&v)); + ok(V_I4(&v) == (STATE_SYSTEM_FOCUSABLE|STATE_SYSTEM_INVISIBLE|STATE_SYSTEM_FOCUSED), + "V_I4(&v) = %x\n", V_I4(&v)); + + hthread = CreateThread(NULL, 0, acc_focus_test_thread, (void *)acc, 0, NULL); + ok(!WaitForSingleObject(hthread, 10000), "Focus thread failed to return!\n"); + CloseHandle(hthread); + str = (void*)0xdeadbeef; hr = IAccessible_get_accHelp(acc, vid, &str); ok(hr == S_FALSE, "got %x\n", hr);