Итак, разобрался я, как эмулировать события мыши в GeckoFx.
Все достаточно просто.
Исходники в конце статьи
В JavaScript есть такой класс, как MouseEvent и метод initMouseEvent(). Вот с помощью них и еще парочки классов можно реализовать эмуляцию событий мыши в GeckoFx.
В движке уже реализованы данные классы и их просто нужно использовать в правильном порядке 🙂
Движок поддерживает следующие типы событий мыши:
- mousedown;
- mouseup;
- mousemove;
- mouseover;
- mouseout;
- contextmenu;
- dblclick;
- click.
С помощью следующего метода-расширения можно эмулировать события мыши:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
internal static class GeckoExtensionMethods { /// <summary> /// Эмулирует событие мыши /// </summary> /// <param name="webBrowser"></param> /// <param name="element">Элемент, которому будет отправляться событие</param> /// <param name="aTypeEvent">Тип события</param> /// <param name="screenX"></param> /// <param name="screenY"></param> /// <param name="clientX">Координата мыши X</param> /// <param name="clientY">Координата мыши Y</param> public static void MouseEventEmulation( this GeckoWebBrowser webBrowser, GeckoElement element, string aTypeEvent, int screenX, int screenY, int clientX, int clientY ) { nsIDOMEventTarget target = Xpcom.QueryInterface<nsIDOMEventTarget>( element.DomObject ); DomEventArgs evt = webBrowser.DomDocument.CreateEvent( "MouseEvent" ); DomMouseEventArgs mouseEvent = (DomMouseEventArgs)DomEventArgs.Create( evt.DomEvent ); mouseEvent.InitMouseEvent( aTypeEvent, true, true, webBrowser.Window, 0, screenX, screenY, clientX, clientY, false, false, false, false, 0, Gecko.DOM.DomEventTarget.Create( target ) ); target.DispatchEvent( mouseEvent.DomEvent ); } } |
Пример использования: движение мыши с точки 0,0 к какому-то элементу:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
/// <summary> /// Эмулирует движение мыши к указанному элементу (с точки 1,1) /// </summary> /// <param name="element"></param> /// <returns></returns> private async Task MoveMouseToElement( GeckoElement element ) { var startPoint = new Point( 1, 1 ); // определяем координаты конечной точки var endPoint = new Point( element.GetBoundingClientRect().Left, element.GetBoundingClientRect().Top ); // примитивное движение мышью к нужной точке do { // эмулируем событие mousemove this._webBrowser.MouseEventEmulation( this._webBrowser.Document.Body, "mousemove", 0, 0, startPoint.X, startPoint.Y ); if ( startPoint.X < endPoint.X ) { startPoint.X++; } if ( startPoint.Y < endPoint.Y ) { startPoint.Y++; } // задержка await TaskEx.Delay( 10 ); } while ( startPoint.X < endPoint.X || startPoint.Y < endPoint.Y ); } |
Для того, чтобы эмулировать движение мыши по всему документу, нужно передавать в метод MouseEventEmulation в качестве первого аргумента Document.Body (т.е. ссылку на весь документ), если же нужно сделать клик по элементу — передавать элемент, по которому нужно произвести клик 🙂
Рассмотрим простой пример, где будем заходить на страницу Google Translate, вводить в поле какой-то текст и нажимать на кнопку «Прослушать».
Создаем новый проект GeckoMouseEvents (.NET Framework v4), подключаем либы GeckoFx 33, устанавливаем Microsoft Async и скачиваем Xulrunner 33.
Атрибуты главной формы: Size = 627; 480, StartPosition = CenterScreen, Text = GeckoMouseEvents, WindowState = Maximized.
Бросаем на форму Panel (Location = 0;0, Size = 610; 25, Name = pnl1), кнопку на панель (Location = 0;0, Size = 75; 23, Name = btnStart, Text = Начнем).
Теперь создаем статический класс с методом, который будет осуществлять эмуляцию событий мыши. Назовем его GeckoExtensionMethods:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
internal static class GeckoExtensionMethods { /// <summary> /// Эмулирует указанное событие мыши /// </summary> /// <param name="webBrowser"></param> /// <param name="element">Элемент, которому будет отправляться событие</param> /// <param name="aTypeEvent">Тип события</param> /// <param name="screenX"></param> /// <param name="screenY"></param> /// <param name="clientX">Координата мыши X</param> /// <param name="clientY">Координата мыши Y</param> public static void MouseEventEmulation( this GeckoWebBrowser webBrowser, GeckoElement element, string aTypeEvent, int screenX, int screenY, int clientX, int clientY ) { nsIDOMEventTarget target = Xpcom.QueryInterface<nsIDOMEventTarget>( element.DomObject ); DomEventArgs evt = webBrowser.DomDocument.CreateEvent( "MouseEvent" ); DomMouseEventArgs mouseEvent = (DomMouseEventArgs)DomEventArgs.Create( evt.DomEvent ); mouseEvent.InitMouseEvent( aTypeEvent, true, true, webBrowser.Window, 0, screenX, screenY, clientX, clientY, false, false, false, false, 0, Gecko.DOM.DomEventTarget.Create( target ) ); target.DispatchEvent( mouseEvent.DomEvent ); } } |
Теперь создадим класс, в котором будет логика работы (загрузка страницы, ввод текста и т.д.). Назовем его EmulMouseEvents:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
internal class EmulMouseEvents { private readonly GeckoWebBrowser _webBrowser; // ссылка на браузер private readonly Timer _loadingTimer; // таймер загрузки private bool _loading; // указывает на статус загрузки public EmulMouseEvents( GeckoWebBrowser webBrowser ) { this._webBrowser = webBrowser; this._loadingTimer = new Timer { Interval = 5000 }; this._loadingTimer.Tick += this._loadingTimer_Tick; } private void _loadingTimer_Tick( object sender, EventArgs e ) { // останавливаем таймер this._loadingTimer.Stop(); // считаем, что загрузка завершилась this._loading = false; } /// <summary> /// Ожидает загрузки страницы /// </summary> private async Task WaitForLoading() { while ( this._loading ) { await TaskEx.Delay( 200 ); } } /// <summary> /// Загружает указанную страницу и ждет завершения загрузки /// </summary> /// <param name="url">Url страницы</param> private async Task Navigate( string url ) { this._loading = true; this._webBrowser.Navigate( url ); this._loadingTimer.Start(); await this.WaitForLoading(); } /// <summary> /// Печатает текст /// </summary> /// <param name="element">Куда печатать</param> /// <param name="text">Текст</param> /// <returns></returns> private async Task WriteText( GeckoElement element, string text ) { Random random = new Random(); var htmlEl = element as GeckoHtmlElement; if ( htmlEl == null ) { return; } for ( int i = 0; i < text.Length; i++ ) { htmlEl.InnerHtml = text.Substring( 0, i + 1 ); await TaskEx.Delay( random.Next( 100, 350 ) ); } } /// <summary> /// Эмулирует движение мыши к указанному элементу (с точки 1,1) /// </summary> /// <param name="element"></param> /// <returns></returns> private async Task MoveMouseToElement( GeckoElement element ) { var startPoint = new Point( 1, 1 ); // определяем координаты конечной точки var endPoint = new Point( element.GetBoundingClientRect().Left, element.GetBoundingClientRect().Top ); // примитивное движение мышью к нужной точке do { // эмулируем событие mousemove this._webBrowser.MouseEventEmulation( this._webBrowser.Document.Body, "mousemove", 0, 0, startPoint.X, startPoint.Y ); if ( startPoint.X < endPoint.X ) { startPoint.X++; } if ( startPoint.Y < endPoint.Y ) { startPoint.Y++; } // задержка await TaskEx.Delay( 10 ); } while ( startPoint.X < endPoint.X || startPoint.Y < endPoint.Y ); } /// <summary> /// Эмулирует нажатие левой кнопкной мыши на элемент /// </summary> /// <param name="element"></param> /// <returns></returns> private async Task MouseClickOnElement( GeckoElement element ) { // эмулируем нажатие на элемент // вместо mousedown и mouseup можно использовать click, но он не везде работает this._webBrowser.MouseEventEmulation( element, "mousedown", 0, 0, 0, 0 ); await TaskEx.Delay( 120 ); this._webBrowser.MouseEventEmulation( element, "mouseup", 0, 0, 0, 0 ); } /// <summary> /// Старт /// </summary> public async void Start() { // открываем страницу переводчика и ждем загрузки await this.Navigate( "http://translate.google.com/" ); // получаем поле для ввода текста GeckoElement textElement = this._webBrowser.Document.GetElementById( "source" ); // "плавный" набор поискового запроса const string query = "Hello, World!"; // вводим поисковый запрос await this.WriteText( textElement, query ); // получаем кнопку "прослушать" GeckoElement listenElement = this._webBrowser.Document.GetElementById( "gt-src-listen" ); // эмулируем движение мыши к кнопке "Прослушать" await this.MoveMouseToElement( listenElement ); // эмулируем нажатие на кнопку "Прослушать" await this.MouseClickOnElement( listenElement ); } } |
Точка входа здесь метод Start().
После начала открытия страницы происходит запуск таймера с интервалом в 5 секунд. По прошествии 5 секунд считаем, что страница загрузилась. Далее получаем поле для ввода текста с идентификатором source, выполняем набор текста, получаем кнопку «Прослушать» с идентификатором gr-src-listener, эмулируем движение к этому объекту (которое мы все равно не увидим 😀 но счетчики типа Яндекс.Метрика такое движение зафиксируют) и, после, эмулируем нажатие на кнопку.
Так-с, теперь весь этот код нужно вызвать. Переходим на главную форму, в конструкторе формы производим инициализацию GeckoFx:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
public FrmMain() { this.InitializeComponent(); // инициализация Xulrunner Xpcom.Initialize( Application.StartupPath + "\\xulrunner\\" ); this.InitializeComponent(); // создаем экземпляр класса GeckoWebBrowser this._webBrowser = new GeckoWebBrowser { Anchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top | AnchorStyles.Bottom, Width = this.Width, Height = this.Height - this.pnl1.Height - 5, Top = this.pnl1.Bottom + 5 }; // устанавливаем UserAgent браузера в Firefox 33 GeckoPreferences.User[ "general.useragent.override" ] = "Mozilla/5.0 (Windows NT 6.1; rv:33.0) Gecko/20150101 Firefox/33.0"; // добавляем контрол браузера на форму this.Controls.Add( this._webBrowser ); } |
И добавляем обработчик события нажатия на кнопку:
1 2 3 4 5 6 7 |
private void btnStart_Click( object sender, EventArgs e ) { // создаем экземпляр класса EmulMouseEvents var clickClass = new EmulMouseEvents( this._webBrowser ); // начинаем выполнение clickClass.Start(); } |
Не забываем добавить поле главной формы:
1 |
private readonly GeckoWebBrowser _webBrowser; |
Скачать исходники можно на GitHub.
Приветствую, закомментировал сам клик — //await this.MouseClickOnElement( listenElement );
Не наводит на прослушать, mousemove не работает!!! скачал исходник тоже самое! когда реальную мышь наводим на прослушать, стает активным значок, у Вашем примере этого не вижу! Или я туплю, чего-то не понимаю. И второе, нужно кликать по координатах ссылки, то есть по ширине ссылке и по длине, в разных местах нужно делать клик. Иногда попадаются системы которые секут наведение на ссылку клик по координатах… Считают что кликер! Подскажите пожалуйста как это организовать? нужно довести до оригинальной мыши! Спасибо за код…
1. А Вы знаете, что это за событие вообще «mousemove», прежде чем бросаться фразами «оно не работает»? И для какого элемента оно в коде эмулируется? И что его метрика фиксирует? И может быть такое, что двигло (или его порт) не умеет эмулировать, например, событие mouseenter, а «активным» значок может становится только при таком событии? И что если эмулировать событие mouseover для этого объекта, то появиться надпись «прослушать»?
2. Ну так кликайте, координаты указывать можно. Я же не запрещаю 🙂
П.С. Если нужно «довести до оригинальной мыши», то доводите. Исходники открыты, информация для старта есть. Я же ни за кого ботов писать не буду.
Респект автору, за проделанную работу.
Александр, тут в статье не говорилось о решении всех Ваших задач, к то му же по новым Вашим требованьям вообще не поднимался вопрос. Попробуйте через PostMessage (http://zhyk.ru/forum/showthread.php?t=970170) может решит Вашу проблему. А теперь к автору и все таки есть привязка к координатам обрабатываемого объекта(screenX, screenY, clientX, clientY). Может я ранее не правильно выразился так прошу прошения, я не программист, это только так для себя хобби в свободное от работы и домашних занятий время.
Ну конечно есть привязка к координатам. Как ее может не быть? Если мы эмулируем событие движения мыши для всего документа и нам нужно подвести указатель к какому-то объекту в этом документе, тот как мы сможем это сделать без координат?
Пробовал кликнуть по ссылке на сайте, не кликает! Вот например нажать на кнопку войти — Google Translate, тоже не нажимает( Подскажите пожалуйста, куда копать?
Нажимает отлично.
Здравствуйте.
Сталкивались ли вы с обработкой события, которое отвечает за открытие окна OpenFileDialog в GeckoFX?
Когда я попытался загрузить файл на сторонний сайт, то, оказалось, что средствами GeckoFX невозможно отловить событие upload window. Сталкивались ли вы с подобной ситуацией и можете ли подсказать решение?
Благодарю.
Сталкивался с перехватом события сохранения файла, но это нужно по сусекам поскребти и не факт, что в новых версиях будет работать.
Не работает. От слова вообще!
Прошу прощения. В 2010-й студии ассинхрон не воткнул. Всё рвботает.
Доброе время суток. У меня то же не выходит клик по ссылке, по кнопкам не проблема все отрабатывает на ура. Не могли бы Вы подсказать причину из за чего не выполняет переход, хотя бы по той же ссылке на вход в логин Google Translate.
GeckoElement listenElement = this._webBrowser.Document.GetElementById(«gb_70»);
await this.MoveMouseToElement( listenElement );
await this.MouseClickOnElement( listenElement );
может, что то не правильно делаю?
Попробуйте использовать для ссылок событие
click
, вместо комбинацииmousedown
иmouseup
.