[英]Prevent a process from creating a MessageBox
I have a problem with a system we're using with our application: Sometimes, when we ask this system for data he pops a MessageBox up to tell us something along the lines of: "I can't retrieve your data, there is way too much data to search for".我的应用程序使用的系统有问题:有时,当我们向该系统询问数据时,他会弹出一个 MessageBox 来告诉我们一些类似的内容:“我无法检索您的数据,有办法要搜索的数据太多”。
The problem with this is that the user may be unable to see or close the popup and thus this blocks the whole application (Explaining why the user cannot close/see the popup would take too much time and be off topic, that sucks but we've to deal with it).这样做的问题是用户可能无法看到或关闭弹出窗口,因此这会阻止整个应用程序(解释为什么用户无法关闭/看到弹出窗口会花费太多时间并且偏离主题,这很糟糕,但我们)必须处理它)。
So, as a temporary solution I would like to prevent this specific process from creating the MessageBox.因此,作为临时解决方案,我想阻止此特定进程创建 MessageBox。
I've looked for a solution online and found about CBTProc which seems to provide a way to react to a particular Windows event (request from a process to create a window) and instruct the OS to block the request.我在网上寻找了一个解决方案,发现CBTProc似乎提供了一种对特定 Windows 事件(来自进程的创建窗口的请求)做出反应并指示操作系统阻止请求的方法。
Is this the way to go?这是要走的路吗?
I tested SetWinEventHook to detect process that request creation of a window and DestroyWindow to destroy the Window:我测试了SetWinEventHook来检测请求创建窗口和DestroyWindow来销毁窗口的进程:
public class PopupWatchdog {
#region constructor
public PopupWatchdog() {
SetWinEventHook(
EVENT_OBJECT_CREATED,
EVENT_OBJECT_CREATED,
IntPtr.Zero,
HookCallback,
0, //id process
0, //id thread
WINEVENT_OUTOFCONTEXT
);
}
#endregion
#region functions
private static void HookCallback(IntPtr hWinEventHook, int iEvent, IntPtr hWnd, int idObject, int idChild, int dwEventThread, int dwmsEventTime) {
Console.WriteLine("window {0} requests creating an object, trying to destroy it...", idChild);
DestroyWindow(hWnd);
}
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetWinEventHook(int eventMin, int eventMax, IntPtr hmodWinEventProc, HookProc lpfnWinEventProc, int idProcess, int idThread, int dwflags);
[DllImport("user32.dll")]
private static extern bool DestroyWindow(IntPtr hWnd);
#endregion
#region events
#endregion
#region variables
#region const
private const int EVENT_OBJECT_CREATED = 0x8000;
private const int WINEVENT_OUTOFCONTEXT = 0;
#endregion
private delegate void HookProc(
IntPtr hWinEventHook,
int iEvent,
IntPtr hWnd,
int idObject,
int idChild,
int dwEventThread,
int dwmsEventTime
);
#endregion
}
DestroyWindow cannot be used to destroy Window created by another Thread like msdn documentation says, which is understable. DestroyWindow 不能用于销毁由另一个线程创建的窗口,如 msdn 文档所说,这是不稳定的。 So my test were not successful.
所以我的测试没有成功。 How can I work this out?
我怎样才能解决这个问题?
I may have made mistakes, I don't know Windows api well and just heard about CBTProc.我可能犯了错误,我不太了解 Windows api,只是听说过 CBTProc。
Update更新
I changed the code following @DavidHeffernan and @AlexK advices, and it works:我按照@DavidHeffernan 和@AlexK 的建议更改了代码,它有效:
public class BlockingPopupWatchdog {
#region ctor
public BlockingPopupWatchdog(int processId) {
_processId = processId;
}
#endregion
#region functions
internal bool Hook() {
if (_hookId != IntPtr.Zero) {
Unhook();
}
_hookId = SetWinEventHook(
EVENT_OBJECT_CREATED,
EVENT_OBJECT_CREATED,
IntPtr.Zero,
_hook,
_processId, //id process
0, //id thread
WINEVENT_OUTOFCONTEXT
);
if (_hookId == IntPtr.Zero) {
Logger.Log(String.Format("Error {0} while hooking", Marshal.GetLastWin32Error()), EventTypes.WARNING);
return false;
}
return true;
}
internal bool Unhook() {
if (_hookId == IntPtr.Zero) return false;
if (!UnhookWinEvent(_hookId)) {
Logger.Log(String.Format("Error {0} while unhooking", Marshal.GetLastWin32Error()), EventTypes.WARNING);
return false;
}
return true;
}
private static void HookCallback(IntPtr hWinEventHook, int iEvent, IntPtr hWnd, int idObject, int idChild, int dwEventThread, int dwmsEventTime) {
if (hWnd == IntPtr.Zero) return;
try {
AutomationElement elem = AutomationElement.FromHandle(hWnd);
if (elem == null || !elem.Current.ClassName.Equals(MESSAGEBOX_CLASS_NAME)) {
return;
}
object pattern;
if (!elem.TryGetCurrentPattern(WindowPattern.Pattern, out pattern)) return;
WindowPattern window = (WindowPattern)pattern;
if (window.Current.WindowInteractionState == WindowInteractionState.ReadyForUserInteraction) {
window.Close();
}
} catch (Exception e) {
Console.WriteLine(e);
}
}
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetWinEventHook(int eventMin, int eventMax, IntPtr hmodWinEventProc, HookProc lpfnWinEventProc, int idProcess, int idThread, int dwflags);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool UnhookWinEvent(IntPtr hWinEventHook);
#endregion
#region variables
#region const
private const String MESSAGEBOX_CLASS_NAME = "#32770";
private const int EVENT_OBJECT_CREATED = 0x8000;
private const int WINEVENT_OUTOFCONTEXT = 0;
#endregion
private delegate void HookProc(
IntPtr hWinEventHook,
int iEvent,
IntPtr hWnd,
int idObject,
int idChild,
int dwEventThread,
int dwmsEventTime
);
private static readonly HookProc _hook = HookCallback;
private IntPtr _hookId;
private readonly int _processId;
#endregion
}
Thanks to DavidHefferman and AlexK.感谢 DavidHefferman 和 AlexK。 here is the solutions that makes what I wanted.
这是使我想要的解决方案。
Using WinApi:使用 WinApi:
public class BlockingPopupWatchdog {
#region ctor
public BlockingPopupWatchdog(int processId) {
_processId = processId;
}
#endregion
#region functions
internal bool Hook() {
if (_hookId != IntPtr.Zero) {
Unhook();
}
_hookId = SetWinEventHook(
EVENT_OBJECT_CREATED,
EVENT_OBJECT_CREATED,
IntPtr.Zero,
_hook,
_processId, //id process
0, //id thread
WINEVENT_OUTOFCONTEXT
);
if (_hookId == IntPtr.Zero) {
Logger.Log(String.Format("Error {0} while hooking", Marshal.GetLastWin32Error()), EventTypes.WARNING);
return false;
}
return true;
}
internal bool Unhook() {
if (_hookId == IntPtr.Zero) return false;
if (!UnhookWinEvent(_hookId)) {
Logger.Log(String.Format("Error {0} while unhooking", Marshal.GetLastWin32Error()), EventTypes.WARNING);
return false;
}
return true;
}
private static void HookCallback(IntPtr hWinEventHook, int iEvent, IntPtr hWnd, int idObject, int idChild, int dwEventThread, int dwmsEventTime) {
if (hWnd == IntPtr.Zero) return;
try {
AutomationElement elem = AutomationElement.FromHandle(hWnd);
if (elem == null || !elem.Current.ClassName.Equals(MESSAGEBOX_CLASS_NAME)) {
return;
}
object pattern;
if (!elem.TryGetCurrentPattern(WindowPattern.Pattern, out pattern)) return;
WindowPattern window = (WindowPattern)pattern;
if (window.Current.WindowInteractionState == WindowInteractionState.ReadyForUserInteraction) {
window.Close();
}
} catch (Exception e) {
Console.WriteLine(e);
}
}
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetWinEventHook(int eventMin, int eventMax, IntPtr hmodWinEventProc, HookProc lpfnWinEventProc, int idProcess, int idThread, int dwflags);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool UnhookWinEvent(IntPtr hWinEventHook);
#endregion
#region variables
#region const
private const String MESSAGEBOX_CLASS_NAME = "#32770";
private const int EVENT_OBJECT_CREATED = 0x8000;
private const int WINEVENT_OUTOFCONTEXT = 0;
#endregion
private delegate void HookProc(
IntPtr hWinEventHook,
int iEvent,
IntPtr hWnd,
int idObject,
int idChild,
int dwEventThread,
int dwmsEventTime
);
private static readonly HookProc _hook = HookCallback;
private IntPtr _hookId;
private readonly int _processId;
#endregion
}
And the solution using UIAutomation:以及使用 UIAutomation 的解决方案:
private AutomationElement _watchedElement;
private void PopupOpenedHandler(Object sender, AutomationEventArgs args) {
var element = sender as AutomationElement;
if (element == null || !element.Current.ClassName.Equals(MESSAGEBOX_CLASS_NAME)) {
return;
}
object pattern;
if (!element.TryGetCurrentPattern(WindowPattern.Pattern, out pattern)) return;
WindowPattern window = (WindowPattern)pattern;
if (window.Current.WindowInteractionState == WindowInteractionState.ReadyForUserInteraction) {
window.Close();
}
}
internal bool Hook() {
Process p = Process.GetProcessById(_processId);
IntPtr wHnd = p.MainWindowHandle;
if (wHnd != IntPtr.Zero) {
_watchedElement = AutomationElement.FromHandle(wHnd);
Automation.AddAutomationEventHandler (
WindowPattern.WindowOpenedEvent,
_watchedElement,
TreeScope.Descendants,
PopupOpenedHandler
);
return true;
}
return false;
}
internal bool Unhook() {
if (_watchedElement == null) return false;
Automation.RemoveAutomationEventHandler(WindowPattern.WindowOpenedEvent, _watchedElement, PopupOpenedHandler);
return true;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.