当有图标时如何获取 MessageBox 的文本?
How to get the text of a MessageBox when it has an icon?
如果某个 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
returns 0 没有任何反应。
MessageBox.Show("Original message", "Caption", MessageBoxButtons.OK, MessageBoxIcon.Information);
我最好的猜测是 FindWindowEx
的第 3 个 and/or 第 4 个参数需要更改,但我不确定要传递什么。或者可能需要更改第二个参数以跳过图标?我不太确定。
看起来当 MessageBox 有一个图标时,FindWindowEx
returns 第一个 child 的文本(在这种情况下是图标)因此,长度为零。现在,在 this answer 的帮助下,我有了迭代 children 的想法,直到找到一个有文本的。这应该有效:
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);
}
显然,您可以调整它以适应您的特定情况(例如,不断迭代直到找到您要查找的实际文本),尽管我认为带有文本的 child 可能永远是第二个:
这是一个 UI 自动化方法,可以检测系统中任何位置的 Window 打开事件,使用其子元素的文本识别 Window 并关闭 Window 确认后。
检测是使用 Automation.AddAutomationEventHandler with WindowPattern.WindowOpenedEvent and Automation Element argument set to AutomationElement.RootElement 初始化的,它没有其他祖先,可以识别整个桌面(任何 Window)。
WindowWatcher
class 公开了一个 public 方法 (WatchWindowBySubElementText
) 允许指定一个文本中包含的文本刚刚打开的 Window 的子元素。如果找到指定的文本,该方法将关闭 Window 并使用自定义事件处理程序通知操作,订阅者可以使用该事件处理程序来确定已检测到并关闭所监视的 Window。
示例用法,使用问题中提供的文本字符串:
WindowWatcher watcher = new WindowWatcher();
watcher.ElementFound += (obj, evt) => { MessageBox.Show("Found and Closed!"); };
watcher.WatchWindowBySubElementText("Original message");
WindowWatcher
class:
此 class 需要对这些程序集的项目引用:
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.
编辑:
更改了不考虑在当前进程中创建的 Window 的条件:
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);
}
}
如果某个 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
returns 0 没有任何反应。
MessageBox.Show("Original message", "Caption", MessageBoxButtons.OK, MessageBoxIcon.Information);
我最好的猜测是 FindWindowEx
的第 3 个 and/or 第 4 个参数需要更改,但我不确定要传递什么。或者可能需要更改第二个参数以跳过图标?我不太确定。
看起来当 MessageBox 有一个图标时,FindWindowEx
returns 第一个 child 的文本(在这种情况下是图标)因此,长度为零。现在,在 this answer 的帮助下,我有了迭代 children 的想法,直到找到一个有文本的。这应该有效:
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);
}
显然,您可以调整它以适应您的特定情况(例如,不断迭代直到找到您要查找的实际文本),尽管我认为带有文本的 child 可能永远是第二个:
这是一个 UI 自动化方法,可以检测系统中任何位置的 Window 打开事件,使用其子元素的文本识别 Window 并关闭 Window 确认后。
检测是使用 Automation.AddAutomationEventHandler with WindowPattern.WindowOpenedEvent and Automation Element argument set to AutomationElement.RootElement 初始化的,它没有其他祖先,可以识别整个桌面(任何 Window)。
WindowWatcher
class 公开了一个 public 方法 (WatchWindowBySubElementText
) 允许指定一个文本中包含的文本刚刚打开的 Window 的子元素。如果找到指定的文本,该方法将关闭 Window 并使用自定义事件处理程序通知操作,订阅者可以使用该事件处理程序来确定已检测到并关闭所监视的 Window。
示例用法,使用问题中提供的文本字符串:
WindowWatcher watcher = new WindowWatcher();
watcher.ElementFound += (obj, evt) => { MessageBox.Show("Found and Closed!"); };
watcher.WatchWindowBySubElementText("Original message");
WindowWatcher
class:
此 class 需要对这些程序集的项目引用:
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.
编辑:
更改了不考虑在当前进程中创建的 Window 的条件:
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);
}
}