捕获按钮 另一个应用程序中的消息框中的单击事件



我想在另一个WinForms应用程序显示的消息框中捕获OK按钮的Click事件。

我想使用 UI 自动化来实现这一点。经过一些研究,我发现IUIAutomation::AddAutomationEventHandler将为我完成这项工作。

虽然,我可以捕获任何其他按钮的Click事件,但我无法捕获 MessageBox 的Click事件。

我的代码如下:

var FindDialogButton = appElement.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.NameProperty, "OK"));
if (FindDialogButton != null)
{
if (FindDialogButton.GetSupportedPatterns().Any(p => p.Equals(InvokePattern.Pattern)))
{
Automation.AddAutomationEventHandler(InvokePattern.InvokedEvent, FindDialogButton, TreeScope.Element, new AutomationEventHandler(DialogHandler));
}
}
private void DialogHandler(object sender, AutomationEventArgs e)
{
MessageBox.Show("Dialog Button clicked at : " + DateTime.Now);
}

编辑:

我的完整代码如下:

private void DialogButtonHandle()
{
AutomationElement rootElement = AutomationElement.RootElement;
if (rootElement != null)
{
System.Windows.Automation.Condition condition = new PropertyCondition
(AutomationElement.NameProperty, "Windows Application"); //This part gets the handle of the Windows application that has the MessageBox
AutomationElement appElement = rootElement.FindFirst(TreeScope.Children, condition);
var FindDialogButton = appElement.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.NameProperty, "OK")); // This part gets the handle of the button inside the messagebox
if (FindDialogButton != null)
{
if (FindDialogButton.GetSupportedPatterns().Any(p => p.Equals(InvokePattern.Pattern)))
{
Automation.AddAutomationEventHandler(InvokePattern.InvokedEvent, FindDialogButton, TreeScope.Element, new AutomationEventHandler(DialogHandler)); //Here I am trying to catch the click of "OK" button inside the MessageBox
}
}
}
}
private void DialogHandler(object sender, AutomationEventArgs e)
{
//On Button click I am trying to display a message that the button has been clicked
MessageBox.Show("MessageBox Button Clicked");
}

我试图使此过程尽可能通用,以便无论您正在监视的应用程序在启动应用程序时是否已经在运行,它都可以正常工作。

您只需要提供被监视的应用程序的进程名称或其主窗口标题,即可让过程识别此应用程序。
使用以下字段之一和相应的枚举器:

private string appProcessName = "theAppProcessName"; and 
FindWindowMethod.ProcessName
// Or
private string appWindowTitle = "theAppMainWindowTitle"; and 
FindWindowMethod.Caption

将这些值传递给启动观察程序的过程,例如:

StartAppWatcher(appProcessName, FindWindowMethod.ProcessName); 

如您所见 - 由于您将问题标记为winforms- 这是一个完整的表单(名为frmWindowWatcher(,其中包含执行此任务所需的所有逻辑。

它是如何工作的:

  • 启动frmWindowWatcher时,该过程将验证监视的应用程序(此处使用其进程名称标识,但您可以更改方法,如前所述(是否已运行。
    如果是,它将初始化一个支持类ElementWindow,它将包含有关被监视应用程序的一些信息。
    我添加了这个支持类,以防您需要在监视的应用程序已经在运行时执行一些操作(在这种情况下,调用StartAppWatcher()方法时ElementWindow windowElement字段不会为 null(。这些信息在其他情况下也可能有用。
  • 在系统中打开新窗口时,该过程将验证此窗口是否属于监视的应用程序。如果是这样,则进程 ID 将相同。如果 Windows 是一个消息框(使用其标准ClassName#32770标识(,并且它属于监视的应用程序,则 AutomationEventHandler 将附加到子OK按钮。
    在这里,我使用了一个 委托:AutomationEventHandler DialogButtonHandler作为处理程序和一个实例字段 (AutomationElement msgBoxButton( 用于按钮元素,因为在关闭 MessageBox 时需要这些引用来删除按钮单击处理程序。
  • 单击消息框的OK按钮时,将调用MessageBoxButtonHandler方法。在这里,您可以确定此时要执行的操作。
  • 关闭frmWindowWatcher窗体时,将调用 Automation.RemoveAllEventHandlers(( 方法删除所有自动化处理程序,以提供最终清理并防止应用泄漏资源。


using System.Diagnostics;
using System.Linq;
using System.Windows.Automation;
using System.Windows.Forms;
public partial class frmWindowWatcher : Form
{
AutomationEventHandler DialogButtonHandler = null;
AutomationElement msgBoxButton = null;
ElementWindow windowElement = null;
int currentProcessId = 0;
private string appProcessName = "theAppProcessName";
//private string appWindowTitle = "theAppMainWindowTitle";
public enum FindWindowMethod
{
ProcessName,
Caption
}
public frmWindowWatcher()
{
InitializeComponent();
using (var proc = Process.GetCurrentProcess()) {
currentProcessId = proc.Id;
}
// Identify the application by its Process name...
StartAppWatcher(appProcessName, FindWindowMethod.ProcessName);
// ... or by its main Window Title
//StartAppWatcher(appWindowTitle, FindWindowMethod.Caption);
}
protected override void OnFormClosed(FormClosedEventArgs e)
{
Automation.RemoveAllEventHandlers();
base.OnFormClosed(e);
}
private void StartAppWatcher(string elementName, FindWindowMethod method)
{
windowElement = GetAppElement(elementName, method);
// (...)
// You may want to perform some actions if the watched application is already running when you start your app
Automation.AddAutomationEventHandler(WindowPattern.WindowOpenedEvent, AutomationElement.RootElement,
TreeScope.Subtree, (elm, e) => {
AutomationElement element = elm as AutomationElement;
try
{
if (element == null || element.Current.ProcessId == currentProcessId) return;
if (windowElement == null) windowElement = GetAppElement(elementName, method);
if (windowElement == null || windowElement.ProcessId != element.Current.ProcessId) return;
// If the Window is a MessageBox generated by the watched app, attach the handler
if (element.Current.ClassName == "#32770")
{
msgBoxButton = element.FindFirst(TreeScope.Descendants, 
new PropertyCondition(AutomationElement.NameProperty, "OK"));
if (msgBoxButton != null && msgBoxButton.GetSupportedPatterns().Any(p => p.Equals(InvokePattern.Pattern)))
{
Automation.AddAutomationEventHandler(
InvokePattern.InvokedEvent, msgBoxButton, TreeScope.Element,
DialogButtonHandler = new AutomationEventHandler(MessageBoxButtonHandler));
}
}
}
catch (ElementNotAvailableException) {
// Ignore: this exception may be raised if you show a modal dialog, 
// in your own app, that blocks the execution. When the dialog is closed, 
// AutomationElement element is no longer available
}
});
Automation.AddAutomationEventHandler(WindowPattern.WindowClosedEvent, AutomationElement.RootElement,
TreeScope.Subtree, (elm, e) => {
AutomationElement element = elm as AutomationElement;
if (element == null || element.Current.ProcessId == currentProcessId || windowElement == null) return;
if (windowElement.ProcessId == element.Current.ProcessId) {
if (windowElement.MainWindowTitle == element.Current.Name) {
windowElement = null;
}
}
});
}
private void MessageBoxButtonHandler(object sender, AutomationEventArgs e)
{
Console.WriteLine("Dialog Button clicked at : " + DateTime.Now.ToString());
// (...)
// Remove the handler after, since the next MessageBox needs a new handler.
Automation.RemoveAutomationEventHandler(e.EventId, msgBoxButton, DialogButtonHandler);
}
private ElementWindow GetAppElement(string elementName, FindWindowMethod method)
{
Process proc = null;
try {
switch (method) {
case FindWindowMethod.ProcessName:
proc = Process.GetProcessesByName(elementName).FirstOrDefault();
break;
case FindWindowMethod.Caption:
proc = Process.GetProcesses().FirstOrDefault(p => p.MainWindowTitle == elementName);
break;
}
return CreateElementWindow(proc);
}
finally {
proc?.Dispose();
}
}
private ElementWindow CreateElementWindow(Process process) => 
process == null ? null : new ElementWindow(process.ProcessName) {
MainWindowTitle = process.MainWindowTitle,
MainWindowHandle = process.MainWindowHandle,
ProcessId = process.Id
};
}

支持类,用于存储有关受监视应用程序的信息:它使用应用程序的进程名称进行初始化:

public ElementWindow(string processName)

但当然,您可以根据需要更改它,如前所述使用窗口标题,或者如果您愿意,甚至可以删除初始化的参数(当检测到并识别监视的应用程序时,只需要不null该类(。

using System.Collections.Generic;
public class ElementWindow
{
public ElementWindow(string processName) => this.ProcessName = processName;
public string ProcessName { get; set; }
public string MainWindowTitle { get; set; }
public int ProcessId { get; set; }
public IntPtr MainWindowHandle { get; set; }
}

最新更新