C#のWinFormsプロジェクトなどで、Win32 APIのSendInput関数を利用して仮想キーの入力を実行してもWindows側でキー入力されない場合の対処法を備忘録に投稿します。
SendInputで送信
例えば、Win32 APIの[SendInput]関数を使うと、キーボードのキー情報を送信ができます。
public const int INPUT_KEYBOARD = 1;
public const uint KEYEVENTF_SCANCODE = 0x0008;
public const uint KEYEVENTF_KEYUP = 0x0002;
[DllImport("user32.dll")]
public static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);
[DllImport("user32.dll")]
public static extern uint MapVirtualKey(uint uCode, uint uMapType);
[StructLayout(LayoutKind.Sequential)]
struct INPUT
{
public int type;
public InputUnion U;
}
[StructLayout(LayoutKind.Explicit)]
struct InputUnion
{
[FieldOffset(0)]
public KEYBDINPUT ki;
}
[StructLayout(LayoutKind.Sequential)]
struct KEYBDINPUT
{
public ushort wVk;
public ushort wScan;
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}
public static void SendKey(byte vk, bool keyUp = false)
{
ushort scan = (ushort)MapVirtualKey(vk, 4);
uint flags = KEYEVENTF_SCANCODE;
if (keyUp)
{
flags |= KEYEVENTF_KEYUP;
}
var input = new INPUT
{
type = INPUT_KEYBOARD,
U = new InputUnion
{
ki = new KEYBDINPUT
{
wVk = 0,
wScan = scan,
dwFlags = flags
}
}
};
SendInput(1, new[] { input }, Marshal.SizeOf(typeof(INPUT)));
}
上記のコードはビルドは正常に完了しますが、実行しても[SendKey]プロシージャーでキー情報を送信しても、何も起こりません。
例えば、メモ帳をアクティブにした状態で、SendKeyプロシージャーの引数に”0x41″を指定して実行すると文字列 “a” が入力されるはずですが、実際には何も入力が有りません。
SendKey(0x41, false);
Thread.Sleep(10);
SendKey(0x41, true);
InputUnion構造体が原因
上記のコードで動作しないのは、InputUnion構造体に必要な構造体が足らないのが原因です。
[SendKey]プロシージャーに次のコードを追加して構造体のサイズを出力してみます。
Debug.WriteLine(Marshal.SizeOf(typeof(INPUT)));
この時、INPUT構造体に必要なサイズは、Windows11が64ビット版の場合は 40バイト、32ビット版の場合は28バイトになります。
これ以外の数値が出力された場合は、InputUnion構造体で定義している構造体が不足している可能性があります。
そもそも、InputUnion構造体にはキーボードの他にマウスやその他のハードウェアからの入力を想定した構造体があり、利用しなくても、すべての構造を設定しないと動作しません。
コードにすると、こんな感じです。
[StructLayout(LayoutKind.Sequential)]
struct INPUT
{
public int type;
public InputUnion U;
}
[StructLayout(LayoutKind.Explicit)]
struct InputUnion
{
[FieldOffset(0)]
public KEYBDINPUT ki;
[FieldOffset(0)]
public MOUSEINPUT mi;
[FieldOffset(0)]
public HARDWAREINPUT hi;
}
[StructLayout(LayoutKind.Sequential)]
struct KEYBDINPUT
{
public ushort wVk;
public ushort wScan;
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
struct MOUSEINPUT
{
public int dx;
public int dy;
public uint mouseData;
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
struct HARDWAREINPUT
{
public uint uMsg;
public ushort wParamL;
public ushort wParamH;
}
私は必要な構造体だけ設定してハマりましたが、SendInput関数の引数に利用する[InputUnion]構造体は、必要な構造体をすべて設定しないと正しく動作しません。
まとめ
今回は短い記事ですがC#のWinFormsプロジェクトなどで、Win32 APIのSendInput関数を利用して仮想キーの入力を実行してもWindows側でキー入力されない場合の対処法について書きました。
ビルド時にエラーなどは表示されませんが、Win32 API の SendInput 関数を利用する場合には引数で利用する[InputUnion]構造体に、必要な構造体をすべて設定しないと正しく動作しません。
WinFormsプロジェクトでSendInput関数を使って仮想キーの入力を送信したい人の参考になれば幸いです。
スポンサーリンク
最後までご覧いただき、ありがとうございます。
