2012-10-14 30 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

我的钩子是线程钩子,不是低级钩子。另外,钩子程序几乎不会做任何事情,只是写一行到控制台。它怎么会超时? –

相关问题