本文為系列第二篇,定義滑鼠/鍵盤/硬體輸入結構體,封裝 SendInput、GetTickCount、GetKeyState 等 Win32 API,為後續模擬輸入奠基。
namespace CnBlogs.Youzai.ScreenKeyboard
{
[StructLayout(LayoutKind.Sequential)]
internal struct MOUSEINPUT
{
public int dx, dy, mouseData, dwFlags, time;
public IntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
internal struct KEYBDINPUT
{
public short wVk, wScan;
public int dwFlags, time;
public IntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Explicit)]
internal struct Input
{
[FieldOffset(0)] public int type;
[FieldOffset(4)] public MOUSEINPUT mi;
[FieldOffset(4)] public KEYBDINPUT ki;
[FieldOffset(4)] public HARDWAREINPUT hi;
}
[StructLayout(LayoutKind.Sequential)]
internal struct HARDWAREINPUT
{
public int uMsg;
public short wParamL, wParamH;
}
internal static class INPUT
{
public const int MOUSE = 0;
public const int KEYBOARD = 1;
public const int HARDWARE = 2;
}
internal static class NativeMethods
{
[DllImport("User32.dll", CharSet = CharSet.Auto, SetLastError = false)]
internal static extern IntPtr GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("User32.dll", CharSet = CharSet.Auto, SetLastError = false)]
internal static extern IntPtr SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("User32.dll", EntryPoint = "SendInput", CharSet = CharSet.Auto)]
internal static extern UInt32 SendInput(UInt32 nInputs, Input[] pInputs, Int32 cbSize);
[DllImport("Kernel32.dll", EntryPoint = "GetTickCount", CharSet = CharSet.Auto)]
internal static extern int GetTickCount();
[DllImport("User32.dll", EntryPoint = "GetKeyState", CharSet = CharSet.Auto)]
internal static extern short GetKeyState(int nVirtKey);
[DllImport("User32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Auto)]
internal static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
}
}
Input 為聯集,type 決定使用哪個子結構,FieldOffset(4) 使三個子結構共用位移 4 之後的記憶體。SendInput 時必須傳入 Marshal.SizeOf(typeof(Input)),否則 API 會失敗。| API | 用途 |
|---|---|
SendInput | 模擬鍵盤/滑鼠/硬體輸入事件 |
GetTickCount | 取得系統啟動後毫秒數,填入 Input.time 欄位 |
GetKeyState | 查詢虛擬鍵當前狀態(按下/鎖定) |
SendMessage | 發送視窗訊息,可用於特定控制項互動 |
Marshal.SizeOf 與原生 sizeof(INPUT) 一致(32 位為 28 位元組,64 位為 40 位元組)。CharSet.Auto 讓運行期自動選擇 Unicode/ANSI 版本。SendInput 失敗時需 Marshal.GetLastWin32Error() 取得錯誤碼,建議在 DllImport 設定 SetLastError = true。正確定義輸入結構體與封裝 Win32 API,是實作模擬輸入與全域鉤子的基石。下一篇將介紹鍵盤鉤子類型 WH_KEYBOARD_LL 及其訊息處理邏輯。