2014-09-04 26 views
1

我有一個C#程序,用於輪詢EnumDesktopWindows集合的更改。如果用戶關閉或打開窗口,則輪詢例程會檢測到該窗口,並將可用窗口的更新列表發送給另一個.Net窗體窗體項目。但是,我不喜歡輪詢方法。我更喜歡EnumDesktopWindows的任何更改都會觸發一個事件,以便對異常做出響應。在桌面窗口集合更改時鎖定異步委派?

我能拿出最好的是你在下面看到。我嘗試了Scott C.的建議,從控制檯窗口執行,但沒有奏效。

當您在Windows窗體加載時(這是一個Windows窗體應用程序),您下面看到的內容捕獲CreateWnd = 3。但是它不會全局捕獲:它只捕獲當前正在運行的可執行文件中的窗口事件。如果有人有老鷹的眼睛,並可以發現如何使這個代碼捕獲全球我會獎勵答案。

嘗試一下;首先創建一個Windows窗體應用程序項目,將下面的代碼添加到Form1.cs中(你將需要一個列表框添加到名爲lstLog形式正確編譯)

using System; 
using System.Windows.Forms; 

namespace Utilities 
{ 
    public partial class Form1 : Form 
    { 
     public Form1() 
     { 
      InitializeComponent(); 
     } 

     private void Form1_Load(object sender, EventArgs e) 
     { 
      var gwh = new GlobalWindowHook(); 
      gwh.WindowCreated += onWindowCreated; 
     } 

     private void onWindowCreated() 
     { 
      lstLog.Items.Add("window creation event detected."); 
     } 
    } 
} 

名爲GlobalWindowHook同一項目中創建一個類文件.cs並複製粘貼如下:

using System; 
using System.Runtime.InteropServices; 

namespace Utilities 
{ 
    internal class GlobalWindowHook 
    { 
     private delegate IntPtr HookProc(int code, IntPtr wParam, IntPtr lParam); 

     [DllImport("user32.dll")] 
     private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); 

     public enum HookType 
     { 
      WH_JOURNALRECORD = 0, 
      WH_JOURNALPLAYBACK = 1, 
      WH_KEYBOARD = 2, 
      WH_GETMESSAGE = 3, 
      WH_CALLWNDPROC = 4, 
      WH_CBT = 5, 
      WH_SYSMSGFILTER = 6, 
      WH_MOUSE = 7, 
      WH_HARDWARE = 8, 
      WH_DEBUG = 9, 
      WH_SHELL = 10, 
      WH_FOREGROUNDIDLE = 11, 
      WH_CALLWNDPROCRET = 12, 
      WH_KEYBOARD_LL = 13, 
      WH_MOUSE_LL = 14 
     } 

     public enum HCBT 
     { 
      MoveSize = 0, 
      MinMax = 1, 
      QueueSync = 2, 
      CreateWnd = 3, 
      DestroyWnd = 4, 
      Activate = 5, 
      ClickSkipped = 6, 
      KeySkipped = 7, 
      SysCommand = 8, 
      SetFocus = 9 
     } 

     private IntPtr hhook = IntPtr.Zero; 

     public GlobalWindowHook() 
     { 
      hook(); 
     } 


     ~GlobalWindowHook() 
     { 
      unhook(); 
     } 


     public void hook() 
     { 
      IntPtr hInstance = LoadLibrary("User32"); 

      hhook = SetWindowsHookEx(HookType.WH_CBT, hookProc, hInstance, 0); 
     } 

     public void unhook() 
     { 
      UnhookWindowsHookEx(hhook); 
     } 

     public IntPtr hookProc(int code, IntPtr wParam, IntPtr lParam) 
     { 
      if (code != (int) HCBT.CreateWnd && code != (int) HCBT.DestroyWnd) 
       return CallNextHookEx(IntPtr.Zero, code, wParam, lParam); 

      //Do whatever with the created or destroyed window. 

      return CallNextHookEx(IntPtr.Zero, code, wParam, lParam); 
     } 


     [DllImport("user32.dll")] 
     private static extern IntPtr SetWindowsHookEx(HookType code, HookProc func, IntPtr hInstance, int threadId); 

     [DllImport("user32.dll")] 
     private static extern bool UnhookWindowsHookEx(IntPtr hInstance); 

     [DllImport("kernel32", SetLastError = true, CharSet = CharSet.Auto)] 
     private static extern IntPtr LoadLibrary(string fileName); 
    } 
} 

執行上述步驟後,執行windows窗體項目。您應該看到它檢測到正在創建的一個窗口,即您剛剛執行的窗口。

+0

定義的類型,我認爲你可以用做'和SetWindowsHookEx()'用'WH_CBT'鉤型。 – cdhowie 2014-09-04 19:07:02

+0

http://msdn.microsoft.com/en-us/library/windows/desktop/dd373640%28v=vs.85%29.aspx – 2014-09-04 19:08:44

+0

我剛剛修改我的問題,用我迄今爲止的最佳嘗試更新它(謝謝你斯科特)。我仍在尋求答案。 – sapbucket 2014-09-04 23:57:39

回答

0

如果您想要推送通知而不是拉他們,這不是太困難。您需要做的是P/Invoke SetWindowsHookEx並註冊WH_CBT事件。一旦你收到這些消息,你就可以用HCBT_CREATEWND or HCBT_DESTROYWND代碼來收聽消息。當新窗口被創建或被銷燬時,它會通知你。

使用在pinvoke.net

class HookingClass 
{ 
    private delegate IntPtr CBTProc(HCBT nCode, IntPtr wParam, IntPtr lParam); 

    private readonly CBTProc _callbackDelegate; 

    public HookingClass() 
    { 
     _callbackDelegate = CallbackFunction; 
    } 

    private IntPtr _hook; 

    private void CreateHook() 
    { 
     using (Process process = Process.GetCurrentProcess()) 
     using (ProcessModule module = process.MainModule) 
     { 
      IntPtr hModule = GetModuleHandle(module.ModuleName); 

      _hook = SetWindowsHookEx(HookType.WH_CBT, _callbackDelegate, hModule, 0); 
      if(_hook == IntPtr.Zero) 
       throw new Win32Exception(); //The default constructor automatically calls Marshal.GetLastError() 
     } 

    } 

    private void Unhook() 
    { 
     var success = UnhookWindowsHookEx(_hook); 
     if(!success) 
      throw new Win32Exception(); 
    } 

    private IntPtr CallbackFunction(HCBT code, IntPtr wParam, IntPtr lParam) 
    { 
     if (code != HCBT.CreateWnd && code != HCBT.DestroyWnd) 
     {   
      return CallNextHookEx(IntPtr.Zero, code, wParam, lParam); 
     } 

     //Do whatever with the created or destroyed window. 

     return CallNextHookEx(IntPtr.Zero, (int)code, wParam, lParam); 
    } 
} 
+0

@sapbucket你的問題是'hModule'需要指向包含'_callbackDelegate'函數的程序集。 'user32.dll'不包含你的函數,這就是爲什麼它不起作用。嘗試從測試控制檯應用程序執行此操作,然後再嘗試從單元測試中運行它。此外,您的單元測試程序將在事件發生之前退出並關閉。 – 2014-09-04 20:54:36

+0

漢斯帕薩特解釋說,hModule沒有在低級庫中使用,並傳入任何有效的應該做的伎倆。這就是爲什麼我堅持使用LoadLibrary。 Hans是不正確的?無論哪種方式,我都沒有成功。 – sapbucket 2014-09-04 23:59:26

+1

這隻適用於低級掛鉤。沒有關於CBT鉤子的低級別,模塊句柄非常重要。 – 2014-09-05 00:17:15