首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# 使用全域鉤子實作螢幕鍵盤(二):滑鼠鉤子結構體與 SendInput API 封裝

     編輯:關於C語言

前言

本文為系列第二篇,定義滑鼠/鍵盤/硬體輸入結構體,封裝 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);
    }
}

結構體設計要點

  • LayoutKind.Sequential:依欄位順序排列,對應 Win32 結構體記憶體佈局。
  • LayoutKind.Explicit + FieldOffset:Input 為聯集,type 決定使用哪個子結構,FieldOffset(4) 使三個子結構共用位移 4 之後的記憶體。
  • cbSize 參數:呼叫 SendInput 時必須傳入 Marshal.SizeOf(typeof(Input)),否則 API 會失敗。

API 封裝說明

API用途
SendInput模擬鍵盤/滑鼠/硬體輸入事件
GetTickCount取得系統啟動後毫秒數,填入 Input.time 欄位
GetKeyState查詢虛擬鍵當前狀態(按下/鎖定)
SendMessage發送視窗訊息,可用於特定控制項互動

常見錯誤與除錯

  • 結構體大小不符:確認 Marshal.SizeOf 與原生 sizeof(INPUT) 一致(32 位為 28 位元組,64 位為 40 位元組)。
  • CharSet 不匹配:統一使用 CharSet.Auto 讓運行期自動選擇 Unicode/ANSI 版本。
  • SetLastError:SendInput 失敗時需 Marshal.GetLastWin32Error() 取得錯誤碼,建議在 DllImport 設定 SetLastError = true。

結語

正確定義輸入結構體與封裝 Win32 API,是實作模擬輸入與全域鉤子的基石。下一篇將介紹鍵盤鉤子類型 WH_KEYBOARD_LL 及其訊息處理邏輯。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved