带有图标的MessageBox文本如何获取?

时间:2019-03-06 02:11:25

标签: c# user32 findwindowex

我正在尝试关闭特定的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的第三和/或第四参数需要更改,但是我不确定该通过什么。还是可能需要更改第二个参数才能跳过图标?我不太确定。

2 个答案:

答案 0 :(得分:3)

当MessageBox带有图标时,FindWindowEx会返回第一个子项的文本(在这种情况下为图标),因此返回零长度。现在,在this answer的帮助下,我有了一个要遍历孩子的想法,直到找到一个带有文字的孩子。这应该起作用:

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);
}

很明显,您可以对其进行调整以适合您的特定情况(例如,不断进行迭代,直到找到您要查找的实际文本为止),尽管我认为带有文本的孩子可能永远是第二个孩子:

Messagebox in Spy++

答案 1 :(得分:2)

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

使用Automation.AddAutomationEventHandlerWindowPattern.WindowOpenedEvent设置为AutomationElement.RootElement,并将Automation Element参数设置为Ahmed Abdelhameed noted in the comments来初始化检测,该参数在没有其他祖先的情况下标识整个Desktop(任何窗口)。

WindowWatcher 类公开了一个公共方法(WatchWindowBySubElementText),该方法可以指定包含在刚打开的Window的子元素之一中的Text。如果找到了指定的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)  

enter image description here一样,它施加了一个可能不必要的限制:该Dialod也可能属于当前Process。我只留下了null支票。

using System;
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;
                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();
                    this.OnElementFound(new EventArgs());
                }
            });
    public void OnElementFound(EventArgs e)
    {
        Automation.RemoveAllEventHandlers();
        ElementFound?.Invoke(this, e);
    }
}