Windows Hooks!
windows 钩子背后的概念是您的程序将自己插入操作系统的消息链中,并且可以读取/拦截这些消息。您还可以使用 Windows 挂钩发送输入。
windows hooks api 是一个 C++ API,因此您必须使用一些 DLLImports 来在您的项目中获取这些本地方法。如下所示。这些是您需要接收消息的函数的导入。您还需要导入 SendInput 函数。
//Imports for Windows Hooks operations
[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, IntPtr wParam, IntPtr lParam);
您还需要一些常量和结构。
这些第一个常量将用于告诉 SetWindowsHookEx 函数您分别需要鼠标钩子或键盘钩子
/// <summary>
/// A lower level mouse hook
/// </summary>
public const int WH_MOUSE_LL = 14;
/// <summary>
/// A lower level keyboard hook
/// </summary>
public const int WH_KEYBOARD_LL = 13;
这些常量将用于识别鼠标正在做什么(点击、移动等)
/// <summary>
/// Indicates left mouse button down
/// </summary>
public const int WM_LBUTTONDOWN = 0x0201;
/// <summary>
/// Indicates left mouse button up
/// </summary>
public const int WM_LBUTTONUP = 0x0202;
/// <summary>
/// Indicates right mouse button down
/// </summary>
public const int WM_RBUTTONDOWN = 0x0204;
/// <summary>
/// Indicates the mouse is moving
/// </summary>
public const int WM_MOUSEMOVE = 0x0200;
此枚举将允许您识别键盘输入。还有一些本地方法可以为您进行这种转换。我可能遗漏了一些键,但您可以在 MSDN 上找到更完整的枚举列表。
public enum SystemHotKeys
{
[Description("ENTER key")]
ENTER = 0x0D,
[Description("SPACEBAR")]
SPACE = 0x20,
[Description("DOWN ARROW key")]
DOWN = 0x28,
[Description("Numeric keypad 0 key")]
NUMPAD_ZERO = 0x60,
[Description("Numeric keypad 1 key")]
NUMPAD_ONE = 0x61,
[Description("Numeric keypad 2 key")]
NUMPAD_TWO = 0x62,
[Description("Numeric keypad 3 key")]
NUMPAD_THREE = 0x63,
[Description("Numeric keypad 4 key")]
NUMPAD_FOUR = 0x64,
[Description("Numeric keypad 5 key")]
NUMPAD_FIVE = 0x65,
[Description("Numeric keypad 6 key")]
NUMPAD_SIX = 0x66,
[Description("Numeric keypad 7 key")]
NUMPAD_SEVEN = 0x67,
[Description("Numeric keypad 8 key")]
NUMPAD_EIGHT = 0x68,
[Description("Numeric keypad 9 key")]
NUMPAD_NINE = 0x69,
[Description("F4 key")]
F4 = 0x73,
[Description("F6 key")]
F6 = 0x75
}
public enum WindowsVirtualKey
{
[Description("BACKSPACE key")]
BACKSPACE = 0x08,
[Description("TAB key")]
TAB = 0x09,
[Description("ENTER key")]
ENTER = 0x0D,
[Description("SPACEBAR")]
SPACE = 0x20,
[Description("LEFT ARROW key")]
LEFT = 0x25,
[Description("UP ARROW key")]
UP = 0x26,
[Description("RIGHT ARROW key")]
RIGHT = 0x27,
[Description("DOWN ARROW key")]
DOWN = 0x28,
[Description("0 key")]
ZERO = 0x30,
[Description("1 key")]
ONE = 0x31,
[Description("2 key")]
TWO = 0x32,
[Description("3 key")]
THREE = 0x33,
[Description("4 key")]
FOUR = 0x34,
[Description("5 key")]
FIVE = 0x35,
[Description("6 key")]
SIX = 0x36,
[Description("7 key")]
SEVEN = 0x37,
[Description("8 key")]
EIGHT = 0x38,
[Description("9 key")]
NINE = 0x39,
[Description("A key")]
A = 0x41,
[Description("B key")]
B = 0x42,
[Description("C key")]
C = 0x43,
[Description("D key")]
D = 0x44,
[Description("E key")]
E = 0x45,
[Description("F key")]
F = 0x46,
[Description("G key")]
G = 0x47,
[Description("H key")]
H = 0x48,
[Description("I key")]
I = 0x49,
[Description("J key")]
J = 0x4A,
[Description("K key")]
K = 0x4B,
[Description("L key")]
L = 0x4C,
[Description("M key")]
M = 0x4D,
[Description("N key")]
N = 0x4E,
[Description("O key")]
O = 0x4F,
[Description("P key")]
P = 0x50,
[Description("Q key")]
Q = 0x51,
[Description("R key")]
R = 0x52,
[Description("S key")]
S = 0x53,
[Description("T key")]
T = 0x54,
[Description("U key")]
U = 0x55,
[Description("V key")]
V = 0x56,
[Description("W key")]
W = 0x57,
[Description("X key")]
X = 0x58,
[Description("Y key")]
Y = 0x59,
[Description("Z key")]
Z = 0x5A,
[Description("Numeric keypad 0 key")]
NUMPAD_ZERO = 0x60,
[Description("Numeric keypad 1 key")]
NUMPAD_ONE = 0x61,
[Description("Numeric keypad 2 key")]
NUMPAD_TWO = 0x62,
[Description("Numeric keypad 3 key")]
NUMPAD_THREE = 0x63,
[Description("Numeric keypad 4 key")]
NUMPAD_FOUR = 0x64,
[Description("Numeric keypad 5 key")]
NUMPAD_FIVE = 0x65,
[Description("Numeric keypad 6 key")]
NUMPAD_SIX = 0x66,
[Description("Numeric keypad 7 key")]
NUMPAD_SEVEN = 0x67,
[Description("Numeric keypad 8 key")]
NUMPAD_EIGHT = 0x68,
[Description("Numeric keypad 9 key")]
NUMPAD_NINE = 0x69,
[Description("F1 key")]
F1 = 0x70,
[Description("F2 key")]
F2 = 0x71,
[Description("F3 key")]
F3 = 0x72,
[Description("F4 key")]
F4 = 0x73,
[Description("F5 key")]
F5 = 0x74,
[Description("F6 key")]
F6 = 0x75,
[Description("F7 key")]
F7 = 0x76,
[Description("F8 key")]
F8 = 0x77,
[Description("F9 key")]
F9 = 0x78,
[Description("F10 key")]
F10 = 0x79,
[Description("F11 key")]
F11 = 0x7A,
[Description("F12 key")]
F12 = 0x7B
}
您还需要一些结构来在 C++ API 和您的代码之间进行通信。
//Declare the wrapper managed POINT class.
[StructLayout(LayoutKind.Sequential)]
public class POINT
{
public int x;
public int y;
}
//Declare the wrapper managed MouseHookStruct class.
[StructLayout(LayoutKind.Sequential)]
public class MouseHookStruct
{
public POINT pt;
public int hwnd;
public int wHitTestCode;
public int dwExtraInfo;
}
从那里您可以使用SetWindowsHookEx 设置鼠标或键盘的窗口挂钩。 (一次可以有多个钩子)您将传递一个委托,即您的 HookProcedure。每当触发 Windows 消息时,都会调用此过程。要使钩子成为全局钩子,您需要将IntPtr.Zero 作为最后一个参数传递给SetWindowsHookEx。这个函数返回一个钩子句柄。您需要保存它并稍后使用它来取消挂钩。
要停止钩子,您将调用UnhookWindowsHookEx 并传入钩子句柄。
现在我们已经介绍了通过键盘和鼠标接收消息,我将讨论发送消息。
为此,您将使用来自同一 API 的 SendInput 方法。但是,您将再次需要一些结构来帮助您进行交流。
[StructLayout(LayoutKind.Sequential)]
public struct MOUSEINPUT
{
public int dx;
public int dy;
public uint mouseData;
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
public struct KEYBOARDINPUT
{
public ushort wVk;
public ushort wScan;
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
public struct HARDWAREINPUT
{
public uint uMsg;
public ushort wParamL;
public ushort wParamH;
}
[StructLayout(LayoutKind.Explicit)]
public struct InputUnion
{
[FieldOffset(0)]
public MOUSEINPUT mi;
[FieldOffset(0)]
public KEYBDINPUT ki;
[FieldOffset(0)]
public HARDWAREINPUT hi;
}
[StructLayout(LayoutKind.Sequential)]
public struct INPUT
{
public int type;
public InputUnion union;
}
您将使用您需要发送的信息填充这些结构。将它们组合在 InputUnion 中,然后将它们放入 INPUT 结构中以发送到系统。您或多或少会使用与上面相同的常量来告诉键盘或鼠标发送某些信号的内容和位置。然而,不幸的是,这都是基于 x 和 y 坐标的,因此这些坐标应该始终根据屏幕上应用程序的大小和位置来读取。
Hooking API 参考可以在这里找到:https://msdn.microsoft.com/en-us/library/windows/desktop/ms632589(v=vs.85).aspx
可以在这里找到如何使用钩子的概述:https://msdn.microsoft.com/en-us/library/windows/desktop/ms644960(v=vs.85).aspx
发送输入参考可以在这里找到:https://msdn.microsoft.com/en-us/library/windows/desktop/ms646310(v=vs.85).aspx
我希望这会有所帮助!如果您对我的回答有任何疑问,请随时给我留言。我经常使用 windows hooks。