ChatGPTに、任意のキー入力を送るサンプルプログラムを聞いて、実際に実行させてみる →OK 一発で正常動作

# はじめに

ChatGPTに、プログラムのコードにより任意のキー入力を送るサンプルプログラムを作ってもらい、うまく動作するかを試してみます。

# ChatGPTにサンプルプログラムを聞いてみる

任意のキー入力を送る方法として、下記のWebページを見つけて、keybd_event関数があると、知っていたとします。(あえて旧式の方法)

https://www.inasoft.org/webhelp/mousefr/winapi_keybd_event.html

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606924/5963e64d-84ef-5383-7a78-e3278b3cb09d.png)

## 試行1 (古い関数keybd_event)

しっかりとした質問文章を作成するのも面倒なので、端的に単語を並べるだけにします。

ChatGPTの回答は、端的に単語を並べるだけでも、常識の範囲内で推測して、上手に意味を解釈して貰えました。

**▼私(人間)**
C#、keybd_event、コード例

質問中の画面例: 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606924/7ecb2655-c393-3b16-1381-5e8f758cfc6d.png)

**▼ChatGPT(自動応答)**
以下はC#でkeybd_eventを使用したコード例です。keybd_eventはキーボードのキーをシミュレートするためのWinAPI関数です。
```C#
using System;
using System.Runtime.InteropServices;

class Program
{
    [DllImport("user32.dll")]
    public static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, UIntPtr dwExtraInfo);

    public const int KEYEVENTF_KEYDOWN = 0x0000;
    public const int KEYEVENTF_KEYUP = 0x0002;

    public const byte VK_SPACE = 0x20;
    public const byte VK_A = 0x41;
    public const byte VK_B = 0x42;

    static void Main(string[] args)
    {
        // "A"キーを押す
        keybd_event(VK_A, 0, KEYEVENTF_KEYDOWN, UIntPtr.Zero);

        // "B"キーを押す
        keybd_event(VK_B, 0, KEYEVENTF_KEYDOWN, UIntPtr.Zero);

        // "B"キーを離す
        keybd_event(VK_B, 0, KEYEVENTF_KEYUP, UIntPtr.Zero);

        // "A"キーを離す
        keybd_event(VK_A, 0, KEYEVENTF_KEYUP, UIntPtr.Zero);

        // スペースキーを押す
        keybd_event(VK_SPACE, 0, KEYEVENTF_KEYDOWN, UIntPtr.Zero);

        // スペースキーを離す
        keybd_event(VK_SPACE, 0, KEYEVENTF_KEYUP, UIntPtr.Zero);
    }
}
```
このコードでは、keybd_event関数を使用して"A"キー、"B"キー、およびスペースキーをシミュレートしています。キーの押下と解放を制御するために、KEYEVENTF_KEYDOWNおよびKEYEVENTF_KEYUPフラグを使用しています。各キーは対応する仮想キーコード（VK_A、VK_B、VK_SPACE）で指定されています。

注意: keybd_eventはWindows APIの古い関数であり、現在はSendInputなどの新しい関数が推奨されています。SendInputはより高度なキーボードシミュレーションを提供します。

## 試行2 (新しい関数SendInput)

しっかりと、丁寧に「keybd_eventは古い関数」との注意書きを頂いたので(優秀)、新しい関数SendInput関数の方を検討することにします。

> 注意: keybd_eventはWindows APIの古い関数であり、現在はSendInputなどの新しい関数が推奨されています。

**▼私(人間)**
代わりに、SendInput、コード例

質問中の画面例:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606924/daad8637-4aff-d6f5-1427-8817bceff71b.png)

**▼ChatGPT(自動応答)**
以下はC#でSendInputを使用したキーボードイベントのシミュレーションのコード例です。
```C#
using System;
using System.Runtime.InteropServices;

class Program
{
    [DllImport("user32.dll")]
    public static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);

    [StructLayout(LayoutKind.Sequential)]
    public struct INPUT
    {
        public int type;
        public INPUTUNION data;
    }

    [StructLayout(LayoutKind.Explicit)]
    public struct INPUTUNION
    {
        [FieldOffset(0)]
        public MOUSEINPUT mi;
        [FieldOffset(0)]
        public KEYBDINPUT ki;
        [FieldOffset(0)]
        public HARDWAREINPUT hi;
    }

    public const int INPUT_KEYBOARD = 1;
    public const int KEYEVENTF_KEYDOWN = 0x0000;
    public const int KEYEVENTF_KEYUP = 0x0002;

    [StructLayout(LayoutKind.Sequential)]
    public struct KEYBDINPUT
    {
        public ushort wVk;
        public ushort wScan;
        public uint dwFlags;
        public uint time;
        public IntPtr dwExtraInfo;
    }

    public const ushort VK_SPACE = 0x20;
    public const ushort VK_A = 0x41;
    public const ushort VK_B = 0x42;

    static void Main(string[] args)
    {
        INPUT[] inputs = new INPUT[2];

        // "A"キーを押す
        inputs[0].type = INPUT_KEYBOARD;
        inputs[0].data.ki.wVk = VK_A;
        inputs[0].data.ki.dwFlags = KEYEVENTF_KEYDOWN;

        // "A"キーを離す
        inputs[1].type = INPUT_KEYBOARD;
        inputs[1].data.ki.wVk = VK_A;
        inputs[1].data.ki.dwFlags = KEYEVENTF_KEYUP;

        SendInput(2, inputs, Marshal.SizeOf(typeof(INPUT)));

        // "B"キーを押す
        inputs[0].data.ki.wVk = VK_B;
        inputs[0].data.ki.dwFlags = KEYEVENTF_KEYDOWN;

        // "B"キーを離す
        inputs[1].data.ki.wVk = VK_B;
        inputs[1].data.ki.dwFlags = KEYEVENTF_KEYUP;

        SendInput(2, inputs, Marshal.SizeOf(typeof(INPUT)));

        // スペースキーを押す
        inputs[0].data.ki.wVk = VK_SPACE;
        inputs[0].data.ki.dwFlags = KEYEVENTF_KEYDOWN;

        // スペースキーを離す
        inputs[1].data.ki.wVk = VK_SPACE;
        inputs[1].data.ki.dwFlags = KEYEVENTF_KEYUP;

        SendInput(2, inputs, Marshal.SizeOf(typeof(INPUT)));
    }
}
```
このコードでは、SendInput関数を使用して"A"キー、"B"キー、およびスペースキーのキーボードイベントをシミュレートしています。各キーボードイベントはINPUT構造体で表され、KEYBDINPUT構造体内のwVkとdwFlagsフィールドを設定することでキーの押下と解放を制御しています。

注意: SendInputはWindows APIの関数であり

(回答が長すぎで?...途中で終了)

## 試行3

追加で、テンキー(NumPad0)のキー入力を送る方法も知りたいので、「NumPad0キー入力」を追加して聞いてみます。

**▼私(人間)**
C#、SendInput、NumPad0キー入力、コード例

質問中の画面例:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606924/7903c37a-dedd-ec70-737a-324ae09fc0c5.png)

**▼ChatGPT(自動応答)**
以下はC#でSendInputを使用してNumPad0キーの入力をシミュレートするコード例です。
```C#
using System;
using System.Runtime.InteropServices;

class Program
{
    [DllImport("user32.dll")]
    public static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);

    [StructLayout(LayoutKind.Sequential)]
    public struct INPUT
    {
        public int type;
        public INPUTUNION data;
    }

    [StructLayout(LayoutKind.Explicit)]
    public struct INPUTUNION
    {
        [FieldOffset(0)]
        public MOUSEINPUT mi;
        [FieldOffset(0)]
        public KEYBDINPUT ki;
        [FieldOffset(0)]
        public HARDWAREINPUT hi;
    }

    public const int INPUT_KEYBOARD = 1;
    public const int KEYEVENTF_KEYDOWN = 0x0000;
    public const int KEYEVENTF_KEYUP = 0x0002;

    [StructLayout(LayoutKind.Sequential)]
    public struct KEYBDINPUT
    {
        public ushort wVk;
        public ushort wScan;
        public uint dwFlags;
        public uint time;
        public IntPtr dwExtraInfo;
    }

    public const ushort VK_NUMPAD0 = 0x60;

    static void Main(string[] args)
    {
        INPUT[] inputs = new INPUT[2];

        // NumPad0キーを押す
        inputs[0].type = INPUT_KEYBOARD;
        inputs[0].data.ki.wVk = VK_NUMPAD0;
        inputs[0].data.ki.dwFlags = KEYEVENTF_KEYDOWN;

        // NumPad0キーを離す
        inputs[1].type = INPUT_KEYBOARD;
        inputs[1].data.ki.wVk = VK_NUMPAD0;
        inputs[1].data.ki.dwFlags = KEYEVENTF_KEYUP;

        SendInput(2, inputs, Marshal.SizeOf(typeof(INPUT)));
    }
}
```
このコードでは、SendInput関数を使用してNumPad0キーのキーボードイベントをシミュレートしています。NumPad0キーの押下と解放は、INPUT構造体内のKEYBDINPUT構造体のwVkとdwFlagsフィールドを適切に設定することで制御されています。

注意: SendInputはWindows APIの関数であり、実行環境がWindowsである必要があります。また、このコードを実行するには管理者権限が必要な場合があります。

## 実際に実行させてみる→OK

ChatGPTの回答で得られた、上記のコード例は、実際にビルドして実行してみると、修正の必要なく、完全に期待通りの動作をするものになっており、非常に優秀。「VK_NUMPAD0 = 0x60」の定数の値も正しく、テンキー0が入力されます。

当機能を呼出し易いように、自作関数InputSender.KeyPressとして実装したもの:

https://qiita.com/dl_from_scratch/items/0517eb2eae72ac922bb9


# ChatGPTの代わりにPhindを使用

ChatGPTの代わりにPhindを使用してみます。

https://www.phind.com/

## 試行1

Phindに同じ質問をすると、英語表記で回答が返ってきますが、サンプルコードは殆ど同じものが得られます。

質問中の画面例:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606924/5218f0e9-8363-1822-c1d6-cf5bb9349ae1.png)

**▼私(人間)**
C#、SendInput、NumPad0キー入力、コード例

**▼Phind(自動応答)**
To simulate the input of the NumPad0 key in C#, you can use the SendInput function from the user32.dll library. Here's an example code snippet:
```C#
using System;
using System.Runtime.InteropServices;

public class Program
{
    [DllImport("user32.dll", SetLastError = true)]
    public static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);

    [StructLayout(LayoutKind.Sequential)]
    public struct INPUT
    {
        public uint type;
        public InputUnion u;
    }

    [StructLayout(LayoutKind.Explicit)]
    public struct InputUnion
    {
        [FieldOffset(0)]
        public MOUSEINPUT mi;

        [FieldOffset(0)]
        public KEYBDINPUT ki;

        [FieldOffset(0)]
        public HARDWAREINPUT hi;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct MOUSEINPUT
    {
        public int dx;
        public int dy;
        public uint mouseData;
        public uint dwFlags;
        public uint time;
        public IntPtr dwExtraInfo;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct KEYBDINPUT
    {
        public ushort wVk;
        public ushort wScan;
        public uint dwFlags;
        public uint time;
        public IntPtr dwExtraInfo;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct HARDWAREINPUT
    {
        public uint uMsg;
        public ushort wParamL;
        public ushort wParamH;
    }

    public static void Main()
    {
        INPUT[] inputs = new INPUT[1];
        inputs[0] = new INPUT();
        inputs[0].type = 1; // Keyboard input
        inputs[0].u.ki = new KEYBDINPUT();
        inputs[0].u.ki.wVk = 0x60; // Virtual key code for NumPad0
        inputs[0].u.ki.dwFlags = 0; // Key press

        uint result = SendInput(1, inputs, Marshal.SizeOf(typeof(INPUT)));
        if (result == 0)
        {
            Console.WriteLine("Failed to send input");
        }
    }
}
```
This code uses the SendInput function from the user32.dll library to simulate the input of the NumPad0 key. The SendInput function takes an array of INPUT structures as input, where each structure represents a single input event. In this case, we are sending a single keyboard input event for the NumPad0 key press.

Please note that the virtual key code for the NumPad0 key is 0x60. You can find the virtual key codes for other keys in the [MSDN documentation.](https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes)

This code snippet is based on the following references:

* [stackoverflow.com](https://stackoverflow.com/questions/1021175/sendinput-keyboard-letters-c-c)
* [stackoverflow.com](https://stackoverflow.com/questions/22419038/how-to-use-sendinput-function-c)

Hope this helps! Let me know if you have any further questions.

## 試行2

日本語で表記して貰うように、追加で聞いてみます。

**▼私(人間)**
日本語で表記

**▼Phind(自動応答)**
日本語で表記すると、C#のSendInput関数を使用してNumPad0キーの入力をシミュレートする方法は次のようになります。
```C#
using System;
using System.Runtime.InteropServices;

public class Program
{
    [DllImport("user32.dll", SetLastError = true)]
    public static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);

    [StructLayout(LayoutKind.Sequential)]
    public struct INPUT
    {
        public uint type;
        public InputUnion u;
    }

    [StructLayout(LayoutKind.Explicit)]
    public struct InputUnion
    {
        [FieldOffset(0)]
        public MOUSEINPUT mi;

        [FieldOffset(0)]
        public KEYBDINPUT ki;

        [FieldOffset(0)]
        public HARDWAREINPUT hi;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct MOUSEINPUT
    {
        public int dx;
        public int dy;
        public uint mouseData;
        public uint dwFlags;
        public uint time;
        public IntPtr dwExtraInfo;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct KEYBDINPUT
    {
        public ushort wVk;
        public ushort wScan;
        public uint dwFlags;
        public uint time;
        public IntPtr dwExtraInfo;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct HARDWAREINPUT
    {
        public uint uMsg;
        public ushort wParamL;
        public ushort wParamH;
    }

    public static void Main()
    {
        INPUT[] inputs = new INPUT[1];
        inputs[0] = new INPUT();
        inputs[0].type = 1; // キーボード入力
        inputs[0].u.ki = new KEYBDINPUT();
        inputs[0].u.ki.wVk = 0x60; // NumPad0の仮想キーコード
        inputs[0].u.ki.dwFlags = 0; // キーを押す

        uint result = SendInput(1, inputs, Marshal.SizeOf(typeof(INPUT)));
        if (result == 0)
        {
            Console.WriteLine("入力の送信に失敗しました");
        }
    }
}
```
このコードは、user32.dllライブラリのSendInput関数を使用してNumPad0キーの入力をシミュレートしています。SendInput関数は、INPUT構造体の配列を入力として受け取り、各構造体は単一の入力イベントを表します。この場合、NumPad0キーの押下のために単一のキーボード入力イベントを送信しています。

NumPad0キーの仮想キーコードは0x60です。他のキーの仮想キーコードについては、[MSDNのドキュメント](https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes)を参照してください。

このコードは以下の参考文献を基にしています：

* [slash-mochi.net](https://stackoverflow.com/questions/1021175/sendinput-keyboard-letters-c-c)
* [daniweb.com](https://stackoverflow.com/questions/22419038/how-to-use-sendinput-function-c)

もしご質問がありましたらお知らせください。お手伝いできることを願っています。

## 実際に実行させてみる→OK

Phindの回答で得られた、上記のコード例は、実際にビルドして実行してみると、修正の必要なく、完全に期待通りの動作をするものになっており、非常に優秀。「inputs[0].u.ki.wVk = 0x60」の定数の値も正しく、テンキー0が入力されます。


#   参考

##   ChatGPTの始め方

記事: 【無料】チャットGPTの始め方を解説！

https://www.minotake-gadget.net/chatgpt-howto/

##   ChatGPTバージョン

ChatGPT Mar 14 Version. Free Research Preview.