How can I capture Form events from a separate running process
I'm trying to automate a product that provides no such automation capability.
I've take a cursory look at loading the app in a separate AppDomain, and also, through reflection, executing the Program.Main() to get the app running. I also tried getting the windowhandle from a separately created Process object (which I've learned will not work).
If I have a reference to their assembly added to my project so I can reference an instance of "TheirProduct.FormMain", what, if it's possib开发者_C百科le, is the best way to go about capturing events from that form?
What I need to do is to be able to capture a couple events, and perform a few Button.PerformClick() against the form.
Check out the The Microsoft UI Automation Library, it ships with .Net 3.5 and 4.0. Here's a code sample for 4.0, just add a reference to UIAutomationClient and UIAutomationTypes. The program launches the calculator and presses some buttons.
Option Explicit On
Option Strict On
Imports System.Windows.Automation
Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
''//Start the calculator
Using P = Process.Start("calc.exe")
''//Hack, pause for a bit while calculator starts
System.Threading.Thread.Sleep(2000)
''//Try and grab the calculator window
Dim CalcWindow = AutomationElement.FromHandle(P.MainWindowHandle)
''//Make sure we've got something
If CalcWindow Is Nothing Then Throw New ApplicationException("Could find calculator window")
''//Grab all of the calculator buttons
Dim Buttons = CalcWindow.FindAll(TreeScope.Descendants, New PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Button))
If (Buttons Is Nothing) OrElse (Buttons.Count = 0) Then Throw New ApplicationException("Could not find any buttons on the calculator")
''//Grab individual buttons by label
Dim B5 = GetObjectByLabel(Buttons, "5")
Dim BAdd = GetObjectByLabel(Buttons, "Add")
Dim B7 = GetObjectByLabel(Buttons, "7")
Dim BEquals = GetObjectByLabel(Buttons, "Equals")
''//Press the buttons
DirectCast(B5.GetCurrentPattern(InvokePattern.Pattern), InvokePattern).Invoke()
DirectCast(BAdd.GetCurrentPattern(InvokePattern.Pattern), InvokePattern).Invoke()
DirectCast(B7.GetCurrentPattern(InvokePattern.Pattern), InvokePattern).Invoke()
DirectCast(BEquals.GetCurrentPattern(InvokePattern.Pattern), InvokePattern).Invoke()
End Using
End Sub
Private Shared Function GetObjectByLabel(ByVal objects As AutomationElementCollection, ByVal label As String) As AutomationElement
''//Sanity check
If objects Is Nothing Then Throw New ArgumentNullException("objects")
If label Is Nothing Then Throw New ArgumentNullException("label")
''//Loop through each looking by name
For Each B As AutomationElement In objects
If B.Current.Name = label Then Return B
Next
Return Nothing
End Function
End Class
The UI Automation library is intended to work with a mocked up client with named controls but it also works with pretty much any program. If you didn't mock up your program nicely then you'll have to hack around as I did above.
There's plenty to read on the subject:
- MSDN - UI Automation Overview
- MSDN Magazine - The Microsoft UI Automation Library
- ArtfulBits
You might find it useful to inspect your program with Spy++, too.
精彩评论