Предотвратить процесс от создания функции MessageBox
У меня есть проблема с системой, которую мы используем с нашим приложением: иногда, когда мы просим эту систему о данных, он открывает MessageBox, чтобы сказать нам что-то вроде: "Я не могу получить ваши данные, слишком много данных для поиска".
Проблема с этим заключается в том, что пользователь может быть не в состоянии увидеть или закрыть всплывающее окно и, таким образом, это блокирует все приложение (объясняя, почему пользователь не может закрыть/увидеть всплывающее окно займет слишком много времени и будет не по теме, Что отстой, но мы должны с этим разобраться).
Итак, в качестве временного решения я хотел бы предотвратить этот конкретный процесс от создания MessageBox.
Я искал решение в интернете и нашел около CBTProc, который, по-видимому, обеспечивает способ реагировать на конкретное событие Windows (запрос от процесса для создания окна) и предписывает ОС блокировать запрос.
Разве это путь?
Я протестировал 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 не может быть использован для уничтожения окна, созданного другим потоком, как сказано в документации msdn, что является недостаточным. Так что мои испытания не увенчались успехом. Как я могу решить эту проблему?
Возможно, я ошибся, я не очень хорошо знаю Windows api и только что услышал о CBTProc.
Обновить
Я изменил код, следуя советам @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
}
1 ответ:
Спасибо DavidHefferman и AlexK. вот решения, которые делают то, что я хотел.
Использование 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 }И решение с использованием Маутомации:
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; }
Comments