Страницы

Поиск по вопросам

воскресенье, 15 декабря 2019 г.

Почему поток с компонентом WebBrowser в цикле приводит к неработоспособности и росту памяти

#c_sharp #webbrowser


Поток с WebBrowser после определённой итерации зависает в ожидании примерно на 5
минут в коде потока:

while (wb.ReadyState != WebBrowserReadyState.Complete) 
  { System.Windows.Forms.Application.DoEvents(); }


где значение состояния равно: 

wb.ReadyState == Uninitialized или Interactive


в итоге возвращает страницу "Navigation canceled" - либо  возвращает её уже с 5 минутными
задержками.


Почему компонент WebBrowser использует IE как 7-ой версии, если у меня в системе
стоит IE8? 
Почему компонент WebBrowser перестаёт отдавать страницы?
Почему объём память растёт приложения и в итоге занимает около 800 мб?


У меня стоит задача сохранить отображение страницы на жёсткий диск, используя консольное
приложение. Возможно можно как-то в pdf или ещё в какой формат сохранить, чтобы можно
было увидеть содержимое HTML странцы.

Я задавал вопрос здесь но ненашёл где написать продолжение:)

Буду рад любому ценному комментарию.

    class Program
    {
    static string url = "http://www.whatbrowser.org";
    static int width = 960, height = 1380;
    static int count = 0;

    [STAThread]
    static void Main(string[] args)
    {
        Console.WriteLine("hi");

        try
        {
            int number = 1000;
            for (int i = 0; i < number; i++)
            {
                var th = new Thread(obj => RunGenerateScreenshotThread((string)obj));
                th.SetApartmentState(ApartmentState.STA);
                th.Start(url);
                th.Join();

                count++;
            }

            Console.WriteLine("All successfully completed!");
            Console.ReadKey(true);
        }
        catch (Exception ex)
        {
            Console.WriteLine("Error: {0}", ex.ToString());
        }
    }

    static void RunGenerateScreenshotThread(string url)
    {
        try
        {
            using (WebBrowser wb = new WebBrowser())
            {
                // Set the size of the WebBrowser control
                wb.Width = width;
                wb.Height = height;

                //wb.DocumentCompleted += GenerateScreenshotCompleted;
                wb.Navigate(url);

                while (wb.ReadyState != WebBrowserReadyState.Complete) 
                  { System.Windows.Forms.Application.DoEvents(); }

                if (wb.DocumentTitle == "Navigation Canceled")
                    Console.WriteLine("Natigated ERROR {0}", count);
                else
                    Console.WriteLine("Natigated OK {0}", count);
                }
        }
        catch (Exception ex)
        {
            Console.WriteLine("Error: {0}", ex.ToString());
        }
        finally
        {
            Application.ExitThread();   // Stops the thread
        }
    }
}

    


Ответы

Ответ 1



1) нужно явно включить использования для своего приложения, прописав в реестре ключик. 2) что третий вопрос является ответом на второй. 3) а вот это сложный вопрос. сейчас практически все браузеры кушают очень много. upd Как бы я сделал. Отдельное приложение, которое получает два параметра - урл и куда сохранить (можно через параметры, можно через пайп). Его задача проста - загрузил, заскриншотил и отдал. Второе приложение в цикле запускает первое, передавая ему параметры (на первых порах это может быть обычный батник). плюсы: очень легко получается сделать для разных версий IE - просто два приложения, для одного прописан правильный webbrowser падения "скриншотерра" безопасно для основного приложения. так как приложение закрывается каждый раз, то это такой "костыль" против утечек. минусы: возможно некое падение скорости (но это ещё нужно проверить) нужно следить за дочерними процессами

Ответ 2



Браузер можно запускать в отдельном Thread + Task. Работает в WinForms и Console Application using System; using System.Windows.Forms; using System.Threading; using System.Threading.Tasks; static Task Navigate(Uri url, Func callback) { var ts = new TaskCompletionSource(); var t = new Thread(() => { var wb = new WebBrowser() { AllowNavigation=true, ScriptErrorsSuppressed=true }; wb.DocumentCompleted += (s, e) => ts.SetResult(callback(wb.Document)); wb.Navigate(url); while (!ts.Task.IsCompleted) Application.DoEvents(); }); t.TrySetApartmentState(ApartmentState.STA); t.Start(); return ts.Task; } var t = Navigate(Uri("http://ru.stackoverflow.com"), doc => { return doc.Body.InnerHtml; // передается в t.Result });

Ответ 3



Есть еще одна проблема вот в этих строчках. while (wb.ReadyState != WebBrowserReadyState.Complete) { System.Windows.Forms.Application.DoEvents(); } В нормальном состоянии, при отсутствии событий программа засыпает и не расходует время процессора. Но ваш цикл так делать не умеет. Цикл обработки событий надо "крутить" через Application.Run, обязательно передав ему любую форму - тогда с процессорным временем все будет в порядке. Должно получиться примерно так: static void RunGenerateScreenshotThread(string url) { try { using (WebBrowser wb = new WebBrowser()) { // Set the size of the WebBrowser control wb.Width = width; wb.Height = height; wb.DocumentCompleted += (o, e) => Application.ExitThread(); wb.Navigate(url); Application.Run(new Form { Visible = false }); if (wb.DocumentTitle == "Navigation Canceled") Console.WriteLine("Natigated ERROR {0}", count); else Console.WriteLine("Natigated OK {0}", count); } } catch (Exception ex) { Console.WriteLine("Error: {0}", ex.ToString()); } } Наличие формы предотвращает немедленной выход из цикла обработки сообщений.

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

Отправить комментарий