/* ****************************************************************
* author : ShianYuan
* email : a-shyuan@microsoft.com, sayuan@outlook.com
* clr : 4.0.30319.34014
* itemname : KeyboardHook
* history : created by ShianYuan 2014/9/24 14:23:11
* ****************************************************************/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace FirstHook
{
#region 基础类型
/// <summary>
/// Window相关消息
/// </summary>
public class WindowMessage
{
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 WM_MOUSEMOVE = 0x200;
public const int WM_LBUTTONDOWN = 0x201;
public const int WM_RBUTTONDOWN = 0x204;
public const int WM_MBUTTONDOWN = 0x207;
public const int WM_LBUTTONUP = 0x202;
public const int WM_RBUTTONUP = 0x205;
public const int WM_MBUTTONUP = 0x208;
public const int WM_LBUTTONDBLCLK = 0x203;
public const int WM_RBUTTONDBLCLK = 0x206;
public const int WM_MBUTTONDBLCLK = 0x209;
}
/// <summary>
/// Hook类型
/// </summary>
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
}
/// <summary>
///
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct KBDLLHOOKSTRUCT
{
public uint vkCode;
public uint scanCode;
public KBDLLHOOKSTRUCTFlags flags;
public uint time;
public UIntPtr dwExtraInfo;
}
[Flags]
public enum KBDLLHOOKSTRUCTFlags : uint
{
LLKHF_EXTENDED = 0x01,
LLKHF_INJECTED = 0x10,
LLKHF_ALTDOWN = 0x20,
LLKHF_UP = 0x80,
}
/// <summary>
/// 键盘按键事件
/// </summary>
public class HookEventArgs : EventArgs
{
// using Windows.Forms.Keys instead of Input.Key since the Forms.Keys maps
// to the Win32 KBDLLHOOKSTRUCT virtual key member, where Input.Key does not
public Keys Key;
public bool Alt;
public bool Ctrl;
public bool Shift;
public HookEventArgs(UInt32 keyCode)
{
// detect what modifier keys are pressed, using
// Windows.Forms.Control.ModifierKeys instead of Keyboard.Modifiers
// since Keyboard.Modifiers does not correctly get the state of the
// modifier keys when the application does not have focus
this.Key = (Keys)keyCode;
this.Alt = (Control.ModifierKeys & Keys.Alt) != 0;
this.Ctrl = (Control.ModifierKeys & Keys.Control) != 0;
this.Shift = (Control.ModifierKeys & Keys.Shift) != 0;
}
}
/// <summary>
/// 处理Hook的子程序
/// </summary>
/// <param name="code"></param>
/// <param name="wParam"></param>
/// <param name="lParam"></param>
/// <returns></returns>
public delegate int HookProc(int code, IntPtr wParam, IntPtr lParam);
/// <summary>
/// 处理收到的消息句柄
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public delegate void HookEventHandler(object sender, HookEventArgs e);
#endregion
/// <summary>
/// 键盘Hook类
/// </summary>
public class KeyboardHook
{
#region 对外事件接口
/// <summary>
/// 按键按下事件
/// </summary>
public event HookEventHandler KeyDown;
/// <summary>
/// 按键弹出事件
/// </summary>
public event HookEventHandler KeyUp;
#endregion
#region Windows API对象
/// <summary>
/// 安装钩子
/// </summary>
/// <param name="idHook"></param>
/// <param name="lpfn"></param>
/// <param name="hInstance"></param>
/// <param name="threadId"></param>
/// <returns></returns>
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
private static extern int SetWindowsHookEx(int lpcode, HookProc lpfn, IntPtr hInstance, int threadId);
/// <summary>
/// 卸载钩子
/// </summary>
/// <param name="idHook"></param>
/// <returns></returns>
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
private static extern bool UnhookWindowsHookEx(IntPtr idHook);
/// <summary>
/// 继续下一个钩子处理程序
/// </summary>
/// <param name="idHook"></param>
/// <param name="nCode"></param>
/// <param name="wParam"></param>
/// <param name="lParam"></param>
/// <returns></returns>
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
private static extern int CallNextHookEx(IntPtr idHook, int nCode, IntPtr wParam, IntPtr lParam);
/// <summary>
/// 取得当前线程编号
/// </summary>
/// <returns></returns>
[DllImport("kernel32.dll")]
private static extern int GetCurrentThreadId();
/// <summary>
/// 获取扩展模块
/// </summary>
/// <param name="module"></param>
/// <returns></returns>
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
#endregion
#region 私有成员对象
private static IntPtr _hookHandle;
#endregion
/// <summary>
/// 键盘勾子事件
/// </summary>
/// <param name="nCode"></param>
/// <param name="wParam"></param>
/// <param name="lParam"></param>
/// <returns></returns>
private int HookCallback(int code, IntPtr wParam, IntPtr lParam)
{
if (code < 0)
return CallNextHookEx(_hookHandle, code, wParam, lParam);
var dataStruce = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT));
//((int)dataStruce.flags & 0x80) != 0
if (((int)wParam == WindowMessage.WM_KEYUP || (int)wParam == WindowMessage.WM_SYSKEYUP) && this.KeyUp != null)
{
this.KeyUp(this, new HookEventArgs(dataStruce.vkCode));
}
//((int)dataStruce.flags & 0x80) == 0
if (((int)wParam == WindowMessage.WM_KEYDOWN || (int)wParam == WindowMessage.WM_SYSKEYDOWN) && this.KeyDown != null)
{
this.KeyDown(this, new HookEventArgs(dataStruce.vkCode));
if (dataStruce.vkCode == 44) return 0;
}
return -1;
//return CallNextHookEx(_hookHandle, code, wParam, lParam);
}
/// <summary>
/// 安装钩子
/// </summary>
public void Install(HookType hookType, HookProc callBack)
{
if (_hookHandle != IntPtr.Zero)
return;
var _hookFunction = new HookProc(HookCallback);
var pInstance = GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName);
//var pInstance =Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().ManifestModule);
_hookHandle = (IntPtr)SetWindowsHookEx((int)hookType, _hookFunction, pInstance, 0);
//_hookHandle = (IntPtr)SetWindowsHookEx((int)hookType, _hookFunction, IntPtr.Zero, GetCurrentThreadId());
}
/// <summary>
/// 卸载钩子
/// </summary>
public void Uninstall()
{
if (_hookHandle != IntPtr.Zero)
{
UnhookWindowsHookEx(_hookHandle);
_hookHandle = IntPtr.Zero;
}
}
}
}
在安装全局勾子时, 因为获取了错误的窗口句柄导致无法正常的安装键盘勾子,
句柄的获取方式改成了以下的语句
var pInstance = GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName);
安装局部的勾子时,用 Marshal .PtrToStructure读取非托管参数 lParam参数时,会失败, 应该是参数不正确,
估计局部的勾子的lParam参数只是一个整数,因此读成 KBDLLHOOKSTRUCT结构对象时,会报受保护的内存不能访问的错误,具体是否该原因需要进一步证明。