I'm afraid I don't know the cause of the delays that you're seeing, but here are some thoughts on this...
Everything I say below relates to the native UIA API in Windows, not the managed .NET UIA API. All improvements to UIA in recent years have been made to the Windows UIA API. So whenever I write UIA client C# code, I call UIA through a managed wrapper that I generate with the tlbimp.exe SDK tool.
That is, I first generate the wrapper with a command like...
"C:Program Files (x86)Microsoft SDKsWindowsv8.1AinNETFX 4.5.1 Toolsx64lbimp.exe" c:windowssystem32uiautomationcore.dll /out:Interop.UIAutomationCore.dll
Then I include a reference to the Interop.UIAutomationCore.dll in my C# project, add "using Interop.UIAutomationCore;" to my C# file, and then I can do things like...
IUIAutomation uiAutomation = new CUIAutomation8();
IUIAutomationElement rootElement = uiAutomation.GetRootElement();
uiAutomation.AddAutomationEventHandler(
20016, // UIA_Window_WindowOpenedEventId
rootElement,
TreeScope.TreeScope_Descendants,
null,
this);
...
public void HandleAutomationEvent(IUIAutomationElement sender, int eventId)
{
// Got a window opened event...
}
In Windows 7, there were some important constraints around UIA event handlers. It was easy to write event handlers which didn't account for those constraints, and that could lead to long delays when interacting with UIA. For example, it was important to not add or remove a UIA event handler from inside an event handler. So at the time, I intentionally made no UIA calls at all from inside my event handlers. Instead, I'd post myself a message or add some action to a queue, allow my event handler to return, and take whatever action I wanted to in response to the event shortly afterwards on another thread. This required some more work on my part, but I didn't want to risk hitting delays. And any threads I created would be running in an MTA.
An example of the action described above is in my old focus tracking sample up at https://code.msdn.microsoft.com/windowsapps/Windows-7-UI-Automation-6390614a/sourcecode?fileId=21469&pathId=715901329. The file FocusEventHandler.cs creates the MTA thread and queues messages to avoid making UIA calls inside the event hander.
Since Window 7, I know the constraints in UIA relating to threading and delays have been relaxed, and the likelihood of encountering delays has been reduced. More recently, there were some improvements between Windows 8.1 and Windows 10 in this area, so if it'd be practical to run your code on Windows 10, it would be interesting to see if the delays still repro there.
I know this is time consuming, but you might be interested in removing the interaction with UIA inside your event handlers and seeing if the delays go away. If they do, it'd be a case of determining which action seems to trigger the problem, and seeing if there's an alternative way of achieving your goals without performing the UIA interaction in the event handlers.
For example, in your event handler, you call...
this.getAutomationParent(element).GetRuntimeId();
I expect this will lead to two calls back into the provider app which generated the event. The first call is to get the parent of the source element, and the second call is to get the RuntimeId of that parent. So while UIA is waiting for your event handler to return, you've called twice back into UIA. While I don't know that that's a problem, I'd avoid it.
Sometimes you can avoid a cross-proc call back to the provider process by having some data of interest cached with the event itself. For example, say I know I'm going to want the RuntimeId of an element that raised a WindowOpened event. I can ask UIA to cache that data with the events I receive, when I register for the events.
int propertyRuntimeId = 30000; // UIA_RuntimeIdPropertyId
...
IUIAutomationCacheRequest cacheRequestRuntimeId = uiAutomation.CreateCacheRequest();
cacheRequestRuntimeId.AddProperty(propertyRuntimeId);
uiAutomation.AddAutomationEventHandler(
20016, // UIA_Window_WindowOpenedEventId
rootElement,
TreeScope.TreeScope_Descendants,
cacheRequestRuntimeId,
this);
...
public void HandleAutomationEvent(IUIAutomationElement sender, int eventId)
{
// Got a window opened event...
// Get the RuntimeId from the source element. Because that data is cached with the
// event, we don't have to call back through UIA into the provider process here.
int[] runtimeId = sender.GetCachedPropertyValue(propertyRuntimeId);
}
On a side note, when practical, I always cache data when dealing with events or accessing elements through UIA, (by using calls such as FindFirstBuildCache(),) as I want to avoid as many cross-proc calls as possible.
So my advice would be:
- Use the native Windows UIA API with a managed wrapper generated by tlbimp.exe.
- Cache as much data as possible with the events, to avoid having to call back into the provider process unnecessarily later.
- Avoid calls back into UIA from inside a UIA event handler.
Thanks,
Guy