我需要捕获生成的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
如果您正在处理大量使用框架和 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;
}
}
}
}