【问题标题】:Prevent WebBrowser from causing the UI to freeze while navigating?防止 WebBrowser 在导航时导致 UI 冻结?
【发布时间】:2018-08-12 07:20:45
【问题描述】:

我面临的问题是,在处理WebBrowser 控件时(无论它是否可见),它会导致 UI 在导航时冻结一小段时间,当必须按顺序打开多个 URL 时,这会变得非常明显且不可靠。

我目前正在使用 Noseratio's NavigateAsync 扩展方法以静默方式和异步方式导航到多个 URL:(请随意跳过阅读代码并继续提问)

public static async Task<string> NavigateAsync(this WebBrowser webBrowser, string url, CancellationToken token)
{
    var tcs = new TaskCompletionSource<bool>();
    WebBrowserDocumentCompletedEventHandler handler = (s, arg) => tcs.TrySetResult(true);

    using (token.Register(() => { webBrowser.Stop(); tcs.TrySetCanceled(); }, true))
    {
        webBrowser.DocumentCompleted += handler;
        try
        {
            webBrowser.Navigate(url);
            await tcs.Task; // wait for DocumentCompleted
        }
        finally
        {
            webBrowser.DocumentCompleted -= handler;
        }
    }

    var documentElement = webBrowser.Document.GetElementsByTagName("html")[0];
    var html = documentElement.OuterHtml;
    while (true)
    {
        await Task.Delay(POLL_DELAY, token);
        if (webBrowser.IsBusy)
            continue;

        var htmlNow = documentElement.OuterHtml;
        if (html == htmlNow) break; 

        html = htmlNow;
    }

    token.ThrowIfCancellationRequested();
    return html;
}

但即使是最简单的代码如下:

WebBrowser wb = new WebBrowser() { ScriptErrorsSuppressed = true };
wb.Navigate("https://www.google.com/");

..还是一样的效果。

这是一个快速的demo video,用最简单的代码显示问题。

我还尝试让 WebBrowser 在不同的 STA 线程上运行,但仍然没有成功。

那么,有没有办法在处理WebBrowser 时避免冻结?


在您建议将其替换为 HttpClientWebClientHTMLAgilityPack 之前,请注意我使用 WebBrowser 是为了获取显示的文本,其格式尽可能接近它在浏览器(即尽可能接近手动选择和复制文本)。我尝试(或在线找到)不使用浏览器的每个解决方案都无法实现这一点,即使 the one that produced the closest result 也不够好。

【问题讨论】:

  • 嗯,这根本不是一个常见的抱怨,WebBrowser 只是本机代码的包装器,它在引擎盖下有大量线程。 GetElementsByTagName() 调用在大文档上肯定会很昂贵,因为它检索 everything。使用分析器查看费用的去向,也可以分析本机代码。并暂时禁用反恶意软件,因为它在做任何互联网事情时都会起作用。并考虑在 STA 工作线程上执行所有这些操作,因为您不关心视图。
  • @HansPassant 我相信GetElementsByTagName 不是导致问题的原因。请检查上面写着“但即使是最简单的代码,如下所示..”的部分。.
  • 如果没有其他帮助 - 您可以在单独的 UI 线程中运行该代码,这样它(希望)不会冻结您的主 UI 线程。
  • @HansPassant 这似乎是一个不常见的投诉,因为我没有发现很多关于这个特定问题的问题,但我在两台不同的 PC 上进行了尝试并且具有相同的行为。也许人们大多使用停靠在窗口中的 WebBrowser,所以页面加载时发生的事情并不重要?无论如何,这是一个30sec demo video
  • @Evk 感谢您的建议。我已经尝试过了,问题中提到了。

标签: c# .net winforms asynchronous webbrowser-control


【解决方案1】:

我可以确认,当您加载 WebBrowser 控件时,UI 会冻结片刻,如果您使用 WebBrowser 控件的多个实例来加载多个 url,那么滞后的 UI 很烦人,您无法与主界面交互窗户。

要重现问题,可以使用以下代码:

string google = "http://www.google.com";
var urls = Enumerable.Range(1, 100).Select(x => google).ToList();
foreach (var url in urls)
{
    var w = new WebBrowser() { ScriptErrorsSuppressed = true };
    w.DocumentCompleted += (obj, args) =>
        {
            var txt = ((WebBrowser)obj).DocumentText;
            this.textBox1.Text = DateTime.Now.ToString() + Environment.NewLine
                + txt.Substring(1, 200) + "...";
        };
    w.Navigate(url);
}

要解决这个问题,您可以创建一个方法,在另一个线程中加载WebBrowser 控件并返回一个Task&lt;string&gt;,它在浏览器文档完成时完成。我在this post 中创建了一个BrowserBasedWebScraper,您可以使用它在幕后获取WebBrowser 控件的内容,而不会出现UI 滞后:

string google = "http://www.google.com";
var urls = Enumerable.Range(1, 100).Select(x => google).ToList();
foreach (var url in urls)
{
    var txt = await BrowserBasedWebScraper.LoadUrl(url);
    this.textBox1.Text = DateTime.Now.ToString() + Environment.NewLine
        + txt.Substring(1, 200) + "...";
}

您也可以通过repositorydownload 提供一个工作示例。

【讨论】:

    猜你喜欢
    • 2012-08-02
    • 2018-10-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多