I want to use Microsoft UIAutomation, from C#.
I collected some finds I made on the internet and ended up with the following code (using the Nuget package "Interop.UIAutomationClient" version="10.19041.0".)
using System;
using UIA = Interop.UIAutomationClient;
namespace TestUIA
{
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("Starting...");
UIA.IUIAutomation NativeAutomation = new UIA.CUIAutomation8();
var Desktop = NativeAutomation.GetRootElement();
Console.WriteLine("Destop name is {0}", Desktop.CurrentName);
Console.WriteLine("Ending...");
}
}
}
That works great!
Actual result on my Windows 11 22H2 is:
Now I want to enumerate the Desktop children. I know that I have to use the FindAll
method.
Unfortunately, I am only capable of coding the first parameter, as in
Desktop.FindAll( UIA.TreeScope.TreeScope_Children,
but I don't know how to code the second argument... if I were on C++, I would use IUIAutomation::CreateTrueCondition
...
QUESTION: how to pass a "True Condition" to FindAll
, in C#, with the Nuget package "Interop.UIAutomationClient"?
You don't need any nuget, you can just reference UIAutomationClient COM dll from Windows as a COM Object:
Now, to create the true condition, as you said, you must use the IUIAutomation::CreateTrueCondition method which is implemented on ... CUIAutomation8 (the root class, it's the one which implements IUIAutomation
). I've written some extension methods to make that easier:
using System;
using System.Collections.Generic;
using System.Linq;
using UIAutomationClient;
internal class Program
{
static void Main()
{
Console.WriteLine("Starting...");
var desktop = UIAUtilities.Automation.GetRootElement();
Console.WriteLine("Destop name is {0}", desktop.CurrentName);
foreach (var element in desktop.EnumerateAll(TreeScope.TreeScope_Children))
{
Console.WriteLine(element.CurrentName);
}
Console.WriteLine("Ending...");
}
}
public static class UIAUtilities
{
private static readonly Lazy<CUIAutomation8> _automation = new(() => new CUIAutomation8());
public static CUIAutomation8 Automation => _automation.Value;
public static IEnumerable<IUIAutomationElement> EnumerateAll(this IUIAutomationElement element, TreeScope scope = TreeScope.TreeScope_Children, IUIAutomationCondition condition = null)
{
if (element == null)
return Enumerable.Empty<IUIAutomationElement>();
condition ??= Automation.CreateTrueCondition();
return ForEach(element.FindAll(scope, condition));
}
public static IEnumerable<IUIAutomationElement> ForEach(this IUIAutomationElementArray array)
{
if (array == null)
yield break;
for (var i = 0; i < array.Length; i++)
{
var element = array.GetElement(i);
if (element != null)
yield return element;
}
}
}
Here is how the project file (.NET core+) should look like with the COM reference:
<Project Sdk="Microsoft.NET.Sdk">
...
<ItemGroup>
<COMReference Include="UIAutomationClient">
<WrapperTool>tlbimp</WrapperTool>
<VersionMinor>0</VersionMinor>
<VersionMajor>1</VersionMajor>
<Guid>944de083-8fb8-45cf-bcb7-c477acb2f897</Guid>
<Lcid>0</Lcid>
<Isolated>false</Isolated>
<EmbedInteropTypes>true</EmbedInteropTypes>
</COMReference>
</ItemGroup>
...
</Project>