0

Есть тестовый проект, который просто перемещает мышь через mouse_event на 100 пикселей вперед и низ. Но координаты после выполнения этого метода получаются совсем не такими, как я ожидал. Помогите найти ошибку

Код:

using System;
using System.Runtime.InteropServices;

namespace mouse_move_test { class Program { const int ABSOLUTE = 0x00008000; const int MOVE = 0x00000001;

    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool GetCursorPos(out MousePoint lpMousePoint);

    public static MousePoint GetCursorPosition()
    {
        MousePoint currentMousePoint;
        var gotPoint = GetCursorPos(out currentMousePoint);
        if (!gotPoint) { currentMousePoint = new MousePoint(0, 0); }
        return currentMousePoint;
    }

    [DllImport("user32.dll")]
    private static extern void mouse_event(int dwFlags, int dx, int dy, int dwData, int dwExtraInfo);

    [StructLayout(LayoutKind.Sequential)]
    public struct MousePoint
    {
        public int X;
        public int Y;

        public MousePoint(int x, int y)
        {
            X = x;
            Y = y;
        }
    }
    static void Main(string[] args)
    {
        int dt = 100;


        // Тест относительных координат
        Console.WriteLine($"Тест относительных координат");
        for (int i = 0; i < 3; i++)
        {
            var pos = GetCursorPosition();
            Console.WriteLine($"{pos.X} {pos.Y} - текущая позиция");
            Console.WriteLine($"{pos.X + dt} {pos.Y + dt} - ожидание");

            mouse_event(MOVE, dt, dt, 0, 0);

            pos = GetCursorPosition();
            Console.WriteLine($"{pos.X} {pos.Y} - реальность");
            Console.WriteLine();
        }

        // Тест абсолютных координат
        Console.WriteLine($"Тест абсолютных координат");
        for (int i = 0; i < 3; i++)
        {
            var pos = GetCursorPosition();
            Console.WriteLine($"{pos.X} {pos.Y} - текущая позиция");
            Console.WriteLine($"{pos.X + dt} {pos.Y + dt} - ожидание");

            mouse_event(MOVE | ABSOLUTE, pos.X + dt, pos.Y + dt, 0, 0);

            pos = GetCursorPosition();
            Console.WriteLine($"{pos.X} {pos.Y} - реальность");
            Console.WriteLine();
        }

        Console.ReadLine();
    }
}



}

Вывод в консоль:

Тест относительных координат
0 0 - текущая позиция
100 100 - ожидание
155 155 - реальность

155 155 - текущая позиция 255 255 - ожидание 409 409 - реальность

409 409 - текущая позиция 509 509 - ожидание 664 664 - реальность

Тест абсолютных координат 664 664 - текущая позиция 764 764 - ожидание 22 12 - реальность

22 12 - текущая позиция 122 112 - ожидание 3 1 - реальность

3 1 - текущая позиция 103 101 - ожидание 3 1 - реальность

upd. Я правда пытаюсь) Приложение WPF. Я постараюсь привести упрощенные методы, мб это поможет Есть хук мыши

private int MouseHookProc(int nCode, int wParam, IntPtr lParam)
{
    if (nCode >= 0)
    {
        Console.WriteLine($"wParam {wParam}");
        return CallNextHookEx(hMouseHook, nCode, wParam, lParam);
    }
}

Если я вызываю код mouse_event(0x00000001, 100, 100, 0, 0); Я получаю в лог wParam 512 Если использую код SetCursorPos(100, 100); То ничего не перехватывается. Что касается SendMessage с ним у меня были какие-то проблемы. Кажется, не работает в играх

dpi у меня 96, как я выяснил. А у координат прирост 50%. Где-то есть масштаб 150% который я пока не знаю как достать

  • https://ru.stackoverflow.com/a/1436487/373567 – aepot Aug 12 '22 at 10:54
  • В том коде используется SetCursorPos. Я правильно понимаю, что он и есть MouseMove, а в mouse_event флаг MOVE можно использовать только в тандеме с другими флагами? Т.е. сам по себе как мув он так не сработает? Просто мне казалось его использование вполне логичным – Николайн Aug 12 '22 at 11:02
  • mouse_event работает в логических пикселях, SetCursorPos и GetCursorPos - в физических. У вас масштаб на мониторе не 100%, поэтому вы получаете такие спецэффекты. Вы можете и MOVE использовать, но придется пересчитывать координаты через фактическое значение dpi в отношении к стандартному - 96dpi. То есть ваше значение dpi - 148dpi (но это не точно), его так же можно получить из Win API. Но лучше ничего не пересчитывать, а использовать сразу подходящий метод Win API. – aepot Aug 12 '22 at 11:11
  • Кстати mouse_event устарел, когда я еще был маленьким, актуальный метод Win API для управления клавиатурой и мышью - SendInput. Если хотите правильно реализовать, используйте его, но сразу предупреждаю, он не так прост. Вот пример, для начала. – aepot Aug 12 '22 at 11:15
  • Похоже, придется пересчитывать. Так как для меня важно, чтобы это было именно движение, а не смена позиции. SetCursorPos не хукается как смена позиции. За пример с SendInput спасибо. Однажды) – Николайн Aug 12 '22 at 11:25
  • SetCursorPos не хукается как смена позиции Так я вам дал ссылку на код, где все движения реализованы. И на координаты и на смещение. Посмотрите внимательно. Готовый класс, берите и пользуйтесь. Сложно чтоли получить GetCursorPos, добавить к нему смещение и вызвать SetCursorPos? – aepot Aug 12 '22 at 11:27
  • Да, SetCursorPos делает смещение, всё четко по координатам, курсор там где нужно. Но в системе это не регистрируется как событие мыши с кодом 512. Я об этом – Николайн Aug 12 '22 at 11:33
  • Что значит не регистрируется как событие 512? Я этим кодом кликал по всякому, работало как часы. Вы про сообщение WM_MOUSEMOVE? Тогда это не в методе SetCursorPos проблема, а в том что у вас код возможно неправильно написан, вы какой тип приложения используете для этого? – aepot Aug 12 '22 at 11:44
  • Вы не прислушались к моему совету задать отдельный вопрос, вместо этого изменили текущий и мой ответ, данный ранее, превратился в мусор. Жаль, но больше на вопросы, которые меняются после выдачи ответа, я отвечать не намерен, пустая трата времени. Теперь ваш вопрос содержит 2 вопроса, и его необходимо конкретизировать, в противном случае он будет закрыт. Если ничего не помогает, попробуйте SendInput, как я советовал ранее. – aepot Aug 12 '22 at 12:55
  • Тот ответ не бы мусором. Просто он решал проблему только отчасти. Моей ошибкой было то, что я не указал, что мне нужно имитировать реальное движение мыши. Я не ожидал, что обсуждение уйдёт от моего кода. Но, согласитесь, вопрос мой заключался в том, почему в моем коде с координатами беда, а не как мне переместить мышь – Николайн Aug 12 '22 at 13:11
  • https://stackoverflow.com/a/1918890/12888024 – aepot Aug 12 '22 at 13:12
  • Спасибо. Увы, я ошибся, и дело не в масштабе. Какой-то нелинейный коэффициент взрывает мне голову. На перемещении 100 он даёт 155, а на 200 - 317, на 300 - 480. Никакой пропорции. В любом случае, спасибо за уделенное время. На сегодня у меня руки опустились. Мб завтра со свежей головой что-то выйдет. Простите, если что не так. Кстати, поведение оказалось одинаковым хоть для консоли, хоть для wpf, хоть для mouse_event, хоть для SendInput – Николайн Aug 12 '22 at 13:56
  • Добавьте минимальную задержку между манипуляциями с мышью, если их несколько. Хук может не сразу реагировать на сообщения. Используйте асинхронное ожидание await Task.Delay() не используйте Thread.Sleep. – aepot Aug 12 '22 at 14:05

1 Answers1

0

Итак, проблему удалось решить через флаг абсолютных координат, хотя поведения без него с непропорциональными коэффициентами я так и не раскусил. Информацию нашел сначала тут https://stackoverflow.com/questions/62626388/c-sharp-winapi-mouse-event потом уже и в справке https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-mouse_event в ремарке. Кстати там же есть что-то полезное и про относительные координаты (без флага), но я уже не мог. Имея рабочий вариант с абсолютными координатами уже не проблема использовать его и для относительных.

using System;
using System.Runtime.InteropServices;

namespace mouse_move_test { class Program { const int ABSOLUTE = 0x00008000; const int MOVE = 0x00000001;

    [DllImport("user32.dll")]
    static extern int GetSystemMetrics(int nIndex);

    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool GetCursorPos(out MousePoint lpMousePoint);

    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool SetCursorPos(int x, int y);

    public static MousePoint GetCursorPosition()
    {
        MousePoint currentMousePoint;
        var gotPoint = GetCursorPos(out currentMousePoint);
        if (!gotPoint) { currentMousePoint = new MousePoint(0, 0); }
        return currentMousePoint;
    }

    [DllImport("user32.dll")]
    private static extern void mouse_event(int dwFlags, int dx, int dy, int dwData, int dwExtraInfo);


    [StructLayout(LayoutKind.Sequential)]
    public struct MousePoint
    {
        public int X;
        public int Y;

        public MousePoint(int x, int y)
        {
            X = x;
            Y = y;
        }
    }
    static void Main(string[] args)
    {
        int dt = 100;

        // получаем разрешение экрана
        int sx = GetSystemMetrics(0);
        int sy = GetSystemMetrics(1);


        // Тест относительных координат
        Console.WriteLine($"Тест абсолютных координат");
        for (int i = 0; i < 3; i++)
        {
            SetCursorPos(0, 0);
            var pos = GetCursorPosition();
            Console.WriteLine($"{pos.X} {pos.Y} - текущая позиция");
            Console.WriteLine($"{pos.X + dt} {pos.Y + dt} - ожидание");

            int x = (int)Math.Ceiling(65536d / sx * dt);
            int y = (int)Math.Ceiling(65536d / sy * dt);

            mouse_event(MOVE | ABSOLUTE, x, y, 0, 0);

            pos = GetCursorPosition();
            Console.WriteLine($"{pos.X} {pos.Y} - реальность");
            Console.WriteLine();

            dt += 100;
        }

        Console.ReadLine();
    }
}

}

Вывод

Тест абсолютных координат
0 0 - текущая позиция
100 100 - ожидание
100 100 - реальность

0 0 - текущая позиция 200 200 - ожидание 200 200 - реальность

0 0 - текущая позиция 300 300 - ожидание 300 300 - реальность