93632


SendInputが動作せず悩んでいます。  
環境はVisualStudio2022、C#コンソールアプリ、.Net7.0です。

主に以下のＨＰを参考に他、C#でSendInputを扱ったＨＰを見ながら作成しました。

<https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-
sendinput>

<https://social.msdn.microsoft.com/Forums/ja-
JP/f1df67f9-33a2-4293-bfe3-46d2810846f3/cwin32apinotepadpostmessagecontrol-a?forum=windowsgeneraldevelopmentissuesja>

エラーコードからパラメータの指定が間違っているのではないかと思いますが行き詰まっています。  
アドバイスよろしくお願いいたします。

※メモ帳にctrl+vを送信しているため、動作にはメモ帳を起動し何かctrl+cをしておく必要があります。

[code]

    using System.Runtime.InteropServices;
    
    internal class Program
    {
        #pragma warning disable IDE0051
        private const int INPUT_MOUSE = 0;
        private const int INPUT_KEYBOARD = 1;
        private const int INPUT_HARDWARE = 2;
    
        private const int MOUSEEVENTF_MOVE = 0x1;
        private const int MOUSEEVENTF_ABSOLUTE = 0x8000;
        private const int MOUSEEVENTF_LEFTDOWN = 0x2;
        private const int MOUSEEVENTF_LEFTUP = 0x4;
        private const int MOUSEEVENTF_RIGHTDOWN = 0x8;
        private const int MOUSEEVENTF_RIGHTUP = 0x10;
        private const int MOUSEEVENTF_MIDDLEDOWN = 0x20;
        private const int MOUSEEVENTF_MIDDLEUP = 0x40;
        private const int MOUSEEVENTF_WHEEL = 0x800;
        private const int WHEEL_DELTA = 120;
    
        private const short VK_CONTROL = 0x11;
        private const short VK_V = (short)'V';
    
        private const int KEYEVENTF_KEYDOWN = 0x0;
        private const int KEYEVENTF_KEYUP = 0x2;
        private const int KEYEVENTF_EXTENDEDKEY = 0x1;
        #pragma warning restore IDE0051
    
        [StructLayout(LayoutKind.Sequential)]
        private struct MOUSEINPUT
        {
            public int dx;
            public int dy;
            public int mouseData;
            public int dwFlags;
            public int time;
            public int dwExtraInfo;
        };
    
        [StructLayout(LayoutKind.Sequential)]
        private struct KEYBDINPUT
        {
            public short wVk;
            public short wScan;
            public int dwFlags;
            public int time;
            public int dwExtraInfo;
        };
    
        [StructLayout(LayoutKind.Sequential)]
        private struct HARDWAREINPUT
        {
            public int uMsg;
            public short wParamL;
            public short wParamH;
        };
    
        [StructLayout(LayoutKind.Explicit)]
        private struct INPUT
        {
            [FieldOffset(0)] public int type;
            [FieldOffset(4)] public MOUSEINPUT mi;
            [FieldOffset(4)] public KEYBDINPUT ki;
            [FieldOffset(4)] public HARDWAREINPUT hi;
        };
    
        private static class NativeMethods
        {
            #pragma warning disable SYSLIB1054
            [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
            public static extern IntPtr FindWindow(string lpClassName, string? lpWindowName);
    
            [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
            public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildafter, string lpszClass, string? lpszWindow);
    
            [DllImport("user32.dll", SetLastError = true)]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool SetForegroundWindow(IntPtr hWnd);
    
            [DllImport("user32.dll", SetLastError = true)]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool SetFocus(IntPtr hWnd);
    
            [DllImport("user32.dll", EntryPoint = "MapVirtualKeyA", SetLastError = true)]
            public static extern int MapVirtualKey(int wCode, int wMapType);
    
            [DllImport("user32.dll", SetLastError = true)]
            public static extern uint SendInput(int nInputs, ref INPUT[] pInputs, int cbsize);
            #pragma warning restore SYSLIB1054
        }
    
        private static void Main()
        {
            int i = 0;
            uint rtnCode;
            int errCode;
    
            IntPtr hWnd1, hWnd2;
            hWnd1 = NativeMethods.FindWindow("Notepad", null);
            hWnd2 = NativeMethods.FindWindowEx(hWnd1, IntPtr.Zero, "Edit", null);
            NativeMethods.SetForegroundWindow(hWnd1);
            NativeMethods.SetFocus(hWnd2);
    
            INPUT defaultInput = new();
            INPUT[] inputs = new INPUT[4];
    
            defaultInput.type = INPUT_KEYBOARD;
            defaultInput.ki.wVk = 0x0;
            defaultInput.ki.wScan = 0; // (short)NativeMethods.MapVirtualKey(defaultInput.ki.wVk, 0);
            defaultInput.ki.dwFlags = KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP;
            defaultInput.ki.dwExtraInfo = 0;
            defaultInput.ki.time = 0;
    
            inputs[0] = defaultInput;
            inputs[0].ki.wVk = VK_CONTROL;
            inputs[0].ki.dwFlags = KEYEVENTF_KEYDOWN;
    
            inputs[1] = defaultInput;
            inputs[1].ki.wVk = VK_V;
            inputs[1].ki.dwFlags = KEYEVENTF_KEYDOWN;
    
            inputs[2] = defaultInput;
            inputs[2].ki.wVk = VK_V;
            inputs[2].ki.dwFlags = KEYEVENTF_KEYUP;
    
            inputs[3] = defaultInput;
            inputs[3].ki.wVk = VK_CONTROL;
            inputs[3].ki.dwFlags = KEYEVENTF_KEYUP;
    
            while (true)
            {
                Thread.Sleep(2000);
                rtnCode = NativeMethods.SendInput(4, ref inputs, Marshal.SizeOf(inputs[0]));
                errCode = Marshal.GetLastWin32Error();
                Console.WriteLine("{0} : rtn({1}) / err({2})", ++i, rtnCode, errCode);
            }
        }
    }
    
[/code]


CC BY-SA 4.0
2023-01-31T02:34:15.783
93631
2023-01-31T22:28:00.767
42735
question
c# winapi
C#コンソールアプリでSendInputを扱いたい


まとめておきます。

(1) SendInput の第２引数

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

udaken さんのご指摘にあるように、第２引数は ref を外してください。

(2) INPUT 構造体

[code]

    typedef struct tagINPUT {
      DWORD type;
      union {
        MOUSEINPUT    mi;
        KEYBDINPUT    ki;
        HARDWAREINPUT hi;
      } DUMMYUNIONNAME;
    } INPUT, *PINPUT, *LPINPUT
    
[/code]

32 ビットだと、type のすぐあとに MOUSEINPUT や KEYBDINPUT 等が配置されますが、  
64 ビットだと 64ビット境界に配置（アライメント）されるため、空き領域ができます。  
aroma さんのソースでは、FieldOffset(4) → FieldOffset(8) となります。

.NET のソースでは

[code]

    [StructLayout(LayoutKind.Sequential)]
    public struct INPUT
    {
        public int type;
        public INPUTUNION inputUnion;
    }
    
    [StructLayout(LayoutKind.Explicit)]
    public struct INPUTUNION
    {
        [FieldOffset(0)]
        public MOUSEINPUT mi;
        [FieldOffset(0)]
        public KEYBDINPUT ki;
        [FieldOffset(0)]
        public HARDWAREINPUT hi;
    }
    
[/code]

のようにアライメントを考慮した記述になっています。


CC BY-SA 4.0
2023-01-31T05:44:11.590
93632
2023-01-31T05:44:11.590
50741
93631
answer


動かなかった原因は、おそらくKOZさんの回答の通りだと思います。  
今は、[CsWin32](https://www.nuget.org/packages/Microsoft.Windows.CsWin32/)
でP/Invokeコードを自動生成できるので、そちらを使用した方がマーシャリングや構造体の作り方等であまり悩まずに済むと思います。  
NativeMethods.txtに使用するAPIを記述するだけで、関連する構造体・定数も自動生成されるので、かなり楽です。

[NativeMethods.txt]

[code]

    SendInput
    FindWindow
    FindWindowEx
    SetForegroundWindow
    SetFocus
    
[/code]

[CsWin32を使用して修正したソース]

[code]

    using System;
    using System.Runtime.InteropServices;
    using System.Runtime.Versioning;
    using System.Threading;
    using Windows.Win32.Foundation;
    using Windows.Win32.UI.Input.KeyboardAndMouse;
    using static Windows.Win32.PInvoke;
    
    internal class Program
    {
        [SupportedOSPlatform("windows5.0")]
        private static void Main()
        {
            int i = 0;
    
            var hWnd1 = FindWindow("Notepad", null);
            var hWnd2 = FindWindowEx(hWnd1, HWND.Null, "Edit", null);
            SetForegroundWindow(hWnd1);
            SetFocus(hWnd2);
    
            INPUT defaultInput = new();
            INPUT[] inputs = new INPUT[4];
    
            defaultInput.type = INPUT_TYPE.INPUT_KEYBOARD;
            defaultInput.Anonymous.ki.wVk = 0x0;
            defaultInput.Anonymous.ki.wScan = 0;
            defaultInput.Anonymous.ki.dwFlags = KEYBD_EVENT_FLAGS.KEYEVENTF_EXTENDEDKEY | KEYBD_EVENT_FLAGS.KEYEVENTF_KEYUP;
            defaultInput.Anonymous.ki.dwExtraInfo = 0;
            defaultInput.Anonymous.ki.time = 0;
    
            inputs[0] = defaultInput;
            inputs[0].Anonymous.ki.wVk = VIRTUAL_KEY.VK_CONTROL;
            inputs[0].Anonymous.ki.dwFlags = 0;
    
            inputs[1] = defaultInput;
            inputs[1].Anonymous.ki.wVk = VIRTUAL_KEY.VK_V;
            inputs[1].Anonymous.ki.dwFlags = 0;
    
            inputs[2] = defaultInput;
            inputs[2].Anonymous.ki.wVk = VIRTUAL_KEY.VK_V;
            inputs[2].Anonymous.ki.dwFlags = KEYBD_EVENT_FLAGS.KEYEVENTF_KEYUP;
    
            inputs[3] = defaultInput;
            inputs[3].Anonymous.ki.wVk = VIRTUAL_KEY.VK_CONTROL;
            inputs[3].Anonymous.ki.dwFlags = KEYBD_EVENT_FLAGS.KEYEVENTF_KEYUP;
    
            while (true)
            {
                Thread.Sleep(2000);
                var rtnCode = SendInput(inputs, Marshal.SizeOf(inputs[0]));
                var errCode = Marshal.GetLastWin32Error();
                Console.WriteLine("{0} : rtn({1}) / err({2})", ++i, rtnCode, errCode);
            }
        }
    }
    
[/code]

[CsWin32でWin32APIのプラットフォーム呼び出し(P/Invoke)コードを自動生成](https://qiita.com/radian-
jp/items/a4509f9a44101fb2f30e)


CC BY-SA 4.0
2023-01-31T06:14:31.073
93634
2023-01-31T06:23:56.740
2023-01-31T06:23:56.740
41943
41943
93631
answer


ちなみに.NETクラスライブラリにも[`SendKeys.Send`メソッド](https://learn.microsoft.com/ja-
jp/dotnet/api/system.windows.forms.sendkeys.send?view=windowsdesktop-7.0)が用意されていますので、Ctrl+Vぐらいなら簡単に送信できます。Ctrlキーは`^`と記述する仕様ですので、`SendKeys.Send("^v")`で実現できます。

質問文には「コンソールアプリケーション」とありますが、その場合、プロジェクトファイル（`.csproj`）に次のように記述することでWinFormsを有効化します。

[code]

      <PropertyGroup>
    
        <!-- コンソールアプリケーション -->
        <OutputType>Exe</OutputType>
    
        <!-- Windowsであることを明示する -->
        <TargetFramework>net7.0-windows</TargetFramework>
    
        <!-- WinFormsを有効化 -->
        <UseWindowsForms>true</UseWindowsForms>
    
      </PropertyGroup>
    
[/code]


CC BY-SA 4.0
2023-01-31T22:28:00.767
93645
2023-01-31T22:28:00.767
4236
93631
answer