From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 24 ++++++++++++++++ dlls/uiautomationcore/uia_com_client.c | 32 ++++++++++++++++++++++ dlls/uiautomationcore/uia_event.c | 1 + dlls/uiautomationcore/uia_private.h | 1 + dlls/uiautomationcore/uia_utils.c | 17 ++++++++++++ 5 files changed, 75 insertions(+)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 4367b448518..bacb075cb8a 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -15474,6 +15474,30 @@ static void test_uia_com_event_handler_event_advisement(IUIAutomation *uia_iface check_uia_hwnd_expects_at_most(0, 1, 1, 3, 0); test_hwnd_providers_event_advise_added(&Provider, &Provider_hwnd2, &Provider_nc2, 0, FALSE);
+ /* HWND destruction is tracked with EVENT_OBJECT_DESTROY/OBJID_WINDOW. */ + NotifyWinEvent(EVENT_OBJECT_DESTROY, test_child_hwnd, OBJID_WINDOW, CHILDID_SELF); + if (wait_for_clientside_callbacks(2000)) trace("Kept getting callbacks up until timeout\n"); + + /* + * EVENT_OBJECT_DESTROY removed this HWND, EVENT_OBJECT_FOCUS will now + * advise it again. + */ + reset_event_advise_values_for_hwnd_providers(&Provider2, &Provider_hwnd3, &Provider_nc3); + set_provider_method_event_data(&Provider2, method_event[0], ADVISE_EVENTS_EVENT_ADDED); + SET_EXPECT_MULTI(child_winproc_GETOBJECT_UiaRoot, 3); /* Only sent 3 times on Win11. */ + set_uia_hwnd_expects(0, 1, 1, 2, 0); /* Only Win11 sends WM_GETOBJECT 2 times. */ + + NotifyWinEvent(EVENT_OBJECT_FOCUS, test_child_hwnd, OBJID_CLIENT, CHILDID_SELF); + ok(msg_wait_for_all_events(method_event, 1, 2000) != WAIT_TIMEOUT, "Wait for method_event(s) timed out.\n"); + if (wait_for_clientside_callbacks(2000)) trace("Kept getting callbacks up until timeout\n"); + + set_provider_method_event_data(&Provider2, NULL, -1); + check_uia_hwnd_expects_at_most(0, 1, 1, 2, 0); + CHECK_CALLED_AT_MOST(child_winproc_GETOBJECT_UiaRoot, 3); + test_provider_event_advise_added(&Provider2, UIA_AutomationFocusChangedEventId, FALSE); + test_provider_event_advise_added(&Provider_hwnd3, 0, FALSE); + test_provider_event_advise_added(&Provider_nc3, 0, FALSE); + set_uia_hwnd_expects(0, 1, 1, 0, 0); hr = IUIAutomation_RemoveFocusChangedEventHandler(uia_iface, &FocusChangedHandler.IUIAutomationFocusChangedEventHandler_iface); diff --git a/dlls/uiautomationcore/uia_com_client.c b/dlls/uiautomationcore/uia_com_client.c index 22b55dd5234..91013dfc4ed 100644 --- a/dlls/uiautomationcore/uia_com_client.c +++ b/dlls/uiautomationcore/uia_com_client.c @@ -1108,6 +1108,38 @@ HRESULT uia_com_win_event_callback(DWORD event_id, HWND hwnd, LONG obj_id, LONG break; }
+ case EVENT_OBJECT_DESTROY: + { + static const int uia_event_id = UIA_AutomationFocusChangedEventId; + struct rb_entry *rb_entry; + + if (obj_id != OBJID_WINDOW) + break; + + EnterCriticalSection(&com_event_handlers_cs); + + if ((rb_entry = rb_get(&com_event_handlers.handler_event_id_map, &uia_event_id))) + { + struct uia_event_handler_event_id_map_entry *event_id_map; + struct uia_event_handler_map_entry *entry; + + event_id_map = RB_ENTRY_VALUE(rb_entry, struct uia_event_handler_event_id_map_entry, entry); + LIST_FOR_EACH_ENTRY(entry, &event_id_map->handlers_list, struct uia_event_handler_map_entry, + handler_event_id_map_list_entry) + { + struct uia_com_event *event; + + LIST_FOR_EACH_ENTRY(event, &entry->handlers_list, struct uia_com_event, event_handler_map_list_entry) + { + uia_hwnd_map_remove_hwnd(&event->focus_hwnd_map, hwnd); + } + } + } + + LeaveCriticalSection(&com_event_handlers_cs); + break; + } + default: break; } diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c index 0e6cf4e000a..2cb61a2062e 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -56,6 +56,7 @@ static int win_event_to_uia_event_id(int win_event) case EVENT_OBJECT_FOCUS: return UIA_AutomationFocusChangedEventId; case EVENT_SYSTEM_ALERT: return UIA_SystemAlertEventId; case EVENT_OBJECT_SHOW: return UIA_StructureChangedEventId; + case EVENT_OBJECT_DESTROY: return UIA_StructureChangedEventId;
default: break; diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index 0c4f1ee9dda..b55379b459d 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -270,5 +270,6 @@ BOOL uia_hwnd_is_visible(HWND hwnd) DECLSPEC_HIDDEN; BOOL uia_is_top_level_hwnd(HWND hwnd) DECLSPEC_HIDDEN; BOOL uia_hwnd_map_check_hwnd(struct rb_tree *hwnd_map, HWND hwnd) DECLSPEC_HIDDEN; HRESULT uia_hwnd_map_add_hwnd(struct rb_tree *hwnd_map, HWND hwnd) DECLSPEC_HIDDEN; +void uia_hwnd_map_remove_hwnd(struct rb_tree *hwnd_map, HWND hwnd) DECLSPEC_HIDDEN; void uia_hwnd_map_init(struct rb_tree *hwnd_map) DECLSPEC_HIDDEN; void uia_hwnd_map_destroy(struct rb_tree *hwnd_map) DECLSPEC_HIDDEN; diff --git a/dlls/uiautomationcore/uia_utils.c b/dlls/uiautomationcore/uia_utils.c index 866358f97c8..f62f85d5883 100644 --- a/dlls/uiautomationcore/uia_utils.c +++ b/dlls/uiautomationcore/uia_utils.c @@ -459,6 +459,23 @@ HRESULT uia_hwnd_map_add_hwnd(struct rb_tree *hwnd_map, HWND hwnd) return S_OK; }
+void uia_hwnd_map_remove_hwnd(struct rb_tree *hwnd_map, HWND hwnd) +{ + struct rb_entry *rb_entry = rb_get(hwnd_map, hwnd); + struct uia_hwnd_map_entry *entry; + + if (!rb_entry) + { + TRACE("hwnd %p not in map %p, nothing to remove.\n", hwnd, hwnd_map); + return; + } + + TRACE("Removing hwnd %p from map %p\n", hwnd, hwnd_map); + entry = RB_ENTRY_VALUE(rb_entry, struct uia_hwnd_map_entry, entry); + rb_remove(hwnd_map, &entry->entry); + free(entry); +} + void uia_hwnd_map_init(struct rb_tree *hwnd_map) { rb_init(hwnd_map, uia_hwnd_map_hwnd_compare);