Предотвратить процесс от создания функции 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
}
535   1  

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

    Ничего не найдено.