gpt4 book ai didi

c# - 在另一个应用程序中的 MessageBox 中捕获按钮单击事件

转载 作者:行者123 更新时间:2023-11-30 15:51:02 26 4
gpt4 key购买 nike

我想在另一个 WinForms 应用程序显示的 MessageBox 上捕获 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,其中将包含有关被监视应用程序的一些信息。
    我添加了这个支持类,以防你需要在监视的应用程序已经运行时执行一些操作(在这种情况下,ElementWindow windowElement 字段在StartAppWatcher() 方法被调用)。这些信息在其他情况下也可能有用。
  • 当系统中打开一个新窗口时,该过程会验证该窗口是否属于监视的应用程序。如果是这样,进程 ID 将相同。如果 Windows 是一个 MessageBox(使用其标准 ClassName 标识:#32770)并且它属于被监视的应用程序,则 AutomationEventHandler附加到子 OK 按钮。
    在这里,我使用委托(delegate):AutomationEventHandler DialogBu​​ttonHandler 作为处理程序和实例 Field (AutomationElement msgBoxButton)对于 Button 元素,因为在 MessageBox 关闭时需要这些引用来删除 Button Click Handler。
  • 当单击 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; }
}

关于c# - 在另一个应用程序中的 MessageBox 中捕获按钮单击事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58184953/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com