C# 强化系列文章二:在C#中使用钩子

C#强化系列文章二:在C#中使用钩子

相信以前用过VB、Delphi,特别是VC的程序员应该对钩子程序都不陌生。在C#中我们同样可以使用钩子程序来实现特殊效果,比如当用户按下某个特殊键时提示,比如关闭应用程序前提示等。
当然使用方法相对VC来说要稍微复杂一点,有的地方还不太方便,下面的例子中实现两个基本功能:
1、按下Alt+F4时使窗口最小化
2、关闭应用程序前提示

不过目前只能捕获消息,不能屏蔽消息,我正在实验,也希望知道的高手能多多指教

一、加入winuser.h中的定义
因为钩子程序一般情况下都是在vc下使用的,在c#里面并没有对应的方法、结构等的定义,我们首先需要把winuser.h中的相关定义加入自己的类


     public   enum  HookType :  int
    
{
        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
    }
具体的说明在msdn中都可以查到,主要的比如WH_KEYBOARD是监控按键事件,WH_CALLWNDPROC是在消息触发时执行


     public   enum  VirtualKeys
    
{
        VK_SHIFT 
=   0x10 ,
        VK_CONTROL 
=   0x11 ,
        VK_MENU 
=   0x12 ,     // ALT
        VK_PAUSE  =   0x13 ,
        VK_CAPITAL 
=   0x14
    }
这个不用说明了,对应ALT、CTRL等键


     public   struct  CWPSTRUCT
    
{
        
public  IntPtr lparam;
        
public  IntPtr wparam;
        
public   int  message;
        
public  IntPtr hwnd;
    }
这个是windows内部传递过来的消息的结构

二、加入自己定义的委托和事件参数

     public   delegate   int  HookProc( int  code, IntPtr wParam, IntPtr lParam);
    
public   delegate   void  HookEventHandler( object  sender, HookEventArgs e);
HokkProc是 SetWindowsHookEx 调用时的委托事件,HookEventHandler是自己的委托事件

     public   class  HookEventArgs : EventArgs
    
{
        
public   int  HookCode;
        
public  IntPtr wParam;
        
public  IntPtr lParam;
        
public  Keys key;
        
public   bool  bAltKey;
        
public   bool  bCtrlKey;
    }
是自己的委托事件中接受的事件参数

三、实现自己的钩子类
这一步是最重要的,要使用钩子,我们需要引用 user32.dll 中的相应方法:
        [DllImport( " user32.dll " )]
        
static   extern  IntPtr SetWindowsHookEx(HookType hook, HookProc callback, IntPtr hMod,  uint  dwThreadId);

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

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

        [DllImport(
" user32.dll " )]
        
static   extern   short  GetKeyState(VirtualKeys nVirtKey);
SetWindowsHookEx 是注册一个钩子程序, UnhookWindowsHookEx 是释放钩子程序, CallNextHookEx 调用钩子的后续事件处理, GetKeyState 得到所按的虚键

然后就可以调用这些方法来实现钩子程序,比如注册一个钩子可以调用:
            m_hook  =  SetWindowsHookEx(m_hooktype, m_hookproc, IntPtr.Zero, ( uint )AppDomain.GetCurrentThreadId());
其中m_hooktype就是 HookType 中定义的类型,m_hookproc就是实际的钩子处理程序:
m_hookproc  =   new  HookProc(KeyHookProcedure);

最关键的就是 KeyHookProcedure 等钩子处理程序:
         protected   int  KeyHookProcedure( int  code, IntPtr wParam, IntPtr lParam)
        
{
            
if  (code  !=   0 )
            
{
                
return  CallNextHookEx(m_hook, code, wParam, lParam);
            }


            
if  (HookInvoked  !=   null )
            
{
                Keys key 
=  (Keys)wParam.ToInt32();
                HookEventArgs eventArgs 
=   new  HookEventArgs();
                eventArgs.key 
=  key;
                eventArgs.lParam 
=  lParam;
                eventArgs.wParam 
=  wParam;
                eventArgs.HookCode 
=  code;
                eventArgs.bAltKey 
=  GetKeyState(VirtualKeys.VK_MENU)  <=   - 127 ;
                eventArgs.bCtrlKey 
=  GetKeyState(VirtualKeys.VK_CONTROL)  <=   - 127 ;
                HookInvoked(this, eventArgs);
            }


            
return  CallNextHookEx(m_hook, code, wParam, lParam);
        }
在这个事件中可以取得消息的参数,特别是按键的值,然后通过HookInvoked委托调用事件实际的处理程序

四、在应用程序中调用钩子类
我们可以在自己的form中声明两个钩子对象
         private  MyHook callProcHook  =   new  MyHook(HookType.WH_CALLWNDPROC);
        
private  MyHook keyHook  =   new  MyHook(HookType.WH_KEYBOARD);
然后在初始化时注册钩子:
         private   void  Form1_Load( object  sender, EventArgs e)
        
{
            keyHook.HookInvoked 
+=   new  HookEventHandler(keyHook_HookInvoked );
            keyHook.Install();

            callProcHook.HookInvoked 
+=   new  HookEventHandler(callProcHook_HookInvoked );
            callProcHook.Install();
        }

然后就是实际的钩子事件:
         private   void  keyHook_HookInvoked( object  sender, HookEventArgs e)
        
{
            
if  (e.key  ==  Keys.F4  &&  e.bAltKey)  // Alt + F4
             {
                
this .WindowState  =  FormWindowState.Minimized;
            }

        }


        
private   void  callProcHook_HookInvoked( object  sender, HookEventArgs e)
        
{
            
unsafe
            
{
                CWPSTRUCT
*  message  =  (CWPSTRUCT * )e.lParam;
                
if  (message  !=   null )
                
{
                    
if  (message -> message  ==  WM_CLOSE)
                    
{
                        (sender 
as  MyHook).CallNextProc  =   false ;
                        MessageBox.Show(
" 程序即将关闭! " );
                    }

                }

            }

        }

这样我们就可以通过钩子实现一些相对底层的应用。

代码说的有点乱,我就把最主要的代码直接列在下面供大家参考:


  1 using  System;
  2 using  System.Collections.Generic;
  3 using  System.ComponentModel;
  4 using  System.Data;
  5 using  System.Drawing;
  6 using  System.Text;
  7 using  System.Windows.Forms;
  8 using  System.Runtime.InteropServices;
  9
 10 namespace  HookTest
 11 {
 12      public   partial   class  Form1 : Form
 13      {
 14          消息定义(WinUser.h中定义)
 20
 21          private  MyHook callProcHook  =   new  MyHook(HookType.WH_CALLWNDPROC);
 22          private  MyHook keyHook  =   new  MyHook(HookType.WH_KEYBOARD);
 23
 24          public  Form1()
 25          {
 26             InitializeComponent();
 27         }

 28
 29          private   void  Form1_Load( object  sender, EventArgs e)
 30          {
 31             keyHook.HookInvoked  +=   new  HookEventHandler(keyHook_HookInvoked);
 32             keyHook.Install();
 33
 34             callProcHook.HookInvoked  +=   new  HookEventHandler(callProcHook_HookInvoked);
 35             callProcHook.Install();
 36         }

 37
 38          private   void  keyHook_HookInvoked( object  sender, HookEventArgs e)
 39          {
 40              if  (e.key  ==  Keys.F4  &&  e.bAltKey)  // Alt + F4
 41              {
 42                  this .WindowState  =  FormWindowState.Minimized;
 43             }

 44         }

 45
 46          private   void  callProcHook_HookInvoked( object  sender, HookEventArgs e)
 47          {
 48              unsafe
 49              {
 50                 CWPSTRUCT *  message  =  (CWPSTRUCT * )e.lParam;
 51                  if  (message  !=   null )
 52                  {
 53                      if  (message -> message  ==  WM_CLOSE)
 54                      {
 55                         (sender  as  MyHook).CallNextProc  =   false ;
 56                         MessageBox.Show( " 程序即将关闭! " );
 57                     }

 58                 }

 59             }

 60         }

 61
 62     }

 63
 64      #region  消息结构体(参照WinUser.h中定义)
 65      public   struct  CWPSTRUCT
 66      {
 67          public  IntPtr lparam;
 68          public  IntPtr wparam;
 69          public   int  message;
 70          public  IntPtr hwnd;
 71     }

 72      #endregion

 73
 74      #region  钩子类型的枚举
 75      public   enum  HookType :  int
 76      {
 77         WH_JOURNALRECORD  =   0 ,
 78         WH_JOURNALPLAYBACK  =   1 ,
 79         WH_KEYBOARD  =   2 ,
 80         WH_GETMESSAGE  =   3 ,
 81         WH_CALLWNDPROC  =   4 ,
 82         WH_CBT  =   5 ,
 83         WH_SYSMSGFILTER  =   6 ,
 84         WH_MOUSE  =   7 ,
 85         WH_HARDWARE  =   8 ,
 86         WH_DEBUG  =   9 ,
 87         WH_SHELL  =   10 ,
 88         WH_FOREGROUNDIDLE  =   11 ,
 89         WH_CALLWNDPROCRET  =   12 ,
 90         WH_KEYBOARD_LL  =   13 ,
 91         WH_MOUSE_LL  =   14
 92     }

 93      #endregion

 94
 95      #region  虚键值的定义(参照WinUser.h中定义)
 96      public   enum  VirtualKeys
 97      {
 98         VK_SHIFT  =   0x10 ,
 99         VK_CONTROL  =   0x11 ,
100         VK_MENU  =   0x12 ,     // ALT
101         VK_PAUSE  =   0x13 ,
102         VK_CAPITAL  =   0x14
103     }

104      #endregion

105
106      #region  钩子委托
107      public   delegate   int  HookProc( int  code, IntPtr wParam, IntPtr lParam);
108      public   delegate   void  HookEventHandler( object  sender, HookEventArgs e);
109      #endregion

110
111      #region  钩子事件参数
112      public   class  HookEventArgs : EventArgs
113      {
114          public   int  HookCode;
115          public  IntPtr wParam;
116          public  IntPtr lParam;
117          public  Keys key;
118          public   bool  bAltKey;
119          public   bool  bCtrlKey;
120     }

121      #endregion

122
123      #region  钩子类
124      public   class  MyHook
125      {
126          调用Windows API
139
140          局部变量
154
155          public   event  HookEventHandler HookInvoked;
156
157          public   void  Install()
158          {
159             m_hook  =  SetWindowsHookEx(m_hooktype, m_hookproc, IntPtr.Zero, ( uint )AppDomain.GetCurrentThreadId());
160         }

161
162          public   void  Uninstall()
163          {
164              if  (m_hook  !=  IntPtr.Zero)
165              {
166                 UnhookWindowsHookEx(m_hook);
167             }

168         }

169
170          public  MyHook(HookType HookType)
171          {
172             m_hooktype  =  HookType;
173              if  (m_hooktype  ==  HookType.WH_KEYBOARD)
174              {
175                 m_hookproc  =   new  HookProc(KeyHookProcedure);
176             }

177              else   if  (m_hooktype  ==  HookType.WH_CALLWNDPROC)
178              {
179                 m_hookproc  =   new  HookProc(CallProcHookProcedure);
180             }

181         }

182
183          protected   int  KeyHookProcedure( int  code, IntPtr wParam, IntPtr lParam)
184          {
185              if  (code  !=   0 )
186              {
187                  return  CallNextHookEx(m_hook, code, wParam, lParam);
188             }

189
190              if  (HookInvoked  !=   null )
191              {
192                 Keys key  =  (Keys)wParam.ToInt32();
193                 HookEventArgs eventArgs  =   new  HookEventArgs();
194                 eventArgs.key  =  key;
195                 eventArgs.lParam  =  lParam;
196                 eventArgs.wParam  =  wParam;
197                 eventArgs.HookCode  =  code;
198                 eventArgs.bAltKey  =  GetKeyState(VirtualKeys.VK_MENU)  <=   - 127 ;
199                 eventArgs.bCtrlKey  =  GetKeyState(VirtualKeys.VK_CONTROL)  <=   - 127 ;
200                 HookInvoked( this , eventArgs);
201             }

202
203              return  CallNextHookEx(m_hook, code, wParam, lParam);
204         }

205
206          protected   int  CallProcHookProcedure( int  code, IntPtr wParam, IntPtr lParam)
207          {
208              try
209              {
210                 CallNextProc  =   true ;
211                  if  (HookInvoked  !=   null )
212                  {
213                     HookEventArgs eventArgs  =   new  HookEventArgs();
214                     eventArgs.lParam  =  lParam;
215                     eventArgs.wParam  =  wParam;
216                     eventArgs.HookCode  =  code;
217                     HookInvoked( this , eventArgs);
218                 }

219
220                  if  (CallNextProc)
221                  {
222                      return  CallNextHookEx(m_hook, code, wParam, lParam);
223                 }

224                  else
225                  {
226                      // return 1;
227                      return  CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
228                 }

229             }

230              catch  (Exception ex)
231              {
232                 MessageBox.Show(ex.Message);
233                  return   0 ;
234             }

235         }

236     }

237      #endregion

238 }


以上的钩子只对当前应用程序起作用,如果想控制其他的所有程序,需要使用全局钩子。原则上全局钩子在C#中是不支持的,在http://www.codeproject.com/csharp/globalhook.asp 中的代码可以参照来实现全局钩子

posted @ 2008-01-08 18:39 永春 阅读(6480) 评论(14) 编辑 收藏 所属分类: .Net

 

   回复    引用    查看     
#1楼 2008-01-08 18:46 | egmkang        
好文~~~
顶一下

   回复    引用    查看     
#2楼 2008-01-08 20:21 | BirdsHover        
c#的钩子只能是全局的,很不好用。而且不能注入非托管的程序,托管的情况没试过
   回复    引用    查看     
#3楼 2008-01-08 22:17 | 代码乱了        
不能注入非托管的程序?
---------------------------
应该是可以的,这个是没有限制的

   回复    引用     
#4楼 2008-01-08 23:24 | 看客[未注册用户]
c#的钩子只能是全局的??


说反了吧
C#不能做全局钩子的 但有两个例外(键盘消息和鼠标消息)

   回复    引用    查看     
#5楼 2008-01-09 02:51 | deerchao        
呵呵,这两天正在为WinForms里的程序级快捷键功能找资料,试下这个了:)
   回复    引用    查看     
#6楼 [ 楼主 ]2008-01-09 08:39 | 永春        
@BirdsHover
是可以注入非托管程序的。
正如看客所说,C#不能做全局钩子的 但有两个例外(键盘消息和鼠标消息) ,做全局钩子的方法可以参考我文章中提供的链接

   回复    引用    查看     
#7楼 2008-01-09 17:04 | Zealic        
舍近求远

System.Windows.Forms.Form.Closing

System.Windows.Forms.Application.AddMessageFilter


窗体级和程序级皆可
何必采用 PInvoke 呢

   回复    引用    查看     
#8楼 [ 楼主 ]2008-01-09 18:18 | 永春        
@Zealic
没试过,下次来试试看。不过我想可能没有钩子那么灵活吧

   回复    引用    查看     
#9楼 2008-01-10 10:37 | Schiller John        
很不错,收藏了。
C#调用API真是繁琐,我试图把博主的这段封装成C++CLR编译出来的库……失败咯……郁闷

   回复    引用     
#10楼 2008-03-08 03:57 | answerx[未注册用户]
C#只支持两种类型的全局钩子.低级键盘和低级鼠标全局钩子.其它的不行.别盲目各网友们
   回复    引用    查看     
#11楼 [ 楼主 ]2008-03-08 15:13 | 永春        
@answerx
C#中的全局钩子是有一定的限制的

   回复    引用     
#12楼 2008-07-18 22:50 | 54321[未注册用户]
7楼正解..
   回复    引用     
#13楼 2009-04-09 11:13 | longhunt
ssdddd
   回复    引用     
#14楼 2009-05-13 16:48 | mbxiwzd[未注册用户]
写的太好了,强.用这个我解决了插件中textBox的退格键不响应问题.谢谢
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值