Windows10 64bit環境にて。
今回の結論 - 参考サイト
32bit/64bitでPackサイズ(アライメント)が変わるので、アンマネージのunion
やstruct
をLayoutKind.Explicit(0以外)
で配置するとはまるケースがあるよう。今回はまさにこれ。
https://jinblog.at.webry.info/201604/article_1.html
現象
ペイント(mspaint
)上でイベントを受け取れているかを確認。
csc /platform x86 xxx.cs
だとSendInput
が効いているが、
csc xxx.cs
もしくは
csc /platform x64 xxx.cs
では動作しない。(SetCursorPos
による移動しかしない)
SendInputでのマウス操作の落とし穴 - その他
- カーソルが動いておらず、受け取れないアプリがある(Scrcpyとか)。(
SetCursorPos
で対策済) - 構造体の型定義が正しくない。今回これ
-
ExtraInfo
を0(IntPtr.Zero
)のままにしている(解消済)(ただ、0でも動くかも) - フォーカスが移っていないとイベントを受け取れない?(マウスイベントは事前にフォーカス移さなくても取れるっぽい)
- 座標系を(0,0)-(65535,65535)系に変換していない。
マルチディスプレイで座標が崩れる。(未確認)
ソースコード
※64bitでは動かない →修正済み
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Windows.Forms;
using System.Threading;
using System.Threading.Tasks;
public static class MouseInputWrapper
{
class NativeMethods
{
[DllImport("user32.dll", SetLastError = true)]
public extern static int SendInput(int nInputs, ref Input pInputs, int cbsize);
[DllImport("user32.dll", SetLastError = true)]
public extern static IntPtr GetMessageExtraInfo();
[DllImport("user32.dll")]
public extern static bool SetCursorPos(int x, int y);
}
const int MOUSEEVENTF_MOVE = 0x0001;
const int MOUSEEVENTF_LEFTDOWN = 0x0002;
const int MOUSEEVENTF_LEFTUP = 0x0004;
const int MOUSEEVENTF_VIRTUALDESK = 0x4000;
const int MOUSEEVENTF_ABSOLUTE = 0x8000;
[StructLayout(LayoutKind.Sequential)]
struct MouseInput
{
public int X;
public int Y;
public int Data;
public int Flags;
public int Time;
public IntPtr ExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
struct KeyboardInput
{
public short VirtualKey;
public short ScanCode;
public int Flags;
public int Time;
public IntPtr ExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
struct HardwareInput
{
public int uMsg;
public short wParamL;
public short wParamH;
}
[StructLayout(LayoutKind.Sequential)]
struct Input
{
public int Type;
public InputUnion ui;
}
[StructLayout(LayoutKind.Explicit)]
struct InputUnion
{
[FieldOffset(0)]
public MouseInput Mouse;
[FieldOffset(0)]
public KeyboardInput Keyboard;
[FieldOffset(0)]
public HardwareInput Hardware;
}
/*
// 64 bitで動かないコード
[StructLayout(LayoutKind.Explicit)]
struct Input
{
[FieldOffset(0)]
public int Type; // 0:Mouse, 1:Keyboard, 2:Hardware
[FieldOffset(4)] // ここがpackingのアライメントが64bitだと8にしないといけない
public MouseInput Mouse;
[FieldOffset(4)]
public KeyboardInput Keyboard;
[FieldOffset(4)]
public HardwareInput Hardware;
}
*/
static Input MouseMoveData(int x, int y, System.Windows.Forms.Screen screen, IntPtr extraInfo)
{
x = x * 65535 / screen.Bounds.Width;
y = y * 65535 / screen.Bounds.Height;
Input input = new Input();
input.Type = 0; // MOUSE = 0
input.ui.Mouse.Flags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE; // | MOUSEEVENTF_VIRTUALDESK
input.ui.Mouse.Data = 0;
input.ui.Mouse.X = x;
input.ui.Mouse.Y = y;
input.ui.Mouse.Time = 0;
input.ui.Mouse.ExtraInfo = extraInfo;
return input;
}
static Input MouseDataWithoutMove(int flags, IntPtr extraInfo)
{
Input input = new Input();
input.Type = 0; // MOUSE = 0
input.ui.Mouse.Flags = flags;
input.ui.Mouse.Data = 0;
input.ui.Mouse.X = 0;
input.ui.Mouse.Y = 0;
input.ui.Mouse.Time = 0;
input.ui.Mouse.ExtraInfo = extraInfo;
return input;
}
public static void SendMouseMove(int x, int y, System.Windows.Forms.Screen screen)
{
IntPtr extraInfo = NativeMethods.GetMessageExtraInfo();
Input inputs = MouseMoveData(x,y,screen,extraInfo);
NativeMethods.SetCursorPos(x,y);
int ret = NativeMethods.SendInput(1, ref inputs, Marshal.SizeOf(inputs));
// int errCode = Marshal.GetLastWin32Error();
Console.WriteLine(ret);
// Console.WriteLine(errCode);
}
public static void SendMouseDown()
{
IntPtr extraInfo = NativeMethods.GetMessageExtraInfo();
Input inputs = MouseDataWithoutMove(MOUSEEVENTF_LEFTDOWN,extraInfo);
int ret = NativeMethods.SendInput(1, ref inputs, Marshal.SizeOf(inputs));
}
public static void SendMouseUp()
{
IntPtr extraInfo = NativeMethods.GetMessageExtraInfo();
Input inputs = MouseDataWithoutMove(MOUSEEVENTF_LEFTUP,extraInfo);
int ret = NativeMethods.SendInput(1, ref inputs, Marshal.SizeOf(inputs));
}
}
class Test:Form
{
Test()
{
ClientSize = new Size(220,80);
var btn = new Button();
btn.Text = "MoveAndClick";
btn.Width = 150;
btn.Click += (sender,e)=>{
Task task = Task.Run(() =>
{
Thread.Sleep(2000);
MouseInputWrapper.SendMouseMove(150,150,System.Windows.Forms.Screen.PrimaryScreen);
Thread.Sleep(200);
MouseInputWrapper.SendMouseDown();
Thread.Sleep(200);
MouseInputWrapper.SendMouseMove(200,150,System.Windows.Forms.Screen.PrimaryScreen);
Thread.Sleep(200);
MouseInputWrapper.SendMouseUp();
});
};
Controls.Add(btn);
}
[STAThread]
static void Main()
{
Application.Run(new Test());
}
}