single.php

C#でWin32APIのSendInput関数でキー入力が送信できない場合の対処法

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関数を使って仮想キーの入力を送信したい人の参考になれば幸いです。

スポンサーリンク

最後までご覧いただき、ありがとうございます。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です