Заполнение input type="file" программно

Здравствуйте. Недавно подключил в проект 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);
            }
}
            }

Вызываю так:

Populate().ContinueWith((_) =>
        {

        }, TaskScheduler.FromCurrentSynchronizationContext());

И если в 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.

Поэтому, похоже, данный способ не актуален в моем случае.

А для чего?

Селениумом можно скриншоты выводить например, и есть Headless Хром, чтоб сам браузер не показывать.

Можно предыдущую попробовать, вряд ли там большая разница в поддержке стандартов, и всё равно ж как я понял проект больше не развивается.

Компьютер, на котором используют программу, достаточно древний. Поэтому работа с полноценным браузером медленнее, чем с одной страницей на форме. Да и я сам с селениумом почти никогда не работал, сложновато будет переписать весь имеющийся функционал. Но там есть метод для выбора файла?

В nuget библиотека обновляется, хотя на сайте firefox видел плашку о том, что технологию не развивают. Как-то это странно и не очень понятно.
Что касается предыдущих версий, то это 45-я, я так понимаю. Ее ставил, но в ней ajax-окна, как и в webbrowser, почему-то не открываются - никакой реакции на нажатия кнопок.

Там мелкие исправления, но новой версии Фаерфокса (70+, …) не будет.

Кстати, в последних коммитах пишут

01a92fc Support string sequences when calling webidl interfaces. Add MozSetFileNameArray support, which uses this.

:thinking: может быть в следующем релизе заработает код выше.


Может это сравнение с древним WebBrowser (IE)?
Думаю не должно быть большой разницы между встроенным Фаерфоксом и обычным Фаерфоксом с одной вкладкой.

И я спрашивал не про это, а зачем браузер выводить если автоматизация нужна, а не взаимодействие с ним пользователя.
:arrow_down:


Так вроде бы работает:

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);

            }
        }
    }
}

Спасибо, попробую поработать с этим. Может действительно будет лучше.