在C#中使用鉤子實現Alt+F4健窗口最小化功能


[ 來源:http://www.it55.com | 作者: | 時間:2008-01-11 | 收藏 | 推薦 ] 【大 中 小】


    
  相信以前用過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("程序即將關閉!");
   }
   }
   }
   }
  這樣我們就可以通過鉤子實現一些相對底層的應用。
  
  代碼說的有點亂,我就把最主要的代碼直接列在下面供大家參考:
  
  例子代碼
   1using System;
   2using System.Collections.Generic;
   3using System.ComponentModel;
   4using System.Data;
   5using System.Drawing;
   6using System.Text;
   7using System.Windows.Forms;
   8using System.Runtime.InteropServices;
   9
   10namespace HookTest
   11{
   12 public partial class Form1 : Form
   13 {
   14 消息定義(WinUser.h中定義)#region 消息定義(WinUser.h中定義)
   15 private const int WM_PAINT = 0x000F;
   16 private const int WM_CLOSE = 0x0010;
   17 private const int WM_QUIT = 0x0012;
   18 private const int WM_DESTROY = 0x0002;
   19 #endregion
   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 消息結構體(參照WinUser.h中定義)#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 虛鍵值的定義(參照WinUser.h中定義)#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#region 調用Windows API
  127 [DllImport("user32.dll")]
  128 static extern IntPtr SetWindowsHookEx(HookType hook, HookProc callback, IntPtr hMod, uint dwThreadId);
  129
  130 [DllImport("user32.dll")]
  131 static extern bool UnhookWindowsHookEx(IntPtr hhk);
  132
  133 [DllImport("user32.dll")]
  134 static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
  135
  136 [DllImport("user32.dll")]
  137 static extern short GetKeyState(VirtualKeys nVirtKey);
  138 #endregion
  139
  140 局部變量#region 局部變量
  141 private IntPtr m_hook;
  142 private HookType m_hooktype;
  143 private HookProc m_hookproc;
  144
  145 private bool _bCallNext;
  146
  147 public bool CallNextProc
  148 {
  149 get { return _bCallNext; }
  150 set { _bCallNext = value; }
  151 }
  152
  153 #endregion
  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 中的代碼可以參照來實現全局鉤子

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章