Win32 API SendInput๋กœ Mouse Move ์ œ์–ด

WPF ํ”„๋กœ์ ํŠธ์—์„œ Win32 API๋ฅผ ์ด์šฉํ•˜์—ฌ Mouse๋ฅผ ์ œ์–ดํ•  ์ผ์ด ์ƒ๊ฒผ์Šต๋‹ˆ๋‹ค.

MFC๋ฅผ ํ•ด๋ณธ ์ ์ด ์—†๊ณ , Win32 API์— ๋Œ€ํ•œ ์ดํ•ด๋„๊ฐ€ ๋‚ฎ์•˜๊ธฐ ๋•Œ๋ฌธ์— ์กฐ๊ธˆ ๊ณ ์ƒํ•ด์„œ, ์ด๋ ฅ์„ ๋‚จ๊ฒจ ๋†“์Šต๋‹ˆ๋‹ค.

ChatGPT์—๊ฒŒ Win32 API๋ฅผ ์ด์šฉํ•ด์„œ MouseMove๋ฅผ ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ฌผ์–ด๋ณด๋ฉด 3๊ฐ€์ง€ ๋ฐฉ๋ฒ•์„ ์ถ”์ฒœํ•ด์ค๋‹ˆ๋‹ค.

  1. SendMessage/PostMessage ์ด์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•
  2. mouse_event ์ด์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•
  3. SendInput ์ด์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•

์ด ์ค‘์—์„œ 1๋ฒˆ์„ ์‹œ๋„ํ–ˆ๋‹ค๊ฐ€ ๋™์ž‘ํ•˜์ง€ ์•Š์•„์„œ 3๋ฒˆ์œผ๋กœ ๋ฐ”๊ฟจ์Šต๋‹ˆ๋‹ค. (์™œ ๋™์ž‘ํ•˜์ง€ ์•Š์•˜๋Š”์ง€๋Š” ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค.)

mouse_event๋ฅผ ๊ณ ๋ คํ•˜์ง€ ์•Š์€ ์ด์œ ๋Š”,

mouse_event ํ•จ์ˆ˜(winuser.h) - Win32 apps | Microsoft Learn

image

์ด๊ฒƒ์„ ํ™•์ธํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. Deprecated์ธ์ง€๋Š” ๋ชจ๋ฅด๊ฒ ์ง€๋งŒ์š”.

Mouse ์ œ์–ด ์˜ˆ์ œ์ด์ง€๋งŒ ์ฝ”๋“œ ์ž์ฒด๋Š” Key ์ œ์–ด์˜ ์ฝ”๋“œ๋„ ๊ฐ™์ด ์˜ฌ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค.

์ค€๋น„๊ณผ์ •

Win32 API ์˜ SendInput์—์„œ๋Š” INPUT์ด๋ผ๋Š” ๊ณต์šฉ์ฒด(union)์„ ์‚ฌ์šฉํ•ด์„œ ์ž…๋ ฅ์„ ์ œ์–ดํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

C#์—์„œ๋Š” ๊ณต์šฉ์ฒด๋ฅผ ์ •์˜ํ•˜์ง€ ๋ชปํ•˜๋ฏ€๋กœ, ChatGPT์—๊ฒŒ ๋ฌผ์–ด์„œ ์•„๋ž˜์™€ ๊ฐ™์ด ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.

INPUT32

[StructLayout(LayoutKind.Explicit)]
public struct INPUT32
{
    [FieldOffset(0)]
    public uint type; // x86์—์„œ int, uint๋Š” 4byte์ด๋‹ค.
    [FieldOffset(4)]
    public KEYBDINPUT ki;
    [FieldOffset(4)]
    public MOUSEINPUT mi;
    [FieldOffset(4)]
    public HARDWAREINPUT hi;
}

INPUT64

[StructLayout(LayoutKind.Explicit)]
public struct INPUT64
{
    [FieldOffset(0)]
    public uint type; // x64์—์„œ int, uint๋Š” 8byte์ด๋‹ค.
    [FieldOffset(8)]
    public KEYBDINPUT ki;
    [FieldOffset(8)]
    public MOUSEINPUT mi;
    [FieldOffset(8)]
    public HARDWAREINPUT hi;
}

KEYBDINPUT

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

MONITORINFO

[StructLayout(LayoutKind.Sequential)]
public struct MONITORINFO
{
    public int cbSize;
    public RECT rcMonitor;
    public RECT rcWork;
    public uint dwFlags;
}

MOUSEINPUT

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

์•„ํ‚คํ…์ณ๊ฐ€ ๋‹ค๋ฅผ ๊ฒƒ์„ ๊ณ ๋ คํ•ด์„œ INPUT ๊ฐ์ฒด๋ฅผ 32bit์šฉ 64bit์šฉ์œผ๋กœ 2๊ฐœ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

์‚ฌ์‹ค INPUT ํด๋ž˜์Šค๋ฅผ ์œ„์ฒ˜๋Ÿผ ๊ณต์šฉ์ฒด ์Šคํƒ€์ผ๋กœ ์“ฐ์ง€ ์•Š๊ณ , ๋งˆ์šฐ์Šค, ํ‚ค๋ณด๋“œ์— ๋Œ€ํ•ด ๋”ฐ๋กœ ๋งŒ๋“ค๋ฉด INPUT ํด๋ž˜์Šค ์ž์ฒด๋ฅผ [StructLayout(LayoutKind.Sequential)] ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— Sequential ๋œป๋Œ€๋กœ ์ง๋ ฌํ™”๋˜์–ด x86, x64 ์•„ํ‚คํ…์ณ์—์„œ ๋ชจ๋‘ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์š•์‹ฌ์„ ๋‚ด์„œ๊ณต์šฉ์ฒด๋กœ ์ž‘์„ฑํ•˜๋ ค๋ฉด [StructLayout(LayoutKind.Explicit)]์„ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด ํƒ€์ž…์€ ๋ช‡ ๋ฐ”์ดํŠธ๋ผ๋Š” ๊ฒƒ์„ ๋ช…์‹œํ•ด์ฃผ์–ด์•ผ ํ•˜๊ณ  ๊ทธ๊ฒƒ์ด [FieldOffset(4)] ์ž…๋‹ˆ๋‹ค.

์ฃผ์„์„ ๋ณด์‹œ๋ฉด ์•„์‹œ๊ฒ ์ง€๋งŒ x86๊ณผ x64์—์„œ int์˜ ๊ธฐ๋ณธ ์‚ฌ์ด์ฆˆ๋Š” ๋‹ค๋ฆ…๋‹ˆ๋‹ค. ์ œ ํ˜„์žฌ PC๊ฐ€ x64์ด๊ณ , Explict์œผ๋กœ ์„ ์–ธ๋œ ๊ณต์šฉ์ฒด๋ฅผ ์จ์„œ [FieldOffset(4)]๋กœ ์„ ์–ธ๋œ ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์•„๋ž˜์˜ SendInput ๋ฉ”์„œ๋“œ๋Š” ๋™์ž‘ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

[DllImport("user32.dll", SetLastError = true)]
public static extern uint SendInput(uint nInputs, [MarshalAs(UnmanagedType.LPArray), In] INPUT32[] pInputs, int cbSize);

๊ทธ๋ž˜์„œ ํ•˜๋‚˜ ๋” ์„ ์–ธํ•ฉ๋‹ˆ๋‹ค.

[DllImport("user32.dll", SetLastError = true)]
public static extern uint SendInput(uint nInputs, [MarshalAs(UnmanagedType.LPArray), In] INPUT32[] pInputs, int cbSize);

[DllImport("user32.dll", SetLastError = true)]
public static extern uint SendInput(uint nInputs, [MarshalAs(UnmanagedType.LPArray), In] INPUT64[] pInputs, int cbSize);

์—ฌ๊ธฐ๊นŒ์ง€๊ฐ€ Win32 API๋กœ SendInput์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ ์ค€๋น„๊ณผ์ •์ž…๋‹ˆ๋‹ค.

SendInput์˜ ์ •๊ทœํ™”

INPUT32์™€ INPUT64์— ๋“ค์–ด๊ฐ€๋Š” MOUSEINPUT ๊ฐ’์€ ์ •๊ทœํ™”๋ฅผ ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. ์•„๋ž˜์™€ ๊ฐ™์€ ๊ทœ์น™์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

MOUSEINPUT(winuser.h) - Win32 apps | Microsoft Learn

์œ„ ๋ฌธ์„œ๋ฅผ ๋ณด๋ฉด MOUSEEVENTF_ABSOLUTE ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„๋„ ๋˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ์„ค๋ช…์ด ์จ์žˆ์ง€๋งŒ, ์ด ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฉด ์ ˆ๋Œ€์ขŒํ‘œ๊ฐ€ ์•„๋‹ˆ๋ผ ์ƒ๋Œ€์ขŒํ‘œ ํ˜•ํƒœ๋กœ ๋™์ž‘ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์˜คํžˆ๋ ค ์ œ์–ด๊ฐ€ ์–ด๋ ต์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋ฐ˜๋“œ์‹œ ์จ์•ผํ•˜๋Š” ์˜ต์…˜์ž…๋‹ˆ๋‹ค. ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ์ž‘์„ฑ์ด ๋ฉ๋‹ˆ๋‹ค.

private static INPUT32 MouseMove32(int normalized_X, int normalized_Y)
{
    // ๋งˆ์šฐ์Šค ์ด๋™ ์ž…๋ ฅ ์ƒ์„ฑ
    var mouseMoveInput = new INPUT32
    {
        type = INPUT_MOUSE,
        mi = new MOUSEINPUT
        {
            dx = normalized_X,
            dy = normalized_Y,
            dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE
        }
    };

    return mouseMoveInput;
}

private static INPUT64 MouseMove64(int normalized_X, int normalized_Y)
{
    // ๋งˆ์šฐ์Šค ์ด๋™ ์ž…๋ ฅ ์ƒ์„ฑ
    var mouseMoveInput = new INPUT64
    {
        type = INPUT_MOUSE,
        mi = new MOUSEINPUT
        {
            dx = normalized_X,
            dy = normalized_Y,
            dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE
        }
    };

    return mouseMoveInput;
}

private static INPUT32 MouseDown32()
{
    // ๋งˆ์šฐ์Šค ํด๋ฆญ ์ž…๋ ฅ ์ƒ์„ฑ (์™ผ์ชฝ ๋ฒ„ํŠผ ๋ˆ„๋ฆ„)
    var mouseDownInput = new INPUT32
    {
        type = INPUT_MOUSE,
        mi = new MOUSEINPUT { dwFlags = MOUSEEVENTF_LEFTDOWN }
    };

    return mouseDownInput;
}

private static INPUT64 MouseDown64()
{
    // ๋งˆ์šฐ์Šค ํด๋ฆญ ์ž…๋ ฅ ์ƒ์„ฑ (์™ผ์ชฝ ๋ฒ„ํŠผ ๋ˆ„๋ฆ„)
    var mouseDownInput = new INPUT64
    {
        type = INPUT_MOUSE,
        mi = new MOUSEINPUT { dwFlags = MOUSEEVENTF_LEFTDOWN }
    };

    return mouseDownInput;
}

private static INPUT32 MouseDown32(int x, int y)
{
    // ๋งˆ์šฐ์Šค ํด๋ฆญ ์ž…๋ ฅ ์ƒ์„ฑ (์™ผ์ชฝ ๋ฒ„ํŠผ ๋ˆ„๋ฆ„)
    var mouseDownInput = new INPUT32
    {
        type = INPUT_MOUSE,
        mi = new MOUSEINPUT { dx = x, dy = y, dwFlags = MOUSEEVENTF_LEFTDOWN }
    };

    return mouseDownInput;
}

private static INPUT64 MouseDown64(int x, int y)
{
    // ๋งˆ์šฐ์Šค ํด๋ฆญ ์ž…๋ ฅ ์ƒ์„ฑ (์™ผ์ชฝ ๋ฒ„ํŠผ ๋ˆ„๋ฆ„)
    var mouseDownInput = new INPUT64
    {
        type = INPUT_MOUSE,
        mi = new MOUSEINPUT { dx = x, dy = y, dwFlags = MOUSEEVENTF_LEFTDOWN }
    };

    return mouseDownInput;
}

private static INPUT32 MouseUp32()
{
    // ๋งˆ์šฐ์Šค ํด๋ฆญ ์ž…๋ ฅ ์ƒ์„ฑ (์™ผ์ชฝ ๋ฒ„ํŠผ ๋—Œ)
    var mouseUpInput = new INPUT32
    {
        type = INPUT_MOUSE,
        mi = new MOUSEINPUT { dwFlags = MOUSEEVENTF_LEFTUP }
    };

    return mouseUpInput;
}

private static INPUT64 MouseUp64()
{
    // ๋งˆ์šฐ์Šค ํด๋ฆญ ์ž…๋ ฅ ์ƒ์„ฑ (์™ผ์ชฝ ๋ฒ„ํŠผ ๋—Œ)
    var mouseUpInput = new INPUT64
    {
        type = INPUT_MOUSE,
        mi = new MOUSEINPUT { dwFlags = MOUSEEVENTF_LEFTUP }
    };

    return mouseUpInput;
}

private static INPUT32 KeyDown32(short vk)
{
    var keyDownInput = new INPUT32
    {
        type = INPUT_KEYBOARD,
        ki = new KEYBDINPUT { wVk = (ushort)vk }
    };

    return keyDownInput;
}

private static INPUT64 KeyDown64(short vk)
{
    var keyDownInput = new INPUT64
    {
        type = INPUT_KEYBOARD,
        ki = new KEYBDINPUT { wVk = (ushort)vk }
    };

    return keyDownInput;
}

private static INPUT32 KeyUp32(short vk)
{
    var keyUpInput = new INPUT32
    {
        type = INPUT_KEYBOARD,
        ki = new KEYBDINPUT { wVk = (ushort)vk, dwFlags = KEYEVENTF_KEYUP }
    };

    return keyUpInput;
}

private static INPUT64 KeyUp64(short vk)
{
    var keyUpInput = new INPUT64
    {
        type = INPUT_KEYBOARD,
        ki = new KEYBDINPUT { wVk = (ushort)vk, dwFlags = KEYEVENTF_KEYUP }
    };

    return keyUpInput;
}

์• ์ดˆ์— WindowHandle ๊ธฐ๋ฐ˜์ธ SendMessage๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๊ณ  ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— Handle์„ ์ด๋ฏธ ์–ป์€ ์ƒํƒœ์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด MouseClick์„ ์œ„ํ•œ ์ฝ”๋“œ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

public static void ProcessInMouseClick(IntPtr targetProcessHandle, int x, int y)
{
    IntPtr hMonitor = MonitorFromWindow(targetProcessHandle, MONITOR_DEFAULTTONEAREST);
    var monitorInfo = new MONITORINFO();
    monitorInfo.cbSize = Marshal.SizeOf(monitorInfo);

    if (!GetMonitorInfo(hMonitor, ref monitorInfo)) return;

    if (!GetWindowRect(new HandleRef(null, targetProcessHandle), out RECT rect)) return;

    int offset_window_posX = rect.Left;
    int offset_window_posY = rect.Top;
    int _x = x + offset_window_posX;
    double ratioX = (double)_x / monitorInfo.rcMonitor.Width;
    int normalizedX = Convert.ToInt32(Math.Round(ratioX * NORMALIZED_VALUE));
    int _y = y + offset_window_posY;
    double ratioY = (double)_y / monitorInfo.rcMonitor.Height;
    int normalizedY = Convert.ToInt32(Math.Round(ratioY * NORMALIZED_VALUE));

    if (Environment.Is64BitProcess)
        SendInput(1, new[] { MouseMove64(normalizedX, normalizedY) }, Marshal.SizeOf(typeof(INPUT64)));
    else
        SendInput(1, new[] { MouseMove32(normalizedX, normalizedY) }, Marshal.SizeOf(typeof(INPUT32)));

    if (Environment.Is64BitProcess)
        SendInput(1, new[] { MouseDown64() }, Marshal.SizeOf(typeof(INPUT64)));
    else
        SendInput(1, new[] { MouseDown32() }, Marshal.SizeOf(typeof(INPUT32)));

    if (Environment.Is64BitProcess)
        SendInput(1, new[] { MouseUp64() }, Marshal.SizeOf(typeof(INPUT64)));
    else
        SendInput(1, new[] { MouseUp32() }, Marshal.SizeOf(typeof(INPUT32)));
}

โ€ปRECT๋Š” ๋”ฐ๋กœ ์ •์˜ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ด ์ž‘์—…์„ ํ•  ๋•Œ๋Š” Multi Monitor ์ƒํ™ฉ์„ ๊ณ ๋ คํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
๋ชจ๋‹ˆํ„ฐ๊ฐ€ 1๊ฐœ๋ผ๋ฉด ์‹ ๊ฒฝ์“ฐ์ง€ ์•Š์•„๋„ ๋˜์ง€๋งŒ, ๋“€์–ผ ๋ชจ๋‹ˆํ„ฐ ์ด์ƒ์ด๋ผ๋ฉด Windows์˜ ์ „์ฒด ๊ณต๊ฐ„์˜ ์ขŒํ‘œ๊ฐ’์ด ํ•„์š”ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๊ณ  ๊ทธ ์ขŒํ‘œ๊ณ„๋Š” ์ด์–ด ๋ถ™ํ˜€์ ธ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

  1. ํด๋ฆญํ•˜๊ณ  ์‹ถ์€ ๋Œ€์ƒ์˜ UI ํ•ธ๋“ค์„ ์–ป์Œ
  2. ํ•ธ๋“ค์ด ์กด์žฌํ•˜๋Š” ๋ชจ๋‹ˆํ„ฐ์˜ ํ•ธ๋“ค์„ ์–ป์Œ (ํ”„๋กœ์„ธ์Šค ํ•ธ๋“ค ์•„๋‹˜)
  3. ๋ชจ๋‹ˆํ„ฐ์˜ ํฌ๊ธฐ๋ฅผ ๊ฐ€์ ธ์˜ด
  4. UI ํ•ธ๋“ค์ด ์กด์žฌํ•˜๋Š” ๋ชจ๋‹ˆํ„ฐ ๋‚ด๋ถ€์—์„œ์˜ Left์™€ Top ์ขŒํ‘œ๊ฐ’์„ ํš๋“
  5. ์ขŒํ‘œ๊ฐ’์„ ๊ณ„์‚ฐ

์ค‘์š”ํ•œ ๊ฒƒ์€ 5๋ฒˆ์˜ ์ขŒํ‘œ๊ฐ’์„ ๊ณ„์‚ฐํ•  ๋•Œ์ž…๋‹ˆ๋‹ค.
์ขŒํ‘œ๊ฐ’์€ ๋ชจ๋‹ˆํ„ฐ์˜ ํฌ๊ธฐ์— ๋”ฐ๋ผ ๋น„์œจ์ด ๋‹ฌ๋ผ์ง‘๋‹ˆ๋‹ค. (๋‹น์—ฐํžˆ ์ขŒํ‘œ ์ž์ฒด๋Š” ๋‹ฌ๋ผ์ง€์ง€ ์•Š์Šต๋‹ˆ๋‹ค.)
๋ฌด์Šจ ๋ง์ด๋ƒ๋ฉด, ๋‚ด๊ฐ€ (100, 100) ์ด๋ผ๋Š” ์ขŒํ‘œ๋ฅผ ํด๋ฆญํ•˜๊ณ  ์‹ถ์—ˆ๋Š”๋ฐ ์ด๊ฒƒ์€ ๋‚ด ํ˜„์žฌ ๋ชจ๋‹ˆํ„ฐ ํ•ด์ƒ๋„์ธ (1920 * 1080) ์ผ ๋•Œ 100,100 ์˜ ์ขŒํ‘œ๋ฅผ ์›ํ•˜๋Š” ๊ฒƒ์ด์ง€ ์‚ฌ๋žŒ์ด ์›ํ•˜๋Š” ๊ฒƒ์€ ์ขŒํ‘œ๊ฐ€ ์•„๋‹ˆ๋ผ ๋ชจ๋‹ˆํ„ฐ ์•ˆ์—์„œ ์–ด๋””์— ์œ„์น˜ํ•ด ์žˆ๋Š”์ง€ ์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ํ•ด์ƒ๋„๊ฐ€ ์ปค์ง€๋ฉด ์‚ฌ๋žŒ์ด ์›ํ•˜๋Š” ์œ„์น˜๋Š” (100, 100) ๋ณด๋‹ค ์ปค์ง€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์ฆ‰, ratio๋ฅผ ๋ด์•ผํ•œ๋‹ค๋Š” ๋ง์ž…๋‹ˆ๋‹ค.

๋น„์œจ์„ ๊ตฌํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๊ณ ์ •๋œ ์ขŒํ‘œ๊ฐ’์ธ int๊ฐ’์„ ๋ฐ˜๋“œ์‹œ ์ „์ฒด ํฌ๊ธฐ๋กœ ๋‚˜๋ˆ ์•ผํ•ฉ๋‹ˆ๋‹ค.
์ •์ˆ˜๋ฅผ ๋‚˜๋ˆ„๋Š” ๊ณผ์ •์—์„œ๋Š” ์†Œ์ˆซ์ ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ ์ด์ ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.

์•„๊นŒ ์ •๊ทœํ™”๋ฅผ ํ•˜๊ธฐ ์œ„ํ•ด 0~65535์˜ ์ขŒํ‘œ๊ณ„๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ๋ชจ๋‹ˆํ„ฐ์˜ ๋น„์œจ์— ๋งž์ถœ ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ–ˆ๋Š”๋ฐ์š”.
ํ˜„์žฌ ๋‚ด UI ํ•ธ๋“ค์˜ ์ขŒํ‘œ๊ฐ’์„ ์ •ํ–ˆ์–ด๋„ ์ด๊ฒƒ์„ ์ „์ฒด ๋ชจ๋‹ˆํ„ฐ ํฌ๊ธฐ๋กœ ๋‚˜๋ˆ ์„œ ๋น„์œจ์„ ๋งŒ๋“ค๊ณ , ๊ทธ ๊ฐ’์— 65536์„ ๊ณฑํ•ด์„œ ์ ˆ๋Œ€ ์ขŒํ‘œ๋กœ ๋ณ€ํ™˜ํ•œ ๊ฐ’์„ ์ฐ์–ด์•ผ SendInput์„ ํ†ตํ•ด MouseMove๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

์ ˆ๋Œ€ ์ขŒํ‘œ๋Š” 65535๊นŒ์ง€๋กœ ์ •ํ•ด์ ธ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ํ•ด์ƒ๋„๊ฐ€ ์ปค์ ธ๋„ ์ด ๊ฐ’์ด ๋Š˜์–ด๋‚˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋Š˜์–ด๋‚˜๋Š” ๊ฒƒ์€ ์ ˆ๋Œ€์ขŒํ‘œ๋กœ ๋‚˜๋ˆˆ 65535 Point ๋“ค์˜ 1 Point ๋‹น ๋ฉด์ ์ด ๋„“์–ด์ง‘๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ์†Œ์ˆซ์ ์„ ๊ณ„์‚ฐํ•˜๊ธฐ ์ „๋ถ€ํ„ฐ ์ตœ๋Œ€ํ•œ ์ž˜ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๊ฐ€ ๋งˆ์ง€๋ง‰์— ์ •๊ทœํ™”ํ• ๋•Œ ๊ทธ ๋•Œ ๋น„๋กœ์†Œ ๋ฐ˜์˜ฌ๋ฆผ์„ํ•˜๊ฑฐ๋‚˜ ๋ฒ„๋ฆด ๊ฒƒ์„ ๊ฒฐ์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ด์ „๋ถ€ํ„ฐ ์†Œ์ˆซ์  ๋‹ค ์ž˜๋ผ๋จน์œผ๋ฉด์„œ ํŽธํ•˜๊ฒŒ ๊ณ„์‚ฐํ•˜๋ฉด ๋งˆ์šฐ์Šค์˜ ์ •ํ™•ํ•œ ์ œ์–ด๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

์œ„ ์ˆ˜์‹์˜ ๊ณ„์‚ฐ ์ˆœ์„œ๋ฅผ ์ž˜ ๋ณด๋ฉด ๋„์›€์ด ๋˜์‹ค ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.


์‚ฌ์‹ค ์ด๋ ‡๊ฒŒ ํ–ˆ์–ด๋„ ์™„๋ฒฝํ•˜๊ฒŒ ์ •๋ฐ€ํ•˜์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค.

๋”์šฑ ์ •๋ฐ€ํ•œ ๋งˆ์šฐ์Šค ํฌ์ธํ„ฐ ์ œ์–ด๊ฐ€ ์žˆ๋‹ค๋ฉด ์†Œ๊ฐœ ๋ถ€ํƒ๋“œ๋ฆฝ๋‹ˆ๋‹ค.

3๊ฐœ์˜ ์ข‹์•„์š”

๋งˆ์šฐ์Šค ๋“œ๋ž˜๊ทธ

public static void ProcessInMouseDrag(IntPtr targetProcessHandle, int x1, int y1, int x2, int y2, int speed_ms, int steps = 50)
{
    IntPtr hMonitor = MonitorFromWindow(targetProcessHandle, MONITOR_DEFAULTTONEAREST);
    MONITORINFO monitorInfo = new MONITORINFO();
    monitorInfo.cbSize = Marshal.SizeOf(monitorInfo);

    if (!GetMonitorInfo(hMonitor, ref monitorInfo)) return;

    if (!GetWindowRect(new HandleRef(null, targetProcessHandle), out RECT rect)) return;

    int offset_window_posX = rect.Left;
    int offset_window_posY = rect.Top;

    int _x = x1 + offset_window_posX;
    double startRatioX = (double)_x / monitorInfo.rcMonitor.Width;
    int startNormalizedX = Convert.ToInt32(Math.Round(startRatioX * NORMALIZED_VALUE));
    int _y = y1 + offset_window_posY;
    double startRatioY = (double)_y / monitorInfo.rcMonitor.Height;
    int startNormalizedY = Convert.ToInt32(Math.Round(startRatioY * NORMALIZED_VALUE));

    if (Environment.Is64BitProcess)
        SendInput(1, new[] { MouseMove64(startNormalizedX, startNormalizedY) }, Marshal.SizeOf(typeof(INPUT64)));
    else
        SendInput(1, new[] { MouseMove32(startNormalizedX, startNormalizedY) }, Marshal.SizeOf(typeof(INPUT32)));

    if (Environment.Is64BitProcess)
        SendInput(1, new[] { MouseDown64() }, Marshal.SizeOf(typeof(INPUT64)));
    else
        SendInput(1, new[] { MouseDown32() }, Marshal.SizeOf(typeof(INPUT32)));

    int delay = speed_ms / steps;

    for (int i = 0; i < steps; i++)
    {
        double dx = x1 + (x2 - x1) * i / (double)steps + offset_window_posX;
        double dy = y1 + (y2 - y1) * i / (double)steps + offset_window_posY;
        int viaNormalizedX = Convert.ToInt32(Math.Round(dx / monitorInfo.rcMonitor.Width * NORMALIZED_VALUE));
        int viaNormalizedY = Convert.ToInt32(Math.Round(dy / monitorInfo.rcMonitor.Height * NORMALIZED_VALUE));

        if (Environment.Is64BitProcess)
            SendInput(1, new[] { MouseMove64(viaNormalizedX, viaNormalizedY) }, Marshal.SizeOf(typeof(INPUT64)));
        else
            SendInput(1, new[] { MouseMove32(viaNormalizedX, viaNormalizedY) }, Marshal.SizeOf(typeof(INPUT32)));

        Thread.Sleep(delay);
    }

    if (Environment.Is64BitProcess)
        SendInput(1, new[] { MouseUp64() }, Marshal.SizeOf(typeof(INPUT64)));
    else
        SendInput(1, new[] { MouseUp32() }, Marshal.SizeOf(typeof(INPUT32)));
}

ํ‚ค๋ณด๋“œ ๋ฌธ์ž์—ด ์ž…๋ ฅ

[DllImport("user32.dll")]
public static extern short VkKeyScan(char ch);
public static void ProcessInKeyInput(string key)
{
    foreach (char ch in key)
    {
        short vk = VkKeyScan(ch);

        if (Environment.Is64BitProcess)
        {
            SendInput(1, new[] { KeyDown64(vk) }, Marshal.SizeOf(typeof(INPUT64)));
            SendInput(1, new[] { KeyUp64(vk) }, Marshal.SizeOf(typeof(INPUT64)));
        }
        else
        {
            SendInput(1, new[] { KeyDown32(vk) }, Marshal.SizeOf(typeof(INPUT32)));
            SendInput(1, new[] { KeyUp32(vk) }, Marshal.SizeOf(typeof(INPUT32)));
        }
    }
}
2๊ฐœ์˜ ์ข‹์•„์š”