6

我正在尝试关闭一个特定的MessageBox,如果它基于标题和文本显示。MessageBox当没有图标时,我可以使用它。

IntPtr handle = FindWindowByCaption(IntPtr.Zero, "Caption");
if (handle == IntPtr.Zero)
    return;

//Get the Text window handle
IntPtr txtHandle = FindWindowEx(handle, IntPtr.Zero, "Static", null);
int len = GetWindowTextLength(txtHandle);

//Get the text
StringBuilder sb = new StringBuilder(len + 1);
GetWindowText(txtHandle, sb, len + 1);

//close the messagebox
if (sb.ToString() == "Original message")
{
    SendMessage(new HandleRef(null, handle), WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
}

MessageBox显示没有如下图标时,上面的代码工作得很好。

MessageBox.Show("Original message", "Caption");

但是,如果它包含如下图标(来自MessageBoxIcon),则它不起作用;GetWindowTextLength返回 0 并且没有任何反应。

MessageBox.Show("Original message", "Caption", MessageBoxButtons.OK, MessageBoxIcon.Information);

我最好的猜测是FindWindowEx需要更改的第 3 和/或第 4 参数,但我不确定要通过什么。或者也许第二个参数需要更改以跳过图标?我不太确定。

4

2 回答 2

4

似乎当 MessageBox 有一个图标时,FindWindowEx返回第一个孩子的文本(在这种情况下是图标),因此,长度为零。现在,在这个答案的帮助下,我有了迭代孩子的想法,直到找到一个带有文本的孩子。这应该有效:

IntPtr handle = FindWindowByCaption(IntPtr.Zero, "Caption");

if (handle == IntPtr.Zero)
    return;

//Get the Text window handle
IntPtr txtHandle = IntPtr.Zero;
int len;
do
{
    txtHandle = FindWindowEx(handle, txtHandle, "Static", null);
    len = GetWindowTextLength(txtHandle);
} while (len == 0 && txtHandle != IntPtr.Zero);

//Get the text
StringBuilder sb = new StringBuilder(len + 1);
GetWindowText(txtHandle, sb, len + 1);

//close the messagebox
if (sb.ToString() == "Original message")
{
    SendMessage(new HandleRef(null, handle), WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
}

显然,您可以调整它以适应您的特定情况(例如,不断迭代,直到找到您正在寻找的实际文本),尽管我认为带有文本的孩子可能总是第二个:

Spy++ 中的消息框

于 2019-03-06T03:56:31.680 回答
3

这是一种 UI 自动化方法,可以检测系统中任何位置的 Window Opened 事件,使用其子元素的 Text 识别 Window,并在确定后关闭 Window。

使用带有WindowPattern.WindowOpenedEvent和 Automation Element 参数设置为AutomationElement.RootElement的Automation.AddAutomationEventHandler初始化检测,它没有其他祖先,标识整个桌面(任何窗口)。

该类WindowWatcher公开了一个公共方法 ( WatchWindowBySubElementText),该方法允许指定刚刚打开的窗口的子元素之一中包含的文本。如果找到指定的 Text,则该方法关闭 Window 并使用自定义事件处理程序通知操作,订阅者可以使用该事件处理程序来确定已检测到并关闭了监视的 Window。

示例用法,使用问题中提供的文本字符串:

WindowWatcher watcher = new WindowWatcher();
watcher.ElementFound += (obj, evt) => { MessageBox.Show("Found and Closed!"); };
watcher.WatchWindowBySubElementText("Original message");

WindowWatcher班级:

此类需要对这些程序集的项目引用:
UIAutomationClient
UIAutomationTypes

请注意,在识别后,类事件会在通知订阅者之前删除自动化事件处理程序。这只是一个例子:它指出处理程序需要在某些时候被删除。处理时,该类可以实现IDisposable和删除处理程序。

编辑
更改了不考虑在当前进程中创建的窗口的条件:

if (element is null || element.Current.ProcessId != Process.GetCurrentProcess().Id)  

评论中所述,它施加了一个可能不必要的限制:对话框也可能属于当前进程。我把null支票留在那里。

using System.Diagnostics;
using System.Windows.Automation;

public class WindowWatcher
{
    public delegate void ElementFoundEventHandler(object sender, EventArgs e);
    public event ElementFoundEventHandler ElementFound;

    public WindowWatcher() { }
    public void WatchWindowBySubElementText(string ElementText) => 
        Automation.AddAutomationEventHandler(WindowPattern.WindowOpenedEvent, 
            AutomationElement.RootElement, TreeScope.Subtree, (UIElm, evt) => {
                AutomationElement element = UIElm as AutomationElement;
                try {
                    if (element is null) return;

                    AutomationElement childElm = element.FindFirst(TreeScope.Children,
                        new PropertyCondition(AutomationElement.NameProperty, ElementText));
                    if (childElm != null) {
                        (element.GetCurrentPattern(WindowPattern.Pattern) as WindowPattern).Close();
                        OnElementFound(new EventArgs());
                    }
                }
                catch (ElementNotAvailableException) {
                    // Ignore: generated when a Window is closed. Its AutomationElement   
                    // is no longer available. Usually a modal dialog in the current process. 
                }
            });
    public void OnElementFound(EventArgs e)
    {
        // Automation.RemoveAllEventHandlers(); <= If single use. Add to IDisposable.Dispose()
        ElementFound?.Invoke(this, e);
    }
}
于 2019-03-06T17:13:32.267 回答