开发者

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜