Здравствуйте. Недавно подключил в проект geckofx 60 вместо webbrowser, т.к. он совсем перестал адекватно работать с JS.
На сайте есть форма с разнообразными полями, в том числе и полем типа file. Мне нужно вставить в него файл, находящийся в папке на локальном диске. Я чуть-чуть поправил под gecko метод, который использовал в webbrowser, тоже предварительно найденный на просторах интернета.
//Вставляем торрент
async Task PopulateInputFile(GeckoHtmlElement file)
{
file.Focus();
string a = Properties.Settings.Default.diskString;
// delay the execution of SendKey to let the Choose File dialog show up
var sendKeyTask = Task.Delay(500).ContinueWith((_) =>
{
// this gets executed when the dialog is visible
SendKeys.Send(a + getTorrent + "{ENTER}");
}, TaskScheduler.FromCurrentSynchronizationContext());
file.Click(); // this shows up the dialog
await sendKeyTask;
// delay continuation to let the Choose File dialog hide
await Task.Delay(500);
}
//вставляем постер
async Task PopulateInputFile_poster(GeckoHtmlElement file_poster)
{
file_poster.Focus();
// delay the execution of SendKey to let the Choose File dialog show up
var sendKeyTask = Task.Delay(500).ContinueWith((_) =>
{
// this gets executed when the dialog is visible
SendKeys.Send(Properties.Settings.Default.diskString + "poster.jpg" + "{ENTER}");
}, TaskScheduler.FromCurrentSynchronizationContext());
file_poster.Click(); // this shows up the dialog
await sendKeyTask;
// delay continuation to let the Choose File dialog hide
await Task.Delay(500);
}
}
async Task Populate()
{
var elements = geckoWebBrowser1.Document.GetElementsByTagName("input");
// торрент
foreach (GeckoHtmlElement file in elements)
{
if (file.GetAttribute("name") == "file")
{
file.Focus();
await PopulateInputFile(file);
}
}
//постер
foreach (GeckoHtmlElement file_poster in elements)
{
if (file_poster.GetAttribute("name") == "screen")
{
file_poster.Focus();
await PopulateInputFile_poster(file_poster);
}
}
}
И если в webbrowser он работал без каких-либо нареканий, то здесь есть проблема с тем, что он не всегда фиксирует нажатие клавиши enter, в результате чего диалоговое окно остается висеть, а остальные появляются поверх него. Можно конечно, просто потом вручную нажать enter, но хотелось бы довести весь функционал до автоматизма. Отмечу, что иногда срабатывает все как надо и все поля на форме заполняются. Но это происходит редко. Чаще заполняется поле с торрентом, а остальные уже как когда.
Еще заметил, что раньше, если диалоговое окно появлялось с предупреждением, что по указанному пути нет файла, оно висело, а все остальные появлялись только после его закрытия. Сейчас же даже с ошибкой остальные открываются поверх. Как будто какой-то поток блокировался при работе, а сейчас нет…
В старом Awesomium я вызывал клик по координатам кнопки (координаты из .getBoundingClientRect() JS, а для клика по координатам в Awesomium была функция + событие обработки этого диалога, из JS наверно нельзя по file в целях безопасности).
А Селениум точно не подходит?)
гугл говорит, что вроде бы до 60 версии работает так:
GeckoHtmlElement el = webbrowser.DomDocument.GetElementsByTagName("input").FirstOrDefault(elz => elz.GetAttribute("type") == "file"); //inpout type file element
var fileNames = new IntPtr[1];
fileNames[0] = new Gecko.CustomMarshalers.WStringMarshaler().MarshalManagedToNative(file); //file = path to file you want to upload
var domInput = Xpcom.QueryInterface<nsIDOMHTMLInputElement>(el.DOMHtmlElement);
domInput.MozSetFileNameArray(fileNames, (uint)fileNames.Length);
Marshal.ReleaseComObject(domInput);
DomEventArgs ev = webbrowser.Document.CreateEvent("HTMLEvents");
var webEvent = new Event(webbrowser.Window.DomWindow, ev.DomEvent as nsISupports);
webEvent.InitEvent("change", true, true);
el.GetEventTarget().DispatchEvent(ev);
new Gecko.CustomMarshalers.WStringMarshaler().CleanUpNativeData(fileNames[0]); //delete everything
Вот тоже хотел его изначально использовать, но сайт в нем обрезан наполовину))
Под селениум придется слишком много менять. А в приоритете, чтобы сайт отображался внутри формы самого приложения. Насколько знаю, Селениум открывает полноценный браузер.
Тоже натыкался на этот код. Видимо, из-за того, что у меня 60 версия, он не работает - ругается на MozSetFileNameArray, что nsIDOMHTMLInputElement не содержит такой метод. Нагуглил вот это
Получилось такое:
var domInput = Xpcom.QueryInterface<HTMLInputElement>(el.DOMHtmlElement);
domInput.MozSetFileNameArray(fileNames, (uint)fileNames.Length);
Но теперь ругается, что метод не принимает 2 параметра, а лишь 1. При этом, он обязательно должен быть string.
Поэтому, похоже, данный способ не актуален в моем случае.
Компьютер, на котором используют программу, достаточно древний. Поэтому работа с полноценным браузером медленнее, чем с одной страницей на форме. Да и я сам с селениумом почти никогда не работал, сложновато будет переписать весь имеющийся функционал. Но там есть метод для выбора файла?
В nuget библиотека обновляется, хотя на сайте firefox видел плашку о том, что технологию не развивают. Как-то это странно и не очень понятно.
Что касается предыдущих версий, то это 45-я, я так понимаю. Ее ставил, но в ней ajax-окна, как и в webbrowser, почему-то не открываются - никакой реакции на нажатия кнопок.
01a92fc Support string sequences when calling webidl interfaces. Add MozSetFileNameArray support, which uses this.
может быть в следующем релизе заработает код выше.
Может это сравнение с древним WebBrowser (IE)?
Думаю не должно быть большой разницы между встроенным Фаерфоксом и обычным Фаерфоксом с одной вкладкой.
И я спрашивал не про это, а зачем браузер выводить если автоматизация нужна, а не взаимодействие с ним пользователя.
Так вроде бы работает:
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Support.UI;
using SeleniumExtras.WaitHelpers;
using System;
using System.Threading;
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
using (var webDriver = new ChromeDriver())
{
webDriver.Url = "https://html.com/input-type-file/";
var inputSelector = By.Id("fileupload");
var wait = new WebDriverWait(webDriver, TimeSpan.FromSeconds(30));
wait.Until(ExpectedConditions.ElementExists(inputSelector));
var input = webDriver.FindElement(inputSelector);
input.SendKeys(@"C:\Users\Alex\Pictures\1.jpg");
Thread.Sleep(30000);
}
}
}
}