C#鍵盤鉤子實現全局快捷鍵?

本文介紹如何使用Win32Api創建鍵盤鉤子,並編寫一個使用全局快捷鍵的實例。

工具/原料

Visual Studio(本文使用VS2013,其他版本亦可)。

基礎知識

使用鉤子之前,需要使用SetWindowsHookEx()函數創建鉤子,使用完畢之後要UnhookWindowsHookEx()函數卸載鉤子,“鉤”到消息後操作系統會自動調用在創建鉤子時註冊的回調函數來處理消息,處理完後調用CallNextHookEx()函數等待或處理下一條消息。有關鉤子的詳細信息請見參考--C#鼠標鉤子,其中已介紹。

對於鍵盤鉤子,鉤子類型為WH_KEYBOARD_LL=13,只需要設置SetWindowsHookEx的idHook參數為13即可“鉤”到鍵盤消息。關於鉤子類型的資料見參考資料--鉤子類型。

鍵盤鉤子實例

啟動VS,新建C# WinForm項目,命名為“Cs鍵盤鉤子”,如下:

C#鍵盤鉤子實現全局快捷鍵

鍵盤鉤子實現全局快捷鍵#

對主窗口布局,如下:

C#鍵盤鉤子實現全局快捷鍵

鍵盤鉤子實現全局快捷鍵#

添加Win32Api引用,代碼如下:

public class Win32Api

{

#region 常數和結構

public const int WM_KEYDOWN = 0x100;

public const int WM_KEYUP = 0x101;

public const int WM_SYSKEYDOWN = 0x104;

public const int WM_SYSKEYUP = 0x105;

public const int WH_KEYBOARD_LL = 13;

[StructLayout(LayoutKind.Sequential)] //聲明鍵盤鉤子的封送結構類型

public class KeyboardHookStruct

{

public int vkCode; //表示一個在1到254間的虛似鍵盤碼

public int scanCode; //表示硬件掃描碼

public int flags;

public int time;

public int dwExtraInfo;

}

#endregion

#region Api

public delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);

//安裝鉤子的函數

[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]

public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);

//卸下鉤子的函數

[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]

public static extern bool UnhookWindowsHookEx(int idHook);

//下一個鉤掛的函數

[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]

public static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam);

[DllImport("user32")]

public static extern int ToAscii(int uVirtKey, int uScanCode, byte[] lpbKeyState, byte[] lpwTransKey, int fuState);

[DllImport("user32")]

public static extern int GetKeyboardState(byte[] pbKeyState);

[DllImport("kernel32.dll", CharSet = CharSet.Auto,CallingConvention = CallingConvention.StdCall)]

public static extern IntPtr GetModuleHandle(string lpModuleName);

#endregion

添加新建類KeyboardHook,封裝鍵盤鉤子,代碼如下:

public class KeyboardHook

{

int hHook;

Win32Api.HookProc KeyboardHookDelegate;

public event KeyEventHandler OnKeyDownEvent;

public event KeyEventHandler OnKeyUpEvent;

public event KeyPressEventHandler OnKeyPressEvent;

public KeyboardHook() { }

public void SetHook()

{

KeyboardHookDelegate = new Win32Api.HookProc(KeyboardHookProc);

Process cProcess = Process.GetCurrentProcess();

ProcessModule cModule = cProcess.MainModule;

var mh = Win32Api.GetModuleHandle(cModule.ModuleName);

hHook = Win32Api.SetWindowsHookEx(Win32Api.WH_KEYBOARD_LL, KeyboardHookDelegate, mh, 0);

}

public void UnHook()

{

Win32Api.UnhookWindowsHookEx(hHook);

}

private List preKeysList = new List ();//存放被按下的控制鍵,用來生成具體的鍵

private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)

{

//如果該消息被丟棄(nCode<0)或者沒有事件綁定處理程序則不會觸發事件

if ((nCode >= 0) && (OnKeyDownEvent != null OnKeyUpEvent != null OnKeyPressEvent != null))

{

Win32Api.KeyboardHookStruct KeyDataFromHook = (Win32Api.KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(Win32Api.KeyboardHookStruct));

Keys keyData = (Keys)KeyDataFromHook.vkCode;

//按下控制鍵

if ((OnKeyDownEvent != null OnKeyPressEvent != null) && (wParam == Win32Api.WM_KEYDOWN wParam == Win32Api.WM_SYSKEYDOWN))

{

if (IsCtrlAltShiftKeys(keyData) && preKeysList.IndexOf(keyData) == -1)

{

preKeysList.Add(keyData);

}

}

//WM_KEYDOWN和WM_SYSKEYDOWN消息,將會引發OnKeyDownEvent事件

if (OnKeyDownEvent != null && (wParam == Win32Api.WM_KEYDOWN wParam == Win32Api.WM_SYSKEYDOWN))

{

KeyEventArgs e = new KeyEventArgs(GetDownKeys(keyData));

OnKeyDownEvent(this, e);

}

//WM_KEYDOWN消息將引發OnKeyPressEvent

if (OnKeyPressEvent != null && wParam == Win32Api.WM_KEYDOWN)

{

byte[] keyState = new byte[256];

Win32Api.GetKeyboardState(keyState);

byte[] inBuffer = new byte[2];

if (Win32Api.ToAscii(KeyDataFromHook.vkCode, KeyDataFromHook.scanCode, keyState, inBuffer, KeyDataFromHook.flags) == 1)

{

KeyPressEventArgs e = new KeyPressEventArgs((char)inBuffer[0]);

OnKeyPressEvent(this, e);

}

}

//鬆開控制鍵

if ((OnKeyDownEvent != null OnKeyPressEvent != null) && (wParam == Win32Api.WM_KEYUP wParam == Win32Api.WM_SYSKEYUP))

{

if (IsCtrlAltShiftKeys(keyData))

{

for (int i = preKeysList.Count - 1; i >= 0; i--)

{

if (preKeysList[i] == keyData) { preKeysList.RemoveAt(i); }

}

}

}

//WM_KEYUP和WM_SYSKEYUP消息,將引發OnKeyUpEvent事件

if (OnKeyUpEvent != null && (wParam == Win32Api.WM_KEYUP wParam == Win32Api.WM_SYSKEYUP))

{

KeyEventArgs e = new KeyEventArgs(GetDownKeys(keyData));

OnKeyUpEvent(this, e);

}

}

return Win32Api.CallNextHookEx(hHook, nCode, wParam, lParam);

}

//根據已經按下的控制鍵生成key

private Keys GetDownKeys(Keys key)

{

Keys rtnKey = Keys.None;

foreach (Keys i in preKeysList)

{

if (i == Keys.LControlKey i == Keys.RControlKey) { rtnKey = rtnKey Keys.Control; }

if (i == Keys.LMenu i == Keys.RMenu) { rtnKey = rtnKey Keys.Alt; }

if (i == Keys.LShiftKey i == Keys.RShiftKey) { rtnKey = rtnKey Keys.Shift; }

}

return rtnKey key;

}

private Boolean IsCtrlAltShiftKeys(Keys key)

{

if (key == Keys.LControlKey key == Keys.RControlKey key == Keys.LMenu key == Keys.RMenu key == Keys.LShiftKey key == Keys.RShiftKey) { return true; }

return false;

}

}

在主窗體中添加代碼,如下:

public MainForm()

{

InitializeComponent();

}

KeyboardHook kh;

private void Form1_Load(object sender, EventArgs e)

{

kh = new KeyboardHook();

kh.SetHook();

kh.OnKeyDownEvent += kh_OnKeyDownEvent;

}

void kh_OnKeyDownEvent(object sender, KeyEventArgs e)

{

if (e.KeyData == (Keys.S Keys.Control)) { this.Show(); }//Ctrl+S顯示窗口

if (e.KeyData == (Keys.H Keys.Control)) { this.Hide(); }//Ctrl+H隱藏窗口

if (e.KeyData == (Keys.C Keys.Control)) { this.Close(); }//Ctrl+C 關閉窗口

if (e.KeyData == (Keys.A Keys.Control Keys.Alt)) { this.Text = "你發現了什麼?"; }//Ctrl+Alt+A

}

private void Form1_FormClosing(object sender, FormClosingEventArgs e)

{

kh.UnHook();

}

}

代碼添加完畢後,運行調試。當按下Ctrl+H是窗口隱藏,當按下Ctrl+S窗口顯示,當按下Ctrl+C時窗口關閉,開著QQ按下Ctrl+Alt+A時...(會出現什麼呢?這個要你自己去發現哦)。

C#鍵盤鉤子實現全局快捷鍵

鍵盤鉤子實現全局快捷鍵#

注意事項

也許你已經注意到我們可以使用相似的技術把消息(包括自定義消息)封裝成事件,這似乎是一個有趣的事情。

相關問題答案