Creating a UI Automation Provider for a .Net Control
In this post, I will give an overview of the process of creating a client side UI Automation Provider for the MenuStrip Control. This will focos on how to get access to the data, not how to do each detailed step.
Create the Provider Assembly
Get a Control reference from the Windows Handle of the Control
Implement the MenuStrip Provider
Implement the following interfaces using the MenuStrip object:
IRawElementProviderSimple
IRawElementProviderFragment
IRawElementProviderFragmentRoot
See examples in the Windows SDK by searching for each of the previous interfaces.
Register the Provider
In the client, register the provider assembly at runtime.
As an alternative, a client could automatically load all the providers assemblies from a certain directory on the hard drive. This could also be made secure by using an AppDomain with limited permissions. I have not tested this, but I believe it would be the optimal solution for accessibility technology in order to extend the client with additional client side providers without actually modifying the client.
- Create the Provider Assembly
- Get a MenuStrip reference from the Windows Handle of the Control
- Implement the MenuStrip Provider using the MenuStrip object
- Register the Provider
Create the Provider Assembly
- Create a project with any name
- Add a static class called UIAutomationClientSideProviders
- Add a static field called ClientSideProviderDescriptionTable which is a ClientSideProviderDescription[]
- For each provider add a ClientSideProviderDescription to the array with a delegate used to create the provider and the UI "class name" of the control. The UI "class name" can be found with the UISpy tool or Spy++.
using System.Windows.Automation;
using System.Windows.Automation.Provider;
// The namespace must be the same as the name of the DLL,
// so that UI Automation can find the table of descriptors.
// In this example, the DLL would be "Auxsr.CustomClientSideProviders.dll"
namespace Auxsr.CustomClientSideProviders
{
// The assembly must implement a UIAutomationClientSideProviders class
public static class UIAutomationClientSideProviders
{
// Implementation of the static ClientSideProviderDescriptionTable field.
// In this case, only a single provider is listed in the table.
public static ClientSideProviderDescription[] ClientSideProviderDescriptionTable =
{
new ClientSideProviderDescription(
new ClientSideProviderFactoryCallback(MenuStripProvider.Create),
"WindowsForms10.Window.8.app.0.378734a")
};
}
}
Get a Control reference from the Windows Handle of the Control
- The ClientSideProviderFactoryCallback method takes a windows handle as the first parameter.
- Call the Control.FromHandle method to get a reference to the MenuStrip.
- Try to cast the Control to a MenuStrip reference
- If it succeded, you now have acces to the MenuStrip object
internal static IRawElementProviderSimple Create(
IntPtr hwnd, int idChild, int idObject )
{
// Get a reference to the managed control from the windows handle
Control controlFromHwnd = Control.FromHandle( hwnd );
// If the control is a MenuStrip
if( controlFromHwnd is MenuStrip )
{
// Create a menu strip provider
return new MenuStripProvider( controlFromHwnd as MenuStrip );
}
// Otherwise, return the default provider
else
{
return AutomationInteropProvider.HostProviderFromHandle( hwnd );
}
}
Implement the MenuStrip Provider
Implement the following interfaces using the MenuStrip object:
IRawElementProviderSimple
IRawElementProviderFragment
IRawElementProviderFragmentRoot
See examples in the Windows SDK by searching for each of the previous interfaces.
Register the Provider
In the client, register the provider assembly at runtime.
ClientSettings.RegisterClientSideProviderAssembly(
typeof(
Auxsr.CustomClientSideProviders.UIAutomationClientSideProviders
).Assembly.GetName() );
As an alternative, a client could automatically load all the providers assemblies from a certain directory on the hard drive. This could also be made secure by using an AppDomain with limited permissions. I have not tested this, but I believe it would be the optimal solution for accessibility technology in order to extend the client with additional client side providers without actually modifying the client.