КОТ++
Прикладное программирование
Системное программирование
Программирование микроконтроллеров

Большинство хороших программистов делают свою работу не потому, что ожидают оплаты или признания, а потому что получают удовольствие от программирования. (c) Линус Торвальдс

Нажимаем кнопочки в окнах средствами WinApi

Опубликовано
Комментарии 0

Разрабатывая очередную программу, столкнулся с рядом проблем, которые таит в себе WinApi. Передо мной стояла задача написать программу, которая будет выполнять действия в окнах за юзера в определённый момент. Программа должна научиться кликать мышкой и крутить колёсиком в чужих окнах. В этом посте постараюсь изложить то, как я решил задачу.

Я не буду здесь выкладывать сигнатуры всех используемых мной функций, т.к. это можно найти на msdn, наоборот я покажу какие функции, в какой ситуации лучше использовать и как это лучше сделать. Возможно, кому-нибудь пригодится данный опыт.

События мыши это по сути сообщения, которые пользователь посылает в систему. Эти события можно сымитировать программно. Для этого есть две замечательные функции из WinApi SendMessage и PostMessage. Задача у них одна – отправить сообщение в целевое окно. Различие в том, что первая ждёт пока целевое окно обработает отправленное сообщение, вторая не дожидается этого, она просто отсылает это сообщение больше ни о чём не беспокоясь. Разберём теперь как же найти это целевое окно. Я считаю, что хороший способ – найти окно по его заголовку. Для такого поиска есть функция WinApi FindWindow. Для написания программы я использовал c#, и как оказалось в нём достаточно функционала найти хэндл окна по заголовку.
//получаем массив всех процессов системы
Process[] procs = Process.GetProcesses();
//перебираем полученный массив
foreach (Process p in procs)
{
        //если заголовок окна содержит строку поиска
        if (p.MainWindowTitle.IndexOf(TITLE_SEARCH) != -1)
        {
                //то запоминаем хэндл окна
                IntPtr handle = p.MainWindowHandle;
        }
}
Зная хэндл можем отправить клик мыши в это окно. Здесь уже средствами .NET не обойтись, поэтому начинаем юзать WinApi функции. Чтобы подключить функцию SendMessage к c# программе нужно в код прописать следующее:
[DllImport("User32.dll")]
public static extern Int32 SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
hWnd – хэндл окна, который мы нашли выше,
Msg – сообщение (нажатие ЛКМ = 0x201, отпускание ЛКМ = 0x202, вращение колёсика = 0x020A,
wParam – 16-битный параметр функции (word параметр), будем использовать для передачи значения на сколько сдвинулось колёсико мыши,
lParam – 32-битный параметр функции (long параметр), будем использовать для передачи координат мыши.
Но как же затолкать несколько значений всего в два параметра? Для таких действий есть такое извращение как помещение разных значений в старшие и в младшие биты. Для таких фокусов можно использовать эту функцию:
public static IntPtr MakeParam(int low, int hight)
{
       return (IntPtr)((low & 0xFFFF) | (hight << 16));
}
Теперь мы можем вызвать эту функцию и отправить сообщение клика окну. Клик мыши – это наведение курсора, нажатие клавиши мыши и отпускание. Поэтому надо отправить три сообщения.
SendMessage(handle, WM_MOUSEMOVE, (IntPtr)0, MakeParam(x, y));
SendMessage(handle, WM_LBUTTONDOWN, (IntPtr)0, MakeParam(x, y));                    
SendMessage(handle, WM_LBUTTONUP, (IntPtr)0, MakeParam(x, y));
Для прокрутки колёсика:
SendMessage(handle, WM_MOUSEMOVE, (IntPtr)0, MakeParam(x, y));
SendMessage(handle, WM_MOUSEWHEEL, MakeParam(0, delta), MakeParam(x, y));
Где delta значение на которое должно быть прокручено колёсико.
Теперь программа должна уметь кликать в чужие окна и нажимать там кнопки. НО программа кликает, а вот кнопки то не нажимаются! Может ошибка в координатах? Нет, ошибка в том, что сообщение было отправлено не туда. Кнопка это не элемент главного окна, это его часть и в системе она числится как дочернее окно. По сути все элементы окна это дочерние окна, если, конечно, можно так выразиться.

Элементы окна можно посмотреть с помощью программы Spy++. Запустить её можно через Visual Studio, Сервис -> Spy++, хочу обратить внимание, что там присутствуют две программы (Spy++ и Spy++ (x64)), так вот даже в 64 битной системе следует запускать Spy++, т.к. Spy++ (x64) почему-то ограничен в функционале (возможно такой глюк присутствует только у меня). Вот пример работы программы для поиска окон (для поиска нужно нажать на мишень и передвигать её по экрану).


Скриншот показывающий, что контролы окна это отдельные элементы и тоже имеют свой хэндл.

Также с помощью этой программы можно просматривать все сообщения, которые получает то или иное окно. Для этого нужно перейти в запись сообщений (ctrl+M). Кстати не в любом окне каждый контрол является отдельным элементом. Например, если графика отрисована с помощью OpenGL, то само собой кнопки и другие контролы не могут быть отдельными элементами, т.к. просто отрисованы в графическом окне средствами GPU и не имеют своего хэндла. Всё это можно проверить через Spy++. В заключение хочу сказать, что WinApi и Spy++ хорошие помощники в написании программы для автоматизации каких-либо действий пользователя в конкретных программах.

Теги , , ,
Автор

Комментарии

Нет комментариев к данной статье.

Комментировать

Поля обозначенные как * требуются обязательно. Перед постингом всегда делайте просмотр своего комментария.




[
]
[
]