はじめに
(マルチバイト)文字列、または、仮想キーコードを送信して、キー操作をシミュレート(擬似的に操作)するサンプルコード。
任意の他のウィンドウにフォーカスを移せば、アプリケーションを越えて(別プロセスの)ウィンドウへキー入力が可能。自・他(任意)のアプリケーションに対してキーボード入力を送信できます。これにより、自・他(任意)のアプリケーションの自動化やリモート操作等が可能です。
キーワード: キー操作シミュレート, 仮想キーコード送信, Win32 API(user32.dll内) SendInput
処理方法
主要部分は、
- Win32 API(user32.dll内)のSendInput関数を呼び出して、任意のキー入力を送信
- Input構造体の中のScanCode、または、VirtualKeyで送信するキー内容を指定:
- 文字列を送信する場合は、ScanCodeに(マルチバイト)文字コードを代入し、
- 仮想キーコードを送信する場合は、VirtualKeyに仮想キーコードを代入 (仮想キーコードは特殊キーも含めて細かく指定可能)
- 主要機能は自作関数InputSender.KeyPressとして実装
実装+テストコード
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace WindowsFormsApp1 {
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
// sec: test code
// ボタンをクリックしてテスト開始
private void button1_Click(object sender, EventArgs e) {
Test_KeyPress_Basic(textBox1);
Test_KeyPress_ToOtherApp();
}
// キー入力の送信テスト
internal static void Test_KeyPress_Basic(TextBox textBox) {
// 結果: TextBoxへの入力OK: abcあいう醤油★☺<改行>ちzつ
// ・ScanCodeの指定時は、全角モード時でも"abc"と入力が入る。
// ・仮想キー指定時は、全角モード時で、Aと送ると「ち」と入力が入る。Enterを送ると、変換が確定となる。
// ・全角/半角キーを送ると、全角/半角が切り替わる。
Console.WriteLine("▼全角/半角モードでコードからキー入力を送る");
textBox.Focus();
textBox.ImeMode = ImeMode.Hiragana; // 全角モードに強制
// ScanCodeの指定時
InputSender.KeyPress("abc"); // ScanCodeの指定時は、全角モード時でも"abc"と入力が入る。
InputSender.KeyPress("あいう醤油★☺\n"); // 全角文字を直接送信可
// 仮想キー指定時
InputSender.KeyPress(new[] { InputSender.VK_A, InputSender.VK_RETURN }); // 仮想キー指定時は、全角モード時で、Aと送ると「ち」と入力が入る。Enterを送ると、変換が確定となる。
InputSender.KeyPress(new[] { InputSender.VK_KANA, InputSender.VK_Z, InputSender.VK_RETURN }); // 全角/半角キーを送ると、全角/半角が切り替わる。
InputSender.KeyPress(new[] { InputSender.VK_KANA, InputSender.VK_Z, InputSender.VK_RETURN });
}
// 他アプリへのキー入力の送信テスト
internal static async void Test_KeyPress_ToOtherApp() {
// 結果: 他アプリへの入力OK: abc0ちわち
// ・他のアプリにフォーカスを移せば、アプリを越えて(別プロセスの)ウィンドウへキー入力が可能。
Console.WriteLine("▼他のアプリ(別プロセス)にキー入力を送る");
Console.WriteLine("他のアプリにフォーカスを移して、全角モードで待機して下さい。");
await System.Threading.Tasks.Task.Delay(3000);
InputSender.KeyPress("abc"); // 他のアプリにフォーカスを移せば、アプリを越えて(別プロセスの)ウィンドウへキー入力が可能。
InputSender.KeyPress(new[] { InputSender.VK_NUMPAD0, InputSender.VK_A });
InputSender.KeyPress(new[] { InputSender.VK_0, InputSender.VK_A });
}
}
// sec: キー入力を送信
// (マルチバイト)文字列、または、仮想キーコードを送信して、キー操作をシミュレート(擬似的に操作)
public static class InputSender {
// 他のアプリにフォーカスを移せば、アプリを越えて(別プロセスの)ウィンドウへキー入力可能
// (マルチバイト)文字列のキー入力を送信 (ScanCodeの指定となる)
public static void KeyPress(string text) {
Input[] inputs = new Input[2 * text.Length];
for (var i_char = 0; i_char < text.Length; i_char++) {
var val_sc = (short)text[i_char];
var i_in = 2 * i_char;
inputs[i_in] = new Input();
inputs[i_in].Type = INPUT_KEYBOARD;
inputs[i_in].ui.Keyboard.ScanCode = val_sc;
inputs[i_in].ui.Keyboard.Flags = KEYEVENTF_UNICODE | KEYEVENTF_KEYDOWN;
i_in = 2 * i_char + 1;
inputs[i_in] = new Input();
inputs[i_in].Type = INPUT_KEYBOARD;
inputs[i_in].ui.Keyboard.ScanCode = val_sc;
inputs[i_in].ui.Keyboard.Flags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP;
}
InputSender.SendInput(inputs.Length, inputs, Marshal.SizeOf<Input>());
}
public static void KeyPress(char text) => InputSender.KeyPress(text.ToString());
// 仮想キーコードを送信 (VirtualKeyの指定となる)
public static void KeyPress(short[] vkeys) {
var inputs = new Input[2 * vkeys.Length];
for (var i_vkey = 0; i_vkey < vkeys.Length; i_vkey++) {
var val_vk = vkeys[i_vkey];
var i_in = 2 * i_vkey;
inputs[i_in] = new Input();
inputs[i_in].Type = INPUT_KEYBOARD;
inputs[i_in].ui.Keyboard.VirtualKey = val_vk;
inputs[i_in].ui.Keyboard.Flags = KEYEVENTF_KEYDOWN;
i_in = 2 * i_vkey + 1;
inputs[i_in] = new Input();
inputs[i_in].Type = INPUT_KEYBOARD;
inputs[i_in].ui.Keyboard.VirtualKey = val_vk;
inputs[i_in].ui.Keyboard.Flags = KEYEVENTF_KEYUP;
}
InputSender.SendInput(inputs.Length, inputs, Marshal.SizeOf<Input>());
}
public static void KeyPress(short vkey) => InputSender.KeyPress(new[] { vkey });
// sec: BeginInvoke
// [不要?] このWinアプリのスレッド上で呼出しを実施
//public static void BeginInvoke(this Control control, Action func) => control.BeginInvoke(func);
// 使用方法: this.BeginInvoke(() => { SendInput.KeyPress("abc"); }); 等
// sec: 仮想キー値
// ref: 仮想キーの状態
// http://wisdom.sakura.ne.jp/system/winapi/win32/win32.html
// 必要な分を下記に適宜もっと追加
public const short VK_A = 0x41;
public const short VK_Z = 0x5A;
public const short VK_0 = 0x30;
public const short VK_9 = 0x39;
public const short VK_RETURN = 0x0D; // Enter
public const short VK_SHIFT = 0x10; // SHIFTキー
public const short VK_KANA = 0x15;
public const short VK_ESCAPE = 0x1B;
public const short VK_SPACE = 0x20;
public const short VK_NUMPAD0 = 0x60; // テンキー
public const short VK_NUMPAD9 = 0x69;
public const short VK_MULTIPLY = 0x6A; // テンキー *
public const short VK_ADD = 0x6B; // テンキー +
public const short VK_SEPARATOR = 0x6C;
public const short VK_SUBTRACT = 0x6D; // テンキー -
public const short VK_DECIMAL = 0x6E; // テンキー .
public const short VK_DIVIDE = 0x6F; // テンキー /
// sec: Win API
// ref: C# - SendInputでマルチバイト文字列を送信する
// https://qiita.com/kob58im/items/60abf22e6ce9a3a0ed26
// キー操作、マウス操作をシミュレート(擬似的に操作する)
[DllImport("user32.dll", SetLastError = true)]
public extern static void SendInput(int nInputs, Input[] pInputs, int cbsize);
//[DllImport("user32.dll", SetLastError = true)]
//public extern static IntPtr GetMessageExtraInfo();
public const int INPUT_MOUSE = 0; // マウスイベント
public const int INPUT_KEYBOARD = 1; // キーボードイベント
public const int INPUT_HARDWARE = 2; // ハードウェアイベント
public const int MOUSEEVENTF_MOVE = 0x1; // マウスを移動する
public const int MOUSEEVENTF_ABSOLUTE = 0x8000; // 絶対座標指定
public const int MOUSEEVENTF_LEFTDOWN = 0x2; // 左 ボタンを押す
public const int MOUSEEVENTF_LEFTUP = 0x4; // 左 ボタンを離す
public const int MOUSEEVENTF_RIGHTDOWN = 0x8; // 右 ボタンを押す
public const int MOUSEEVENTF_RIGHTUP = 0x10; // 右 ボタンを離す
public const int MOUSEEVENTF_MIDDLEDOWN = 0x20; // 中央ボタンを押す
public const int MOUSEEVENTF_MIDDLEUP = 0x40; // 中央ボタンを離す
public const int MOUSEEVENTF_WHEEL = 0x800; // ホイールを回転する
public const int WHEEL_DELTA = 120; // ホイール回転値
public const int KEYEVENTF_EXTENDEDKEY = 0x0001; // 拡張コード
public const int KEYEVENTF_KEYDOWN = 0x0000; // キーを押す
public const int KEYEVENTF_KEYUP = 0x0002; // キーを離す
public const int KEYEVENTF_SCANCODE = 0x0008;
public const int KEYEVENTF_UNICODE = 0x0004;
// ▼case: 変換する場合
// 仮想キーコードをスキャンコードに変換
//[DllImport("user32.dll", EntryPoint = "MapVirtualKeyA")]
//private extern static int MapVirtualKey(
// int wCode, int wMapType);
// public const int MAPVK_VK_TO_VSC = 0;
// public const int MAPVK_VSC_TO_VK = 1;
// ▲case: end
[StructLayout(LayoutKind.Sequential)]
public struct MouseInput {
public int X;
public int Y;
public int Data;
public int Flags;
public int Time;
public IntPtr ExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
public struct KeyboardInput {
public short VirtualKey;
public short ScanCode;
public int Flags;
public int Time;
public IntPtr ExtraInfo;
}
// VirtualKey・ScanCodeは、winuser.hのKEYBDINPUT構造体では、WORD(16ビット符号なし整数、unsigned short型/2バイト)の定義=ushort
// https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/ns-winuser-keybdinput
// https://learn.microsoft.com/ja-jp/windows/win32/winprog/windows-data-types#word
[StructLayout(LayoutKind.Sequential)]
public struct HardwareInput {
public int uMsg;
public short wParamL;
public short wParamH;
}
[StructLayout(LayoutKind.Sequential)]
public struct Input {
public int Type;
public InputUnion ui;
}
[StructLayout(LayoutKind.Explicit)]
public struct InputUnion {
[FieldOffset(0)]
public MouseInput Mouse;
[FieldOffset(0)]
public KeyboardInput Keyboard;
[FieldOffset(0)]
public HardwareInput Hardware;
}
}
}