0

Есть программа на C#, она с некоторыми промежутками времени кликает по окну. Буквально 3 дня назад все работало, с тех пор код не меняла, но кликать стал как-то странно.

    public static void clickLeftOnPoint(Point point)
    {
        if (!point.IsEmpty)
        {
            int x = Convert.ToInt32(point.X / 1.25);
            int y = Convert.ToInt32(point.Y / 1.25);
            Point offsetPoint = new Point(x + 5, y + 5);
            WinApi.mouseClickLeft(offsetPoint.X, offsetPoint.Y);
            Wait();
        }
    }
public static void mouseClickLeft(int x, int y)
{
    IntPtr hwnd = getWindow();
    SetForegroundWindow(hwnd);
    ShowWindow(hwnd, 1);
    UserHelper.Wait();
    ClickLeftMouseButton(hwnd, x, y);
}

public static void ClickLeftMouseButton(IntPtr hwnd, int x, int y)
{
    x = CalculateAbsoluteCoordinateX(x);
    y = CalculateAbsoluteCoordinateY(y);
    INPUT[] inputs = new INPUT[3];
    inputs[0] = new INPUT
    {
        type = SendInputEventType.InputMouse,
        mkhi = new MouseKeybdhardwareInputUnion
        {
            mi = new MouseInputData
            {
                dx = x,
                dy = y,
                mouseData = 0,
                dwFlags = MouseEventFlags.MOUSEEVENTF_MOVE | MouseEventFlags.MOUSEEVENTF_ABSOLUTE
            }
        }
    };

    inputs[1] = new INPUT
    {
        type = SendInputEventType.InputMouse,
        mkhi = new MouseKeybdhardwareInputUnion
        {
            mi = new MouseInputData
            {
                dx = x,
                dy = y,
                mouseData = 0,
                dwFlags = MouseEventFlags.MOUSEEVENTF_LEFTDOWN | MouseEventFlags.MOUSEEVENTF_LEFTUP
            }
        }
    };
    var result = SendInput((uint)inputs.Length, inputs, Marshal.SizeOf(typeof(INPUT)));
    Console.WriteLine("Result: " + result);
}       

В некоторые места клик проходит и я вижу сообщения (предварительно активирую окно): введите сюда описание изображения

Дальше начинается самое странное (отправляется сообщение WM_SETCURSOR, а сообщения кликов - нет): введите сюда описание изображения

Не сильна в WinApi, не особо понимаю, почему недавно работало, а теперь перестало. Пробовала использовать другие способы клика, но там вообще сообщения в окно не проходят.

Создается впечатление, что первый клик проходит всегда, а последующие как будто блокируются, визуально окно остается активным, но сообщения на клики игнорятся

  • Marshal.SizeOf(new INPUT()) замените на Marshal.SizeOf(typeof(INPUT)) или Marshal.SizeOf<INPUT>(), вот пример, возможно поможет. – aepot Jun 10 '22 at 19:25
  • Второй нюанс, вы возможно работаете в цикле, в синхронном цикле, и ваше окно попало в цепочку передачи Windows сообщений перед целевым приложением, а циклом вы заблокировали UI поток, как следствие передачу сообщений по цепочке. Но без кода, который воспроизводит проблему, это не угадать. Теоретически, нужно выполнять такие циклы асинхронно, либо делать на таймерах. Кстати, SendInput может принимать массив инпутов, а не один инпут. Попробуйте передать свою группу на ввод пачкой, а не по одному. – aepot Jun 10 '22 at 19:35
  • @aepot, первый вариант не помог, второй вроде пробовала, но сейчас продублирую. Заметила, что если перед каждым кликом снимать фокус с окна, то есть переключаться на другое - программа активирует окно кликом и он проходит. Покопалась, не нашла ничего путного, чем можно снимать фокус с окна програмно, есть идеи, почему так и как можно починить? – Neonika Jun 10 '22 at 20:32
  • С чего вы решили, что это необходимо? У меня кликер сутками может работать, тыкает во все что угодно исправно. Никакой магии, главное - не морозить UI поток. Вы покажете код и обрисуете задачу? А то пока выглядит как борьба с симптомами. Ошибка может быть вообще не там, где вы ищете. Код анализатора WM тоже бы увидеть, чтобы воспроизвести проблему. Хотя-бы обработчик WndProc. – aepot Jun 10 '22 at 20:47
  • Тип приложения укажите в тегах, если код добавите. – aepot Jun 10 '22 at 20:48
  • У меня тоже работал сутками, уже месяц или два, а тут привет и перестал работать:( Код метода, который кликает - с вопросе приложен. Приложение WinForms. Задача - каждые 5 минут нужно кликать поочередно по 5 местам в игре, координаты заранее известны. Срабатывает таймер, нахожу нужное окно и активирую его -
    IntPtr hwnd = getWindow(); SetForegroundWindow(hwnd); ShowWindow(hwnd, 1); Thread.Sleep(1000); Дальше вызываю метод клика, передаю туда корды. Снова запускается таймер, и так по кругу.
    – Neonika Jun 10 '22 at 21:28
  • Кстати, произошло все это после обновления игры, могли защиту лучше сделать или WinApi все обходить должен по умолчанию? – Neonika Jun 10 '22 at 21:43
  • Thread.Sleep в UI потоке делать нельзя, совсем нельзя, это очень плохо, замените на await Task.Delay, если уж очень надо. 1) Уберите таймер, переделайте на асинхронный цикл. 2) как я показал выше, передавайте все события мыши разом одним массивом, оно пачкой отработает нормально, при этом вы снизите нагрузку на WinAPI в 3 раза. 3) Если я прав про проблемы с UI потоком, теоретически запуск кликера от имени администратора должен убрать симптомы, но это конечно же не решение, у админских приложений своя очередь в WM Loop. – aepot Jun 10 '22 at 22:03
  • Игровые приложения используют DirectInput, они вообще могут не иметь Message Loop. Так что просмотр WM-сообщений, это так себе диагностика. Отправленные события в SendInput тоже попадают в девайс DirectInput, так что должно работать на активном окне в любом случае. – aepot Jun 10 '22 at 22:05
  • UserHelper.Wait(); и Wait(); случаем не Thread.Sleep? :) везде оно у вас. MouseEventFlags.MOUSEEVENTF_LEFTDOWN | MouseEventFlags.MOUSEEVENTF_LEFTUP так делать нельзя, отдельно DOWN, отдельно UP надо в массив ложить. – aepot Jun 10 '22 at 22:07
  • new INPUT[3] создаете, а добавляете 2. mouseData = 0 можно не присваивать, оно и так 0 по умолчанию. – aepot Jun 10 '22 at 22:08
  • public static async void Wait() { int sleep = new Random().Next(100, 200); await Task.Delay(sleep); } – Neonika Jun 10 '22 at 22:09
  • этот Wait нерабочий. Он ничего не ждет. Более того, не создавайте Random каждый раз, создайте 1 раз и поместите в поле или свойство. – aepot Jun 10 '22 at 22:10
  • Уже поняла.. Если переделывать его в таску - надо все методы, которые его используют переписывать в асинхронные. Никогда с ними не работала и дико боюсь, столько всего переписывать =\ Методы клика разделила, приложение и игру запускаю от админа. Пока ничего не помогает. Видимо, на асинхронность все это дело переписывать остается единственным вариантом.. – Neonika Jun 10 '22 at 22:14
  • Смысл в том что приложение от админа, а игру - нет. Ответ дал, других вариантов пока не имею. Как пользоваться SendInput тоже показал. – aepot Jun 10 '22 at 22:22

0 Answers0