2016-03-07 70 views
2

我的應用程序使用的系統有問題:有時候,當我們問這個系統的數據時,他彈出一個MessageBox來告訴我們一些事情:「I無法檢索您的數據,有太多的數據要搜索「。防止創建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文檔另一個線程說,這是understable創建的窗口。所以我的測試沒有成功。我該如何解決這個問題?

我可能犯了錯誤,我不太瞭解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

有這個代碼相當多的錯誤。您不會執行任何錯誤檢查,並且您不通過在靜態變量中保存引用來保護GC的委託。我認爲你可能會發佈一個WM_QUIT消息給對話框,而不是調用DestroyWindow。 –

+1

DestroyWindow不合適,您可以發送WM_CLOSE或使用UIAutomation,這是更好的選擇,因爲它也可用於檢測消息框的創建而無需掛鉤。 –

+0

謝謝你們兩位的解決方案,我會努力工作,有些回饋給你! – nkoniishvt

回答

2

感謝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 
} 

而且該解決方案使用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; 
}