2012-10-14 32 views
4

我安裝了一個線程專用的windows鉤子來監視發送給WndProc的消息。它起初工作。但是,在我按Tab鍵大約19次以將焦點移到窗體上之後,我的鉤子回調函數不再被稱爲。無論我是快速還是緩慢地按下Tab,都會發生這種情況。任何人都可以解釋究竟發生了什麼?多次按Tab鍵後不再調用線程掛接程序。爲什麼?

下面是我寫的代碼。我在Windows 7 64位上測試了它。

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Windows.Forms; 
using System.Runtime.InteropServices; 

namespace HookTest 
{ 
    static class Program 
    { 
     private const int WH_CALLWNDPROC = 4; 

     private delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam); 

     private class MainForm : Form 
     { 
      private Button button1; 
      private TextBox textBox1; 

      public MainForm() 
      { 
       this.button1 = new System.Windows.Forms.Button(); 
       this.textBox1 = new System.Windows.Forms.TextBox(); 
       this.SuspendLayout(); 
       // 
       // button1 
       // 
       this.button1.Location = new System.Drawing.Point(12, 38); 
       this.button1.Name = "button1"; 
       this.button1.Size = new System.Drawing.Size(75, 23); 
       this.button1.TabIndex = 0; 
       this.button1.Text = "Button 1"; 
       this.button1.UseVisualStyleBackColor = true; 
       // 
       // textBox1 
       // 
       this.textBox1.Location = new System.Drawing.Point(12, 12); 
       this.textBox1.Name = "textBox1"; 
       this.textBox1.Size = new System.Drawing.Size(100, 20); 
       this.textBox1.TabIndex = 1; 
       // 
       // MainForm 
       // 
       this.Controls.Add(this.textBox1); 
       this.Controls.Add(this.button1); 
       this.Name = "MainForm"; 
       this.Text = "Main Form"; 
       this.ResumeLayout(false); 
       this.PerformLayout(); 
      } 
     } 

     private static IntPtr hWndProcHook = IntPtr.Zero; 
     private static int messageCount = 0; 

     [DllImport("Kernel32.dll", CharSet = CharSet.Auto)] 
     public static extern IntPtr GetModuleHandle(string lpModuleName); 

     [DllImport("Kernel32.dll", CharSet = CharSet.Auto)] 
     public static extern uint GetCurrentThreadId(); 

     [DllImport("User32.dll", CharSet = CharSet.Auto)] 
     private static extern IntPtr SetWindowsHookEx(int idHook, 
      HookProc lpfn, IntPtr hMod, uint dwThreadId); 

     [DllImport("User32.dll", CharSet = CharSet.Auto)] 
     [return: MarshalAs(UnmanagedType.Bool)] 
     private static extern bool UnhookWindowsHookEx(IntPtr hhk); 

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

     /// <summary> 
     /// The main entry point for the application. 
     /// </summary> 
     [STAThread] 
     static void Main() 
     { 
      Application.EnableVisualStyles(); 
      Application.SetCompatibleTextRenderingDefault(false); 
      InstallHook(); 
      Application.Run(new MainForm()); 
      UninstallHook(); 
     } 

     private static void InstallHook() 
     { 
      if (Program.hWndProcHook == IntPtr.Zero) 
      { 
       Console.WriteLine("Hooking..."); 

       Program.hWndProcHook = SetWindowsHookEx(
        WH_CALLWNDPROC, 
        WndProcHookCallback, 
        GetModuleHandle(null), 
        GetCurrentThreadId()); 

       if(Program.hWndProcHook != IntPtr.Zero) 
        Console.WriteLine("Hooked successfully."); 
       else 
        Console.WriteLine("Failed to hook."); 
      } 
     } 

     private static void UninstallHook() 
     { 
      if (Program.hWndProcHook != IntPtr.Zero) 
      { 
       Console.WriteLine("Unhooking..."); 

       if (UnhookWindowsHookEx(Program.hWndProcHook)) 
        Console.WriteLine("Unhooked successfully."); 
       else 
        Console.WriteLine("Failed to unhook."); 

       Program.hWndProcHook = IntPtr.Zero; 
      } 
     } 

     private static IntPtr WndProcHookCallback(int nCode, IntPtr wParam, IntPtr lParam) 
     { 
      Console.WriteLine("WndProcHookCallback {0}", Program.messageCount++); 

      return CallNextHookEx(Program.hWndProcHook, nCode, wParam, lParam); 
     } 
    } 
} 
+0

您的代碼對我來說工作得很好,而且我按Tab超過19次。有沒有其他信息可以提供?你如何驗證它不起作用?我剛剛觀看了VS中的輸出窗格。 –

+0

我通過在調試模式下運行並查看輸出窗口來驗證它是否工作正常。如果鉤子正在工作,它應該將「WndProcHookCallback#」寫入輸出窗口,其中#是遞增的整數。如果沒有更多這樣的消息被寫入輸出窗口,掛鉤程序不再被調用。 –

+0

這個鉤子沒有做任何有用的事情。鉤子回調中的* real *代碼是什麼樣的? –

回答

3

在測試你的程序中,我得到了以下錯誤

CallbackOnCollectedDelegate檢測
消息:回調是對類型的沙盒垃圾回收委託「造 形式Sandbox_Form.Program + HOOKPROC! ::調用」。這可能會導致 應用程序崩潰,損壞和數據丟失。將代理 傳遞給非託管代碼時,它們必須由託管應用程序 保持活動狀態,直到確保它們永遠不會被調用。

我相信問題是隱式創建的代理被傳遞給SetWindowsHookEx,因爲回調會被垃圾收集。通過爲委託顯式創建一個變量並將其保留在範圍內,我認爲這會使問題消失,當我將InstallHook修改爲以下時,我無法再重新創建該錯誤。

private static HookProc hookProcDelegate; 

private static void InstallHook() 
{ 
    if (Program.hWndProcHook == IntPtr.Zero) 
    { 
     Console.WriteLine("Hooking..."); 

     hookProcDelegate = new HookProc(WndProcHookCallback); 

     Program.hWndProcHook = SetWindowsHookEx(
      WH_CALLWNDPROC, 
      hookProcDelegate, 
      GetModuleHandle(null), 
      GetCurrentThreadId()); 

     if (Program.hWndProcHook != IntPtr.Zero) 
      Console.WriteLine("Hooked successfully."); 
     else 
      Console.WriteLine("Failed to hook."); 
    } 
} 
+0

你的答案正確解決了這個問題。謝謝! –

0

What can cause Windows to unhook a low level (global) keyboard hook?涵蓋了這個。如果掛鉤程序超時,則會發生這種情況。超時值在HKEY_CURRENT_USER\Control Panel\Desktop鍵上指定,值爲LowLevelHooksTimeout(雖然此值在我的系統中不存在)。

MSDN(也有在本頁面底部的社區內容一些好的信息):

如果鉤子程序超時,系統將消息傳遞到下一個 鉤。但是,在Windows 7和更高版本中,掛鉤是在沒有被調用的情況下被移除的 。

Global hooks getting lost on windows

在Windows 7中,我們必須確保掛鉤的回調函數可以在不到LowLevelHooksTimeout,這是300毫秒返回。我們允許應用程序在處理掛鉤回調消息時超時10次。如果第11次超時,Windows將從鉤子鏈中解除應用程序。這是一個按設計功能,它是在Win7 RTM中添加的。

該頁面還建議使用Raw Input來代替。

編輯︰這裏是Code Project tutorial在C#中使用原始輸入。

+0

我的鉤子是線程鉤子,不是低級鉤子。另外,鉤子程序幾乎不會做任何事情,只是寫一行到控制檯。它怎麼會超時? –

相關問題