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

C# 實作全域鍵盤鉤子打造螢幕鍵盤

     編輯:關於C#

螢幕鍵盤需攔截系統層級鍵盤訊息,無論視窗是否具焦點皆能運作,核心技術是 低階鍵盤鉤子。本文完整解析鉤子原理、P/Invoke 封裝、安裝/卸載流程、虛擬按鍵模擬與 UI 焦點管理。

鉤子基礎知識

  • WH_KEYBOARD_LL (13):低階鍵盤鉤子,攔截執行緒訊息佇列中的鍵盤訊息,適合全域監聽。
  • WH_MOUSE_LL (14):低階滑鼠鉤子,同理可全域監聽滑鼠事件。
  • 全域鉤子需放在 DLL 中,但 C# 透過 SetWindowsHookEx 傳入當前模組控制代碼與線程 ID 0 即可實現進程外全域鉤子。

核心 P/Invoke 封裝

internal static class NativeMethods {
    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    internal static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, uint dwThreadId);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    internal static extern bool UnhookWindowsHookEx(IntPtr hhk);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    internal static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

    [DllImport("user32.dll", EntryPoint = "SendInput", CharSet = CharSet.Auto)]
    internal static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);

    [DllImport("user32.dll", EntryPoint = "GetKeyState", CharSet = CharSet.Auto)]
    internal static extern short GetKeyState(int nVirtKey);
    // ... GetWindowLong, SetWindowLong, SendMessage, GetTickCount 等
}

安裝與卸載鉤子

private IntPtr hKeyboardHook;
private HookProc KeyboardHookProcedure;

public void Start(bool installKeyboardHook) {
    if (hKeyboardHook == IntPtr.Zero && installKeyboardHook) {
        KeyboardHookProcedure = KeyboardHookProc;
        hKeyboardHook = NativeMethods.SetWindowsHookEx(
            13, // WH_KEYBOARD_LL
            KeyboardHookProcedure,
            Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]),
            0);
        if (hKeyboardHook == IntPtr.Zero)
            throw new Win32Exception(Marshal.GetLastWin32Error());
    }
}

public void Stop() {
    if (hKeyboardHook != IntPtr.Zero) {
        NativeMethods.UnhookWindowsHookEx(hKeyboardHook);
        hKeyboardHook = IntPtr.Zero;
    }
}

虛擬按鍵模擬

透過 SendInput 發送 KeyDown/KeyUp:

private void SendKeyDown(short vk) {
    var input = new INPUT { type = 1, ki = new KEYBDINPUT { wVk = vk, time = NativeMethods.GetTickCount() } };
    if (NativeMethods.SendInput(1, new[] { input }, Marshal.SizeOf(input)) == 0)
        throw new Win32Exception(Marshal.GetLastWin32Error());
}

private void SendKeyUp(short vk) {
    var input = new INPUT { type = 1, ki = new KEYBDINPUT { wVk = vk, dwFlags = 0x0002, time = NativeMethods.GetTickCount() } };
    NativeMethods.SendInput(1, new[] { input }, Marshal.SizeOf(input));
}

UI 焦點管理:點擊標題列才啟用

螢幕鍵盤視窗預設不可獲焦點(WS_EX_NOACTIVATE),僅在使用者點擊標題列時暫時移除該樣式以允許拖曳:

private void HookOnMouseActivity(object sender, MouseEventArgs e) {
    if (e.Button == MouseButtons.Left) {
        var captionRect = new Rectangle(Location, new Size(Width, SystemInformation.CaptionHeight));
        if (captionRect.Contains(e.Location)) {
            // 允許獲焦點
            NativeMethods.SetWindowLong(Handle, -20, 
                NativeMethods.GetWindowLong(Handle, -20).ToInt32() & ~0x08000000);
            NativeMethods.SendMessage(Handle, 0x0007, IntPtr.Zero, IntPtr.Zero);
        } else {
            // 禁止獲焦點
            NativeMethods.SetWindowLong(Handle, -20,
                NativeMethods.GetWindowLong(Handle, -20).ToInt32() | 0x08000000);
        }
    }
}

虛擬鍵碼常數表

文中附贈完整 KeyboardConstaint 類別,涵蓋 F1-F12、方向鍵、修飾鍵、數字鍵盤、OEM 鍵等 90+ 常數,開箱即用。

編譯為 DLL 後,主程式訂閱 KeyDown/KeyUp 事件即可接收全域按鍵,結合自訂按鈕控制項與虛擬按鍵發送,即可打造功能完整的螢幕鍵盤。

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