键盘钩子有两种: WH_KEYBOARD和WH_KEYBOARD_LL(Low Level). 两者的区别:
WH_KEYBOARD | WH_KEYBOARD_LL |
全局或者本线程 | 只能全局 |
Win95/NT都行 | 只能WinNT |
用VS2008调试时注意, 去掉如下选项: 项目>Debug>Enable the Visual Studio hosting process
注意: 下面代码只实现了全局钩子
Win32.cs, 封装了Win32的API, Struct, Enum等
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace KeyboardHook
{
/**//// <summary>
/// Hook Scope
//WH_CALLWNDPROC Thread or global
//WH_CALLWNDPROCRET Thread or global
//WH_CBT Thread or global
//WH_DEBUG Thread or global
//WH_FOREGROUNDIDLE Thread or global
//WH_GETMESSAGE Thread or global
//WH_JOURNALPLAYBACK Global only
//WH_JOURNALRECORD Global only
//WH_KEYBOARD Thread or global
//WH_KEYBOARD_LL Global only
//WH_MOUSE Thread or global
//WH_MOUSE_LL Global only
//WH_MSGFILTER Thread or global
//WH_SHELL Thread or global
//WH_SYSMSGFILTER Global only
/**//// </summary>
public enum Win32HookType : 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
}
/**//// <summary>
/// The KBDLLHOOKSTRUCT structure contains information about a low-level keyboard input event.
/// </summary>
/// <remarks>
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/hooks/hookreference/hookstructures/cwpstruct.asp
/// </remarks>
[StructLayout(LayoutKind.Sequential)]
public class Win32KeyboardHookStruct
{
/**//// <summary>
/// Specifies a virtual-key code. The code must be a value in the range 1 to 254.
/// </summary>
public int vkCode;
/**//// <summary>
/// Specifies a hardware scan code for the key.
/// </summary>
public int scanCode;
/**//// <summary>
/// Specifies the extended-key flag, event-injected flag, context code, and transition-state flag.
/// </summary>
public int flags;
/**//// <summary>
/// Specifies the time stamp for this message.
/// </summary>
public int time;
/**//// <summary>
/// Specifies extra information associated with the message.
/// </summary>
public int dwExtraInfo;
}
public enum VirtualKeys
{
VK_SHIFT = 0x10,
VK_CONTROL = 0x11,
VK_MENU = 0x12, //ALT
VK_ALT = 0x12,
VK_PAUSE = 0x13,
VK_CAPTIAL = 0x14,
VK_NUMLOCK = 0x90
}
public enum Win32Messages
{
WM_KEYDOWN = 0x100,
/**//// <summary>
/// The WM_KEYUP message is posted to the window with the keyboard focus when a nonsystem
/// key is released. A nonsystem key is a key that is pressed when the ALT key is not pressed,
/// or a keyboard key that is pressed when a window has the keyboard focus.
/// </summary>
WM_KEYUP = 0x101,
/**//// <summary>
/// The WM_SYSKEYDOWN message is posted to the window with the keyboard focus when the user
/// presses the F10 key (which activates the menu bar) or holds down the ALT key and then
/// presses another key. It also occurs when no window currently has the keyboard focus;
/// in this case, the WM_SYSKEYDOWN message is sent to the active window. The window that
/// receives the message can distinguish between these two contexts by checking the context
/// code in the lParam parameter.
/// </summary>
WM_SYSKEYDOWN = 0x104,
/**//// <summary>
/// The WM_SYSKEYUP message is posted to the window with the keyboard focus when the user
/// releases a key that was pressed while the ALT key was held down. It also occurs when no
/// window currently has the keyboard focus; in this case, the WM_SYSKEYUP message is sent
/// to the active window. The window that receives the message can distinguish between
/// these two contexts by checking the context code in the lParam parameter.
/// </summary>
WM_SYSKEYUP = 0x105
}
public delegate int Win32HookProc(int nCode, Int32 wParam, IntPtr lParam);
public class Win32
{
Windows function imports#region Windows function imports
/**//// <summary>
/// The SetWindowsHookEx function installs an application-defined hook procedure into a hook chain.
/// You would install a hook procedure to monitor the system for certain types of events. These events
/// are associated either with a specific thread or with all threads in the same desktop as the calling thread.
/// </summary>
/// <param name="idHook">
/// [in] Specifies the type of hook procedure to be installed. This parameter can be one of the following values.
/// </param>
/// <param name="lpfn">
/// [in] Pointer to the hook procedure. If the dwThreadId parameter is zero or specifies the identifier of a
/// thread created by a different process, the lpfn parameter must point to a hook procedure in a dynamic-link
/// library (DLL). Otherwise, lpfn can point to a hook procedure in the code associated with the current process.
/// </param>
/// <param name="hMod">
/// [in] Handle to the DLL containing the hook procedure pointed to by the lpfn parameter.
/// The hMod parameter must be set to NULL if the dwThreadId parameter specifies a thread created by
/// the current process and if the hook procedure is within the code associated with the current process.
/// </param>
/// <param name="dwThreadId">
/// [in] Specifies the identifier of the thread with which the hook procedure is to be associated.
/// If this parameter is zero, the hook procedure is associated with all existing threads running in the
/// same desktop as the calling thread.
/// </param>
/// <returns>
/// If the function succeeds, the return value is the handle to the hook procedure.
/// If the function fails, the return value is NULL. To get extended error information, call GetLastError.
/// </returns>
/// <remarks>
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/hooks/hookreference/hookfunctions/setwindowshookex.asp
/// </remarks>
[DllImport("user32.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall, SetLastError = true)]
public static extern int SetWindowsHookEx(
int idHook,
Win32HookProc lpfn,
IntPtr hMod,
int dwThreadId);
/**//// <summary>
/// The UnhookWindowsHookEx function removes a hook procedure installed in a hook chain by the SetWindowsHookEx function.
/// </summary>
/// <param name="idHook">
/// [in] Handle to the hook to be removed. This parameter is a hook handle obtained by a previous call to SetWindowsHookEx.
/// </param>
/// <returns>
/// If the function succeeds, the return value is nonzero.
/// If the function fails, the return value is zero. To get extended error information, call GetLastError.
/// </returns>
/// <remarks>
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/hooks/hookreference/hookfunctions/setwindowshookex.asp
/// </remarks>
[DllImport("user32.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall, SetLastError = true)]
public static extern int UnhookWindowsHookEx(int idHook);
/**//// <summary>
/// The CallNextHookEx function passes the hook information to the next hook procedure in the current hook chain.
/// A hook procedure can call this function either before or after processing the hook information.
/// </summary>
/// <param name="idHook">Ignored.</param>
/// <param name="nCode">
/// [in] Specifies the hook code passed to the current hook procedure.
/// The next hook procedure uses this code to determine how to process the hook information.
/// </param>
/// <param name="wParam">
/// [in] Specifies the wParam value passed to the current hook procedure.
/// The meaning of this parameter depends on the type of hook associated with the current hook chain.
/// </param>
/// <param name="lParam">
/// [in] Specifies the lParam value passed to the current hook procedure.
/// The meaning of this parameter depends on the type of hook associated with the current hook chain.
/// </param>
/// <returns>
/// This value is returned by the next hook procedure in the chain.
/// The current hook procedure must also return this value. The meaning of the return value depends on the hook type.
/// For more information, see the descriptions of the individual hook procedures.
/// </returns>
/// <remarks>
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/hooks/hookreference/hookfunctions/setwindowshookex.asp
/// </remarks>
[DllImport("user32.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall)]
public static extern int CallNextHookEx(
int idHook,
int nCode,
int wParam,
IntPtr lParam);
/**//// <summary>
/// The ToAscii function translates the specified virtual-key code and keyboard
/// state to the corresponding character or characters. The function translates the code
/// using the input language and physical keyboard layout identified by the keyboard layout handle.
/// </summary>
/// <param name="uVirtKey">
/// [in] Specifies the virtual-key code to be translated.
/// </param>
/// <param name="uScanCode">
/// [in] Specifies the hardware scan code of the key to be translated.
/// The high-order bit of this value is set if the key is up (not pressed).
/// </param>
/// <param name="lpbKeyState">
/// [in] Pointer to a 256-byte array that contains the current keyboard state.
/// Each element (byte) in the array contains the state of one key.
/// If the high-order bit of a byte is set, the key is down (pressed).
/// The low bit, if set, indicates that the key is toggled on. In this function,
/// only the toggle bit of the CAPS LOCK key is relevant. The toggle state
/// of the NUM LOCK and SCROLL LOCK keys is ignored.
/// </param>
/// <param name="lpwTransKey">
/// [out] Pointer to the buffer that receives the translated character or characters.
/// </param>
/// <param name="fuState">
/// [in] Specifies whether a menu is active. This parameter must be 1 if a menu is active, or 0 otherwise.
/// </param>
/// <returns>
/// If the specified key is a dead key, the return value is negative. Otherwise, it is one of the following values.
/// Value Meaning
/// 0 The specified virtual key has no translation for the current state of the keyboard.
/// 1 One character was copied to the buffer.
/// 2 Two characters were copied to the buffer. This usually happens when a dead-key character
/// (accent or diacritic) stored in the keyboard layout cannot be composed with the specified
/// virtual key to form a single character.
/// </returns>
/// <remarks>
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/userinput/keyboardinput/keyboardinputreference/keyboardinputfunctions/toascii.asp
/// </remarks>
[DllImport("user32")]
public static extern int ToAscii(
int uVirtKey,
int uScanCode,
byte[] lpbKeyState,
byte[] lpwTransKey,
int fuState);
/**//// <summary>
/// The GetKeyboardState function copies the status of the 256 virtual keys to the
/// specified buffer.
/// </summary>
/// <param name="pbKeyState">
/// [in] Pointer to a 256-byte array that contains keyboard key states.
/// </param>
/// <returns>
/// If the function succeeds, the return value is nonzero.
/// If the function fails, the return value is zero. To get extended error information, call GetLastError.
/// </returns>
/// <remarks>
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/userinput/keyboardinput/keyboardinputreference/keyboardinputfunctions/toascii.asp
/// </remarks>
[DllImport("user32")]
public static extern int GetKeyboardState(byte[] pbKeyState);
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern short GetKeyState(int vKey);
#endregion
public static short GetKeyState(VirtualKeys vKey)
{
return Win32.GetKeyState((int)vKey);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace KeyboardHook
{
/**//// <summary>
/// Hook Scope
//WH_CALLWNDPROC Thread or global
//WH_CALLWNDPROCRET Thread or global
//WH_CBT Thread or global
//WH_DEBUG Thread or global
//WH_FOREGROUNDIDLE Thread or global
//WH_GETMESSAGE Thread or global
//WH_JOURNALPLAYBACK Global only
//WH_JOURNALRECORD Global only
//WH_KEYBOARD Thread or global
//WH_KEYBOARD_LL Global only
//WH_MOUSE Thread or global
//WH_MOUSE_LL Global only
//WH_MSGFILTER Thread or global
//WH_SHELL Thread or global
//WH_SYSMSGFILTER Global only
/**//// </summary>
public enum Win32HookType : 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
}
/**//// <summary>
/// The KBDLLHOOKSTRUCT structure contains information about a low-level keyboard input event.
/// </summary>
/// <remarks>
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/hooks/hookreference/hookstructures/cwpstruct.asp
/// </remarks>
[StructLayout(LayoutKind.Sequential)]
public class Win32KeyboardHookStruct
{
/**//// <summary>
/// Specifies a virtual-key code. The code must be a value in the range 1 to 254.
/// </summary>
public int vkCode;
/**//// <summary>
/// Specifies a hardware scan code for the key.
/// </summary>
public int scanCode;
/**//// <summary>
/// Specifies the extended-key flag, event-injected flag, context code, and transition-state flag.
/// </summary>
public int flags;
/**//// <summary>
/// Specifies the time stamp for this message.
/// </summary>
public int time;
/**//// <summary>
/// Specifies extra information associated with the message.
/// </summary>
public int dwExtraInfo;
}
public enum VirtualKeys
{
VK_SHIFT = 0x10,
VK_CONTROL = 0x11,
VK_MENU = 0x12, //ALT
VK_ALT = 0x12,
VK_PAUSE = 0x13,
VK_CAPTIAL = 0x14,
VK_NUMLOCK = 0x90
}
public enum Win32Messages
{
WM_KEYDOWN = 0x100,
/**//// <summary>
/// The WM_KEYUP message is posted to the window with the keyboard focus when a nonsystem
/// key is released. A nonsystem key is a key that is pressed when the ALT key is not pressed,
/// or a keyboard key that is pressed when a window has the keyboard focus.
/// </summary>
WM_KEYUP = 0x101,
/**//// <summary>
/// The WM_SYSKEYDOWN message is posted to the window with the keyboard focus when the user
/// presses the F10 key (which activates the menu bar) or holds down the ALT key and then
/// presses another key. It also occurs when no window currently has the keyboard focus;
/// in this case, the WM_SYSKEYDOWN message is sent to the active window. The window that
/// receives the message can distinguish between these two contexts by checking the context
/// code in the lParam parameter.
/// </summary>
WM_SYSKEYDOWN = 0x104,
/**//// <summary>
/// The WM_SYSKEYUP message is posted to the window with the keyboard focus when the user
/// releases a key that was pressed while the ALT key was held down. It also occurs when no
/// window currently has the keyboard focus; in this case, the WM_SYSKEYUP message is sent
/// to the active window. The window that receives the message can distinguish between
/// these two contexts by checking the context code in the lParam parameter.
/// </summary>
WM_SYSKEYUP = 0x105
}
public delegate int Win32HookProc(int nCode, Int32 wParam, IntPtr lParam);
public class Win32
{
Windows function imports#region Windows function imports
/**//// <summary>
/// The SetWindowsHookEx function installs an application-defined hook procedure into a hook chain.
/// You would install a hook procedure to monitor the system for certain types of events. These events
/// are associated either with a specific thread or with all threads in the same desktop as the calling thread.
/// </summary>
/// <param name="idHook">
/// [in] Specifies the type of hook procedure to be installed. This parameter can be one of the following values.
/// </param>
/// <param name="lpfn">
/// [in] Pointer to the hook procedure. If the dwThreadId parameter is zero or specifies the identifier of a
/// thread created by a different process, the lpfn parameter must point to a hook procedure in a dynamic-link
/// library (DLL). Otherwise, lpfn can point to a hook procedure in the code associated with the current process.
/// </param>
/// <param name="hMod">
/// [in] Handle to the DLL containing the hook procedure pointed to by the lpfn parameter.
/// The hMod parameter must be set to NULL if the dwThreadId parameter specifies a thread created by
/// the current process and if the hook procedure is within the code associated with the current process.
/// </param>
/// <param name="dwThreadId">
/// [in] Specifies the identifier of the thread with which the hook procedure is to be associated.
/// If this parameter is zero, the hook procedure is associated with all existing threads running in the
/// same desktop as the calling thread.
/// </param>
/// <returns>
/// If the function succeeds, the return value is the handle to the hook procedure.
/// If the function fails, the return value is NULL. To get extended error information, call GetLastError.
/// </returns>
/// <remarks>
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/hooks/hookreference/hookfunctions/setwindowshookex.asp
/// </remarks>
[DllImport("user32.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall, SetLastError = true)]
public static extern int SetWindowsHookEx(
int idHook,
Win32HookProc lpfn,
IntPtr hMod,
int dwThreadId);
/**//// <summary>
/// The UnhookWindowsHookEx function removes a hook procedure installed in a hook chain by the SetWindowsHookEx function.
/// </summary>
/// <param name="idHook">
/// [in] Handle to the hook to be removed. This parameter is a hook handle obtained by a previous call to SetWindowsHookEx.
/// </param>
/// <returns>
/// If the function succeeds, the return value is nonzero.
/// If the function fails, the return value is zero. To get extended error information, call GetLastError.
/// </returns>
/// <remarks>
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/hooks/hookreference/hookfunctions/setwindowshookex.asp
/// </remarks>
[DllImport("user32.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall, SetLastError = true)]
public static extern int UnhookWindowsHookEx(int idHook);
/**//// <summary>
/// The CallNextHookEx function passes the hook information to the next hook procedure in the current hook chain.
/// A hook procedure can call this function either before or after processing the hook information.
/// </summary>
/// <param name="idHook">Ignored.</param>
/// <param name="nCode">
/// [in] Specifies the hook code passed to the current hook procedure.
/// The next hook procedure uses this code to determine how to process the hook information.
/// </param>
/// <param name="wParam">
/// [in] Specifies the wParam value passed to the current hook procedure.
/// The meaning of this parameter depends on the type of hook associated with the current hook chain.
/// </param>
/// <param name="lParam">
/// [in] Specifies the lParam value passed to the current hook procedure.
/// The meaning of this parameter depends on the type of hook associated with the current hook chain.
/// </param>
/// <returns>
/// This value is returned by the next hook procedure in the chain.
/// The current hook procedure must also return this value. The meaning of the return value depends on the hook type.
/// For more information, see the descriptions of the individual hook procedures.
/// </returns>
/// <remarks>
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/hooks/hookreference/hookfunctions/setwindowshookex.asp
/// </remarks>
[DllImport("user32.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall)]
public static extern int CallNextHookEx(
int idHook,
int nCode,
int wParam,
IntPtr lParam);
/**//// <summary>
/// The ToAscii function translates the specified virtual-key code and keyboard
/// state to the corresponding character or characters. The function translates the code
/// using the input language and physical keyboard layout identified by the keyboard layout handle.
/// </summary>
/// <param name="uVirtKey">
/// [in] Specifies the virtual-key code to be translated.
/// </param>
/// <param name="uScanCode">
/// [in] Specifies the hardware scan code of the key to be translated.
/// The high-order bit of this value is set if the key is up (not pressed).
/// </param>
/// <param name="lpbKeyState">
/// [in] Pointer to a 256-byte array that contains the current keyboard state.
/// Each element (byte) in the array contains the state of one key.
/// If the high-order bit of a byte is set, the key is down (pressed).
/// The low bit, if set, indicates that the key is toggled on. In this function,
/// only the toggle bit of the CAPS LOCK key is relevant. The toggle state
/// of the NUM LOCK and SCROLL LOCK keys is ignored.
/// </param>
/// <param name="lpwTransKey">
/// [out] Pointer to the buffer that receives the translated character or characters.
/// </param>
/// <param name="fuState">
/// [in] Specifies whether a menu is active. This parameter must be 1 if a menu is active, or 0 otherwise.
/// </param>
/// <returns>
/// If the specified key is a dead key, the return value is negative. Otherwise, it is one of the following values.
/// Value Meaning
/// 0 The specified virtual key has no translation for the current state of the keyboard.
/// 1 One character was copied to the buffer.
/// 2 Two characters were copied to the buffer. This usually happens when a dead-key character
/// (accent or diacritic) stored in the keyboard layout cannot be composed with the specified
/// virtual key to form a single character.
/// </returns>
/// <remarks>
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/userinput/keyboardinput/keyboardinputreference/keyboardinputfunctions/toascii.asp
/// </remarks>
[DllImport("user32")]
public static extern int ToAscii(
int uVirtKey,
int uScanCode,
byte[] lpbKeyState,
byte[] lpwTransKey,
int fuState);
/**//// <summary>
/// The GetKeyboardState function copies the status of the 256 virtual keys to the
/// specified buffer.
/// </summary>
/// <param name="pbKeyState">
/// [in] Pointer to a 256-byte array that contains keyboard key states.
/// </param>
/// <returns>
/// If the function succeeds, the return value is nonzero.
/// If the function fails, the return value is zero. To get extended error information, call GetLastError.
/// </returns>
/// <remarks>
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/userinput/keyboardinput/keyboardinputreference/keyboardinputfunctions/toascii.asp
/// </remarks>
[DllImport("user32")]
public static extern int GetKeyboardState(byte[] pbKeyState);
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern short GetKeyState(int vKey);
#endregion
public static short GetKeyState(VirtualKeys vKey)
{
return Win32.GetKeyState((int)vKey);
}
}
}
KeyboardHook.cs, 安装, 卸载Hook, 处理Hook
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Threading;
using System.Reflection;
using System.ComponentModel;
namespace KeyboardHook
{
/**//// <summary>
/// Hook scope
/// </summary>
public enum HookScope
{
Global,
Local
}
public class Win32KeyboardHook
{
/**//// <summary>
/// Occurs when the user presses a key
/// </summary>
public event KeyEventHandler KeyDown;
/**//// <summary>
/// Occurs when the user presses and releases
/// </summary>
public event KeyPressEventHandler KeyPress;
/**//// <summary>
/// Occurs when the user releases a key
/// </summary>
public event KeyEventHandler KeyUp;
int mHookId = 0;
int mThreadId = 0;
HookScope mHookScope = HookScope.Global;
Win32HookType mHookType = Win32HookType.WH_KEYBOARD_LL;
Win32HookProc mKeyboardHookProc;// to avoid "CallbackOnCollectedDelegate dected" exception, inited in constructor
public Win32KeyboardHook()
{
//TO avoid "CallbackOnCollectedDelegate" exception
//should store the delegate in an instance variable
this.mKeyboardHookProc = new Win32HookProc(HookProc);
}
public HookScope Scope
{
get
{
return this.mHookScope;
}
private set
{
this.mHookScope = value;
this.mThreadId = this.IsGlobalScope ? 0 : AppDomain.GetCurrentThreadId();
this.mHookType = this.IsGlobalScope ? Win32HookType.WH_KEYBOARD_LL : Win32HookType.WH_KEYBOARD;
}
}
public bool IsGlobalScope
{
get { return this.mHookScope == HookScope.Global; }
}
protected int HookProc(int nCode, Int32 wParam, IntPtr lParam)
{
if (this.IsGlobalScope)
{
return GlobalHookProc(nCode, wParam, lParam);
}
else
{
return LocalHookProc(nCode, wParam, lParam);
}
}
int GlobalHookProc(int nCode, Int32 wParam, IntPtr lParam)
{
//indicates if any of underlaing events set e.Handled flag
bool handled = false;
//it was ok and someone listens to events
if ((nCode >= 0) && (KeyDown != null || KeyUp != null || KeyPress != null))
{
//read structure KeyboardHookStruct at lParam
Win32KeyboardHookStruct theKeyboardHookStruct = (Win32KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(Win32KeyboardHookStruct));
//raise KeyDown
if (KeyDown != null && (wParam == (int)Win32Messages.WM_KEYDOWN || wParam == (int)Win32Messages.WM_SYSKEYDOWN))
{
Keys keyData = (Keys)theKeyboardHookStruct.vkCode;
KeyEventArgs e = new KeyEventArgs(keyData);
KeyDown(this, e);
handled = handled || e.Handled;
}
// raise KeyPress
if (KeyPress != null && wParam == (int)Win32Messages.WM_KEYDOWN)
{
bool isDownShift = ((Win32.GetKeyState(VirtualKeys.VK_SHIFT) & 0x80) == 0x80 ? true : false);
bool isDownCapslock = (Win32.GetKeyState(VirtualKeys.VK_CAPTIAL) != 0 ? true : false);
byte[] keyState = new byte[256];
Win32.GetKeyboardState(keyState);
byte[] inBuffer = new byte[2];
if (Win32.ToAscii(theKeyboardHookStruct.vkCode,
theKeyboardHookStruct.scanCode,
keyState,
inBuffer,
theKeyboardHookStruct.flags) == 1)
{
char key = (char)inBuffer[0];
if ((isDownCapslock ^ isDownShift) && Char.IsLetter(key)) key = Char.ToUpper(key);
KeyPressEventArgs e = new KeyPressEventArgs(key);
KeyPress(this, e);
handled = handled || e.Handled;
}
}
// raise KeyUp
if (KeyUp != null && (wParam == (int)Win32Messages.WM_KEYUP || wParam == (int)Win32Messages.WM_SYSKEYUP))
{
Keys keyData = (Keys)theKeyboardHookStruct.vkCode;
KeyEventArgs e = new KeyEventArgs(keyData);
KeyUp(this, e);
handled = handled || e.Handled;
}
}
//if event handled in application do not handoff to other listeners
if (handled)
return 1;
else
return Win32.CallNextHookEx(mHookId, nCode, wParam, lParam);
}
int LocalHookProc(int nCode, Int32 wParam, IntPtr lParam)
{
throw new NotImplementedException("Local hook is not implemented!");
}
public bool Create(HookScope scope)
{
if (mHookId != 0)
{
return false;
}
this.Scope = scope;
// set the keyboard hook
this.mHookId = Win32.SetWindowsHookEx(
Convert.ToInt32(this.mHookType),
mKeyboardHookProc,
Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]),
this.mThreadId);
if (this.mHookId == 0)
{
int errorCode = Marshal.GetLastWin32Error();
throw new Win32Exception(errorCode);
}
return true;
}
public void Destroy()
{
if (mHookId != 0)
{
Win32.UnhookWindowsHookEx(this.mHookId);
this.mHookId = 0;
}
}
}
}
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Threading;
using System.Reflection;
using System.ComponentModel;
namespace KeyboardHook
{
/**//// <summary>
/// Hook scope
/// </summary>
public enum HookScope
{
Global,
Local
}
public class Win32KeyboardHook
{
/**//// <summary>
/// Occurs when the user presses a key
/// </summary>
public event KeyEventHandler KeyDown;
/**//// <summary>
/// Occurs when the user presses and releases
/// </summary>
public event KeyPressEventHandler KeyPress;
/**//// <summary>
/// Occurs when the user releases a key
/// </summary>
public event KeyEventHandler KeyUp;
int mHookId = 0;
int mThreadId = 0;
HookScope mHookScope = HookScope.Global;
Win32HookType mHookType = Win32HookType.WH_KEYBOARD_LL;
Win32HookProc mKeyboardHookProc;// to avoid "CallbackOnCollectedDelegate dected" exception, inited in constructor
public Win32KeyboardHook()
{
//TO avoid "CallbackOnCollectedDelegate" exception
//should store the delegate in an instance variable
this.mKeyboardHookProc = new Win32HookProc(HookProc);
}
public HookScope Scope
{
get
{
return this.mHookScope;
}
private set
{
this.mHookScope = value;
this.mThreadId = this.IsGlobalScope ? 0 : AppDomain.GetCurrentThreadId();
this.mHookType = this.IsGlobalScope ? Win32HookType.WH_KEYBOARD_LL : Win32HookType.WH_KEYBOARD;
}
}
public bool IsGlobalScope
{
get { return this.mHookScope == HookScope.Global; }
}
protected int HookProc(int nCode, Int32 wParam, IntPtr lParam)
{
if (this.IsGlobalScope)
{
return GlobalHookProc(nCode, wParam, lParam);
}
else
{
return LocalHookProc(nCode, wParam, lParam);
}
}
int GlobalHookProc(int nCode, Int32 wParam, IntPtr lParam)
{
//indicates if any of underlaing events set e.Handled flag
bool handled = false;
//it was ok and someone listens to events
if ((nCode >= 0) && (KeyDown != null || KeyUp != null || KeyPress != null))
{
//read structure KeyboardHookStruct at lParam
Win32KeyboardHookStruct theKeyboardHookStruct = (Win32KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(Win32KeyboardHookStruct));
//raise KeyDown
if (KeyDown != null && (wParam == (int)Win32Messages.WM_KEYDOWN || wParam == (int)Win32Messages.WM_SYSKEYDOWN))
{
Keys keyData = (Keys)theKeyboardHookStruct.vkCode;
KeyEventArgs e = new KeyEventArgs(keyData);
KeyDown(this, e);
handled = handled || e.Handled;
}
// raise KeyPress
if (KeyPress != null && wParam == (int)Win32Messages.WM_KEYDOWN)
{
bool isDownShift = ((Win32.GetKeyState(VirtualKeys.VK_SHIFT) & 0x80) == 0x80 ? true : false);
bool isDownCapslock = (Win32.GetKeyState(VirtualKeys.VK_CAPTIAL) != 0 ? true : false);
byte[] keyState = new byte[256];
Win32.GetKeyboardState(keyState);
byte[] inBuffer = new byte[2];
if (Win32.ToAscii(theKeyboardHookStruct.vkCode,
theKeyboardHookStruct.scanCode,
keyState,
inBuffer,
theKeyboardHookStruct.flags) == 1)
{
char key = (char)inBuffer[0];
if ((isDownCapslock ^ isDownShift) && Char.IsLetter(key)) key = Char.ToUpper(key);
KeyPressEventArgs e = new KeyPressEventArgs(key);
KeyPress(this, e);
handled = handled || e.Handled;
}
}
// raise KeyUp
if (KeyUp != null && (wParam == (int)Win32Messages.WM_KEYUP || wParam == (int)Win32Messages.WM_SYSKEYUP))
{
Keys keyData = (Keys)theKeyboardHookStruct.vkCode;
KeyEventArgs e = new KeyEventArgs(keyData);
KeyUp(this, e);
handled = handled || e.Handled;
}
}
//if event handled in application do not handoff to other listeners
if (handled)
return 1;
else
return Win32.CallNextHookEx(mHookId, nCode, wParam, lParam);
}
int LocalHookProc(int nCode, Int32 wParam, IntPtr lParam)
{
throw new NotImplementedException("Local hook is not implemented!");
}
public bool Create(HookScope scope)
{
if (mHookId != 0)
{
return false;
}
this.Scope = scope;
// set the keyboard hook
this.mHookId = Win32.SetWindowsHookEx(
Convert.ToInt32(this.mHookType),
mKeyboardHookProc,
Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]),
this.mThreadId);
if (this.mHookId == 0)
{
int errorCode = Marshal.GetLastWin32Error();
throw new Win32Exception(errorCode);
}
return true;
}
public void Destroy()
{
if (mHookId != 0)
{
Win32.UnhookWindowsHookEx(this.mHookId);
this.mHookId = 0;
}
}
}
}
Form1.cs, 使用Hook
public partial class Form1 : Form
{
Win32KeyboardHook mHook = new Win32KeyboardHook();
public Form1()
{
InitializeComponent();
mHook.KeyDown += new KeyEventHandler(mHook_KeyDown);
}
private void Form1_Load(object sender, EventArgs e)
{
mHook.Create(HookScope.Global);
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
this.mHook.Destroy();
}
void mHook_KeyDown(object sender, KeyEventArgs e)
{
this.Text = e.KeyCode.ToString();
}
}
public partial class Form1 : Form
{
Win32KeyboardHook mHook = new Win32KeyboardHook();
public Form1()
{
InitializeComponent();
mHook.KeyDown += new KeyEventHandler(mHook_KeyDown);
}
private void Form1_Load(object sender, EventArgs e)
{
mHook.Create(HookScope.Global);
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
this.mHook.Destroy();
}
void mHook_KeyDown(object sender, KeyEventArgs e)
{
this.Text = e.KeyCode.ToString();
}
}