WebBrowser Control Document在 iframe 和 Javascript 完成后完成

本文关键字:Javascript iframe Control Document WebBrowser | 更新日期: 2023-09-27 18:35:16

我需要捕获生成的HTML的图像。我正在使用Alex Filipovici的出色解决方案:将HTML字符串转换为图像。它工作得很好,除非我尝试加载一个页面,该页面具有使用某些 Javascript 加载的 iframe。

       静态整数宽度 = 1024;        静态整数高度 = 768;        公共静态空隙捕获()        {            var html = @"'";            StartBrowser(html);        }        私有静态空 启动浏览器(字符串源)        {            var th = new Thread(() =>            {                var webBrowser = new WebBrowser();                webBrowser.Width = width;                webBrowser.Height = height;                webBrowser.ScrollBarsEnabled = false;                webBrowser.DocumentDone += webBrowser_DocumentCompleted;                webBrowser.DocumentText = source;                Application.Run();            });            日。SetApartmentState(ApartmentState.STA);            日。开始();        }        static void webBrowser_DocumentCompleted(对象发送器,WebBrowserDocumentCompleteEventArgs e)        {            var webBrowser = (WebBrowser)sender;            使用 (位图位图 = 新位图(宽度、高度))            {                webBrowser.DrawToBitmap(bitmap, new System.Drawing.Rectangle(0, 0, width, height));                位图。Save(@"image.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);            }            Application.Exit();        }

我知道可能没有确定的方法可以知道是否所有javascript都已经结束,iframe加载的变幻莫测以及DocumentDone被调用的次数与框架/iframe + 1一样多的事实。我可以用计数器或其他东西来处理 iframe 加载,但我想要的只是一个合理的延迟,所以 javascript 被加载了,我没有得到一个带有"加载"的图像,如下所示:https://i.stack.imgur.com/eGn2v.jpg

WebBrowser Control Document在 iframe 和 Javascript 完成后完成

如果您正在处理大量使用框架和 AJAX 的动态网页,那么没有完美的解决方案可以查找特定页面何时完成加载资源。您可以通过执行以下两件事来接近:

  • 处理页面的window.onload事件;
  • 然后异步轮询WebBrowser Busy 属性,并具有一些预定义的合理短超时。

例如,(查看 https://stackoverflow.com/a/19283143/1768303 的完整示例):

const int AJAX_DELAY = 2000; // non-deterministic wait for AJAX dynamic code
const int AJAX_DELAY_STEP = 500;
// wait until webBrowser.Busy == false or timed out
async Task<bool> AjaxDelay(CancellationToken ct, int timeout)
{
    using (var cts = CancellationTokenSource.CreateLinkedTokenSource(ct))
    {
        cts.CancelAfter(timeout);
        while (true)
        {
            try
            {
                await Task.Delay(AJAX_DELAY_STEP, cts.Token);
                var busy = (bool)this.webBrowser.ActiveXInstance.GetType().InvokeMember("Busy", System.Reflection.BindingFlags.GetProperty, null, this.webBrowser.ActiveXInstance, new object[] { });
                if (!busy)
                    return true;
            }
            catch (OperationCanceledException)
            {
                if (cts.IsCancellationRequested && !ct.IsCancellationRequested)
                    return false;
                throw;
            }
        }
    }
}

如果您不想使用 async/await ,则可以使用计时器实现相同的逻辑。

这是我在弄

乱了各种其他想法之后一直在使用的东西,这些想法最终变得复杂并具有竞争条件或需要 .Net 4.5(例如这个问题的答案)。

诀窍是在每个文档完成时重新启动秒表,并等待直到在某个阈值内没有文档完成。

为了使其更易于使用,我放入了一个扩展方法:

browser.NavigateAndWaitUntilComplete(uri);

我应该称它为NavigateUntilProbablyComplete()。这种方法的缺点是每次导航都有 250 毫秒的保证损失。我看到的许多解决方案都依赖于最终页面与 url 相同,这在我的方案中无法保证。

using System;
using System.Diagnostics;
using System.Threading;
using System.Windows.Forms;
namespace MyProject.Extensions
{
    public static class WebBrowserExtensions
    {
        const int CompletionDelay = 250;
        private class WebBrowserCompletionHelper
        {
            public Stopwatch LastCompletion;
            public WebBrowserCompletionHelper()
            {
                // create but don't start.
                LastCompletion = new Stopwatch();
            }
            public void DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
            {
                WebBrowser browser = sender as WebBrowser;
                if (browser != null)
                {
                    LastCompletion.Restart();
                }
            }
        }
        public static void NavigateAndWaitUntilComplete(this WebBrowser browser, Uri uri)
        {
            WebBrowserCompletionHelper helper = new WebBrowserCompletionHelper();
            try
            {
                browser.DocumentCompleted += helper.DocumentCompleted;
                browser.Navigate(uri);
                Thread.Sleep(CompletionDelay);
                Application.DoEvents();
                while (browser.ReadyState != WebBrowserReadyState.Complete && helper.LastCompletion.ElapsedMilliseconds < CompletionDelay)
                {
                    Thread.Sleep(CompletionDelay);
                    Application.DoEvents();
                }
            }
            finally
            {
                browser.DocumentCompleted -= helper.DocumentCompleted;
            }
        }
    }
}