I'm attempting to use the MS UI Automation Framework in my application for some automated testing. My goal for now is to listen to GUI events and record them, similar to the sample provided (TestScriptGeneratorSample) in the Windows SDK.
Just using that demo application, I can launch a simple Windows Forms application with a single button and see that it records click events as "invoke" UIA events. However, for each click of the button, the demo application records 4 "invoke" events. Why is that? I don't see this issue when using a WPF button.
My guess is that since the System.Windows.Forms.Button
class supports MSAA instead of the UIA, the part of UIA that is bridging the MSAA interface is misbehaving, or at least behaving in a way that I don't understand and can't find any documentation about.
Maybe it's reporting an invoke event for mouse down, up, click, and then the actual button press?
Can anyone explain this behavior, and/or provide a workaround so that one button press results in one invoke event?
Edit: This is under WinXP SP3. I just installed the Windows Automation API 3.0 update and still see the same behavior.
Edit 2: I found this example where the author off-handedly mentions this behavior as a bug in the Win32 control, but cites no evidence...
Here's my sample application (form with a button on it), along with event listening baked into it. Add references to UIAutomationClient
and UIAutomationTypes
. Click the button and see invoke happen four times instead of one.
using System;
using System.Drawing;
using System.Windows.Automation;
using System.Windows.Forms;
namespace WindowsFormsApplication6
{
public partial class Form1 : Form
{
private TextBox m_Output;
public Form1()
{
InitializeComponent();
// set up the GUI with a single button...
Button b = new Button {Location = new Point(5, 5), Name = "Test", Text = "Click Me"};
// ... and a textbox to write events to.
m_Output = new TextBox { Location = new Point(5, 30), Name = "Output", Multiline = true, Size = new Size(250, 200) };
this.Controls.Add(b);
this.Controls.Add(m_Output);
// get the button as an UIA element
AutomationElement button = AutomationElement.FromHandle(b.Handle);
// attach a handler to the button, listening for the Invoke event
Automation.AddAutomationEventHandler(
InvokePattern.InvokedEvent,
button,
TreeScope.Element,
OnInvoked);
}
// Handler for button invoke events
private void OnInvoked(object Sender, AutomationEventArgs E)
{
AppendText("Invoked: " + ((AutomationElement)Sender).Current.AutomationId);
}
// Just dumps text to the textbox
private void AppendText(string Text)
{
if (m_Output.InvokeRequired)
{
m_Output.BeginInvoke((Action<string>)AppendText, Text);
}
else
{
m_Output.AppendText(DateTime.Now.ToString("hh:mm:ss.fff") + ": " + Text + Environment.NewLine);
}
}
}
}