How to get the text of a MessageBox when it has an icon?

安稳与你 提交于 2019-11-28 13:50:56

It appears that when the MessageBox has an icon, FindWindowEx returns the text of the first child (which is the icon in this case) hence, the zero length. Now, with the help of this answer, I got the idea to iterate the children until finding one with a text. This should work:

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

Obviously, you could adjust it to fit your particular situation (e.g., keep iterating until you find the actual text you're looking for) although I think the child with the text will probably always be the second one:

This is a UI Automation method that can detect a Window Opened event anywhere in the System, identify the Window using the Text of one its child elements and close the Window upon positive identification.

The detection is initialized using Automation.AddAutomationEventHandler with WindowPattern.WindowOpenedEvent and Automation Element argument set to AutomationElement.RootElement, which, having no other ancestors, identifies the whole Desktop (any Window).

The WindowWatcher class exposes a public method (WatchWindowBySubElementText) that allows to specify the Text contained in one of the sub elements of a Window that just opened. If the specified Text is found, the method closes the Window and notifies the operation using a custom event handler that a subscriber can use to determine that the watched Window has been detected and closed.

Sample usage, using the Text string as provided in the question:

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

WindowWatcher class:

This class requires a Project Reference to these assemblies:
UIAutomationClient
UIAutomationTypes

Note that, upon identification, the class event removes the Automation event handler before notifying the subscribers. This is just an example: it points out that the handlers need to be removed at some point. The class could implement IDisposable and remove the handler(s) when disposed of.

EDIT:
Changed the condition that doesn't consider a Window created in the current Process:

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

As Ahmed Abdelhameed noted in the comments, it imposes a limitation that is probably not necessary: the Dialod could also belong to the current Process. I left there just the null check.

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);
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!