前四篇已完成全域鍵盤鉤子的安裝、攔截與卸載。本文將示範如何透過 SendInput API 模擬鍵盤輸入,並整合自訂 KeyboardButton 控制項,完成一個功能完整的螢幕鍵盤。
private void SendKeyDown(short vkCode)
{
var input = new Input[1];
input[0].type = INPUT.KEYBOARD;
input[0].ki.wVk = vkCode;
input[0].ki.time = NativeMethods.GetTickCount();
if (NativeMethods.SendInput((uint)input.Length, input, Marshal.SizeOf(input[0])) < input.Length)
throw new Win32Exception(Marshal.GetLastWin32Error());
}
private void SendKeyUp(short vkCode)
{
var input = new Input[1];
input[0].type = INPUT.KEYBOARD;
input[0].ki.wVk = vkCode;
input[0].ki.dwFlags = KeyboardConstaint.KEYEVENTF_KEYUP;
input[0].ki.time = NativeMethods.GetTickCount();
if (NativeMethods.SendInput((uint)input.Length, input, Marshal.SizeOf(input[0])) < input.Length)
throw new Win32Exception(Marshal.GetLastWin32Error());
}
private void ButtonOnClick(object sender, EventArgs e)
{
var btn = sender as KeyboardButton;
if (btn == null) return;
SendKeyCommand(btn);
}
private void SendKeyCommand(KeyboardButton btn)
{
short vk = btn.VKCode;
// 修飾鍵(Shift, Ctrl, Alt)需切換狀態
if (combinationVKButtonsMap.ContainsKey(vk))
{
if (btn.Checked) SendKeyUp(vk);
else SendKeyDown(vk);
}
else
{
// 一般鍵:按下後立即釋放
SendKeyDown(vk);
SendKeyUp(vk);
}
}
[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, KEYBOARD = 1, HARDWARE = 2; }
internal static class NativeMethods
{
[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();
// ... 其他 API 宣告
}
KeyboardButton 繼承 Button,增加 VKCode、Checked 屬性。Appearance 視覺回饋(按下狀態)。Click 事件處理器 ButtonOnClick。SendInput 需正確填寫 cbSize(單一 Input 結構大小),否則呼叫失敗。VKCode 需對應 Keys 列舉值(如 Keys.A = 0x41)。SendInput,需測試相容性。結合低層鉤子攔截與 SendInput 模擬輸入,即可打造功能完整的螢幕鍵盤。本系列涵蓋從理論到實作的完整路徑,可作為無障礙輸入、自動化測試等專案的基礎。