C#鍵盤鉤子之區域性鉤子和全域性鉤子
阿新 • • 發佈:2018-12-31
最近碰巧要使用鍵盤鉤子,於是在網上搜索了一番,發現大多數部落格的文章都是雷同的,根本就沒有講清楚全域性鉤子和區域性鉤子的區別,於是特開一貼,講全域性鉤子和區域性鉤子捋一捋。也供後面的人學習。
因為大部分應用都應該採用區域性鉤子,所以我這兒使用的是區域性鉤子,而全域性鉤子的例子網上到處都是。
大部分網上參考文章都只是展示了全域性鉤子的寫法,而執行緒鉤子的寫法和介紹相對少一些,特別是關鍵語句上如果定義的不正確是沒有任何效果的,在自己反覆嘗試後決定留下一個正確的版本分享出來
程式碼如下
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace AssistToolSet.Util
{
/// <summary>
/// 鍵盤鉤子類
/// </summary>
public class Hook
{
public delegate int HookProc(WH_CODE nCode, Int32 wParam, IntPtr lParam);
public enum WH_CODE : int
{
WH_JOURNALRECORD = 0,
WH_JOURNALPLAYBACK = 1,
/// <summary>
/// 程序鉤子
/// </summary>
WH_KEYBOARD = 2,
/// <summary>
/// 底層鍵盤鉤子 全域性鉤子就是用這個
/// </summary>
WH_KEYBOARD_LL = 13 ,
}
public enum HC_CODE : int
{
HC_ACTION = 0,
HC_GETNEXT = 1,
HC_SKIP = 2,
HC_NOREMOVE = 3,
HC_NOREM = 3,
HC_SYSMODALON = 4,
HC_SYSMODALOFF = 5
}
/// <summary>
/// 安裝鉤子
/// </summary>
[DllImport("user32.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr SetWindowsHookEx(WH_CODE idHook, HookProc lpfn, IntPtr pInstance, uint threadId);
/// <summary>
/// 解除安裝鉤子
/// </summary>
[DllImport("user32.dll", CallingConvention = CallingConvention.StdCall)]
public static extern bool UnhookWindowsHookEx(IntPtr pHookHandle);
/// <summary>
/// 傳遞鉤子
/// </summary>
[DllImport("user32.dll", CallingConvention = CallingConvention.StdCall)]
public static extern int CallNextHookEx(IntPtr pHookHandle, WH_CODE nCodem, Int32 wParam, IntPtr lParam);
/// <summary>
/// 獲取全部按鍵狀態
/// </summary>
/// <param name="pbKeyState"></param>
/// <returns>非0表示成功</returns>
[DllImport("user32.dll")]
public static extern int GetKeyboardState(byte[] pbKeyState);
/// <summary>
/// 獲取程式集模組的控制代碼
/// </summary>
/// <param name="lpModuleName"></param>
/// <returns></returns>
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
/// <summary>
/// 獲取當前程序中的當前執行緒ID
/// </summary>
/// <returns></returns>
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern uint GetCurrentThreadId();
#region 私有變數
private byte[] mKeyState = new byte[256];
private Keys mKeyData = Keys.None; //專門用於判斷按鍵的狀態
/// <summary>
/// 鍵盤鉤子控制代碼
/// </summary>
private IntPtr mKetboardHook = IntPtr.Zero;
/// <summary>
/// 鍵盤鉤子委託例項
/// </summary>
private HookProc mKeyboardHookProcedure;
#endregion
#region 鍵盤事件
public event KeyEventHandler OnKeyDown;
public event KeyEventHandler OnKeyUp;
#endregion
/// <summary>
/// 建構函式
/// </summary>
public Hook()
{
GetKeyboardState(this.mKeyState);
}
~Hook()
{
UnInstallHook();
}
/// <summary>
/// 鍵盤鉤子處理函式
/// </summary>
private int KeyboardHookProc(WH_CODE nCode, Int32 wParam, IntPtr lParam)
{
/*全域性鉤子應該這樣設定
KeyboardHookStruct MyKeyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
*/
// 定義為執行緒鉤子時,wParam的值是擊打的按鍵,與Keys裡的對應按鍵相同
if ((nCode == (int)HC_CODE.HC_ACTION) && (this.OnKeyDown != null || this.OnKeyUp != null))
{
mKeyData = (Keys)wParam;
KeyEventArgs keyEvent = new KeyEventArgs(mKeyData);
//這裡簡單的通過lParam的值的正負情況與按鍵的狀態相關聯
if (lParam.ToInt32() > 0 && this.OnKeyDown != null)
{
this.OnKeyDown(this, keyEvent);
}
else if (lParam.ToInt32() < 0 && this.OnKeyUp != null)
{
this.OnKeyUp(this, keyEvent);
}
}
if (ShortcutManagement.s_bHotkeyUsed)
{
ShortcutManagement.s_bHotkeyUsed = false;
return 1;
}
return CallNextHookEx(this.mKetboardHook, nCode, wParam, lParam);
}
/// <summary>
/// 安裝鉤子
/// </summary>
/// <returns></returns>
public bool InstallHook()
{
//執行緒鉤子時一定要通過這個取得的值才是作業系統下真實的執行緒
uint result = GetCurrentThreadId();
if (this.mKetboardHook == IntPtr.Zero)
{
this.mKeyboardHookProcedure = new HookProc(this.KeyboardHookProc);
//註冊執行緒鉤子時第三個引數是空
this.mKetboardHook = SetWindowsHookEx(WH_CODE.WH_KEYBOARD, this.mKeyboardHookProcedure, IntPtr.Zero, result);
/*
如果是全域性鉤子應該這樣使用
this.mKetboardHook = SetWindowsHookEx(WH_CODE.WH_KEYBOARD_LL, mKeyboardHookProcedure,GetModuleHandle(System.Diagnostics.Process.GetCurrentProcess().MainModule.ModuleName), 0);
*/
if (this.mKetboardHook == IntPtr.Zero)
{
return false;
}
}
return true;
}
/// <summary>
/// 解除安裝鉤子
/// </summary>
/// <returns>true表示成功 </returns>
public bool UnInstallHook()
{
bool result = true;
if (this.mKetboardHook != IntPtr.Zero)
{
result = UnhookWindowsHookEx(this.mKetboardHook) && result;
this.mKetboardHook = IntPtr.Zero;
}
return result;
}
}
}
通過這次認知,意識到,以後如果要做這些接觸相關的api的時候,我們應該儘量去查官方文件,而不是一開始就是檢視別人的部落格。應該以官方文件為主。一定要記住鍵盤鉤子事件。32位和64位的系統不一樣。