Форум программистов, компьютерный форум, киберфорум
Наши страницы
Nikto
Войти
Регистрация
Восстановить пароль
Оценить эту запись

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

Запись от Nikto размещена 07.08.2016 в 13:39
Обновил(-а) Nikto 08.06.2017 в 10:39

Разрабатывая очередную программу, столкнулся с рядом проблем, которые таит в себе WinApi.
Передо мной стояла задача написать программу, которая будет выполнять действия в окнах за юзера в определённый момент. Программа должна научиться кликать мышкой и крутить колёсиком в чужих окнах.
В этом посте постараюсь изложить то, как я решил задачу. Я не буду здесь выкладывать сигнатуры всех используемых мной функций, т.к. это можно найти на msdn, наоборот я покажу какие функции, в какой ситуации лучше использовать и как это лучше сделать. Возможно, кому-нибудь пригодится данный опыт.
События мыши это по сути сообщения, которые пользователь посылает в систему. Эти события можно сымитировать программно. Для этого есть две замечательные функции из WinApi SendMessage и PostMessage. Задача у них одна – отправить сообщение в целевое окно. Различие в том, что первая ждёт пока целевое окно обработает отправленное сообщение, вторая не дожидается этого, она просто отсылает это сообщение больше ни о чём не беспокоясь.
Разберём теперь как же найти это целевое окно. Я считаю, что хороший способ – найти окно по его заголовку. Для такого поиска есть функция WinApi FindWindow. Для написания программы я использовал c#, и как оказалось в нём достаточно функционала найти хэндл окна по заголовку.
C#
1
2
3
4
5
6
7
8
9
10
11
12
//получаем массив всех процессов системы
Process[] procs = Process.GetProcesses();
//перебираем полученный массив
foreach (Process p in procs)
{
        //если заголовок окна содержит строку поиска
        if (p.MainWindowTitle.IndexOf(TITLE_SEARCH) != -1)
        {
                //то запоминаем хэндл окна
                IntPtr handle = p.MainWindowHandle;
        }
}
Зная хэндл можем отправить клик мыши в это окно. Здесь уже средствами .NET не обойтись, поэтому начинаем юзать WinApi функции. Чтобы подключить функцию SendMessage к c# программе нужно в код прописать следующее:
C#
1
2
[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 параметр), будем использовать для передачи координат мыши.
Но как же затолкать несколько значений всего в два параметра? Для таких действий есть такое извращение как помещение разных значений в старшие и в младшие биты. Для таких фокусов можно использовать эту функцию:
C#
1
2
3
4
public static IntPtr MakeParam(int low, int hight)
{
       return (IntPtr)((low & 0xFFFF) | (hight << 16));
}
Теперь мы можем вызвать эту функцию и отправить сообщение клика окну.
Клик мыши – это наведение курсора, нажатие клавиши мыши и отпускание. Поэтому надо отправить три сообщения.
C#
1
2
3
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));
Для прокрутки колёсика:
C#
1
2
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) почему-то ограничен в функционале (возможно такой глюк присутствует только у меня).
Вот пример работы программы для поиска окон (для поиска нужно нажать на мишень и передвигать её по экрану).
Нажмите на изображение для увеличения
Название: блог пост 4 1.png
Просмотров: 201
Размер:	111.5 Кб
ID:	3937

Скриншот показывающий, что контролы окна это отдельные элементы и тоже имеют свой хэндл.
Нажмите на изображение для увеличения
Название: блог пост 4 2.png
Просмотров: 358
Размер:	175.9 Кб
ID:	3939

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

Источник: https://catplusplus.ru/blog/press_buttons_in_windows_way_winapi
Размещено в Без категории
Просмотров 1201 Комментарии 6
Всего комментариев 6
Комментарии
  1. Старый комментарий
    Аватар для Avazart
    А что С# нет обверток?
    Цитата:
    Например, если графика отрисована с помощью OpenGL, то само собой кнопки и другие контролы не могут быть отдельными элементами, т.к. просто отрисованы в графическом окне средствами GPU и не имеют своего хэндла.
    Где то я видел на форуме кто-то приводил пример на C# для таких случаев, там по моему шла речь про "отлов" контролов в программе на Qt.
    Запись от Avazart размещена 08.08.2016 в 17:06 Avazart вне форума
  2. Старый комментарий
    Аватар для Nikto
    Цитата:
    Сообщение от Avazart Просмотреть комментарий
    А что С# нет обверток?
    А фиг знает есть или нет)
    Цитата:
    Сообщение от Avazart Просмотреть комментарий
    Где то я видел на форуме кто-то приводил пример на C# для таких случаев, там по моему шла речь про "отлов" контролов в программе на Qt.
    А вот это интересно. Как это примерно делалось, не помните? Как же их отловить, если это просто по сути картинка?
    Запись от Nikto размещена 08.08.2016 в 20:05 Nikto на форуме
  3. Старый комментарий
    Запись от Avazart размещена 03.01.2017 в 22:29 Avazart вне форума
    Обновил(-а) Avazart 03.01.2017 в 22:31
  4. Старый комментарий
    Аватар для Nikto
    Цитата:
    Сообщение от Avazart Просмотреть комментарий
    Правильно ли я понимаю, что ничего не выйдет, если элемент отрисован не через WinAPI, а например через OpenGL?
    Запись от Nikto размещена 04.01.2017 в 13:28 Nikto на форуме
  5. Старый комментарий
    Аватар для Avazart
    Я лично не знаю, у меня очень маленький опыт работы с С#, но читайте sau:
    Цитата:
    Сообщение от sau Посмотреть сообщение
    UI Automation не использует hwnd напрямую , информацию по UI предоставляет соответствующий провайдер , так оно легко справляется с WPF приложениями , где тоже чистая графика.
    Я обратил на это внимание по тому как мне тоже интересно как оно работает.
    Запись от Avazart размещена 04.01.2017 в 21:06 Avazart вне форума
  6. Старый комментарий
    Аватар для Nikto
    Цитата:
    Сообщение от Avazart Просмотреть комментарий
    Я лично не знаю, у меня очень маленький опыт работы с С#, но читайте sau:

    Я обратил на это внимание по тому как мне тоже интересно как оно работает.
    Попробовал код из темы, очень хорошо работает, с многих окон тянет текст, но из opengl приложения ни с одного элемента не может вытянуть.
    Запись от Nikto размещена 06.01.2017 в 13:53 Nikto на форуме
 
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2019, vBulletin Solutions, Inc.
Рейтинг@Mail.ru