在线程中执行Webbrowser控件的屏幕抓取

3
我正在使用以下技术:

在新线程中使用WebBrowser控件

尝试对网页进行屏幕截图。当WebBrowser控件放置在WinForm上时,我已经能够成功地运行下面的代码。但是,在线程内运行时,它会提供桌面的任意图像而失败。请注意不要删除任何HTML标签并准确保留格式。
Thread browserThread = new Thread(() =>
{
    WebBrowser br = new WebBrowser();
    br.DocumentCompleted += webBrowser1_DocumentCompleted;
    br.ProgressChanged += webBrowser1_ProgressChanged;
    br.ScriptErrorsSuppressed = true;
    br.Navigate(url);
    Application.Run();
});
browserThread.SetApartmentState(ApartmentState.STA);
browserThread.Start();

private Image TakeSnapShot(WebBrowser browser)
{
    int width;
    int height;

    width = browser.ClientRectangle.Width;
    height = browser.ClientRectangle.Height;

    Bitmap image = new Bitmap(width, height);

    using (Graphics graphics = Graphics.FromImage(image))
    {
        Point p = new Point(0, 0);
        Point upperLeftSource = browser.PointToScreen(p);
        Point upperLeftDestination = new Point(0, 0);

        Size blockRegionSize = browser.ClientRectangle.Size;
        blockRegionSize.Width = blockRegionSize.Width - 15;
        blockRegionSize.Height = blockRegionSize.Height - 15;
        graphics.CopyFromScreen(upperLeftSource, upperLeftDestination, blockRegionSize);
    }

    return image;
}

显然,这是由于方法Graphics.CopyFromScreen()引起的,但我不知道是否有其他方法来解决这个问题。任何人都能提出如何解决这个问题的建议吗?或者我的唯一选择是创建一个表单,添加控件,使其可见,然后进行屏幕截取?由于显而易见的原因,我希望避免这种方法。
2个回答

9

您可以编写

private Image TakeSnapShot(WebBrowser browser)
{
     browser.Width = browser.Document.Body.ScrollRectangle.Width;
     browser.Height= browser.Document.Body.ScrollRectangle.Height;

     Bitmap bitmap = new Bitmap(browser.Width - System.Windows.Forms.SystemInformation.VerticalScrollBarWidth, browser.Height);

     browser.DrawToBitmap(bitmap, new Rectangle(0, 0, bitmap.Width, bitmap.Height));

     return bitmap;
}

一个完整的可用代码
var image = await WebUtils.GetPageAsImageAsync("http://www.stackoverflow.com");
image.Save(fname , System.Drawing.Imaging.ImageFormat.Bmp);

public class WebUtils
{
    public static Task<Image> GetPageAsImageAsync(string url)
    {
        var tcs = new TaskCompletionSource<Image>();

        var thread = new Thread(() =>
        {
            WebBrowser browser = new WebBrowser();
            browser.Size = new Size(1280, 768);

            WebBrowserDocumentCompletedEventHandler documentCompleted = null;
            documentCompleted = async (o, s) =>
            {
                browser.DocumentCompleted -= documentCompleted;
                await Task.Delay(2000); //Run JS a few seconds more

                Bitmap bitmap = TakeSnapshot(browser);

                tcs.TrySetResult(bitmap);
                browser.Dispose();
                Application.ExitThread();
            };

            browser.ScriptErrorsSuppressed = true;
            browser.DocumentCompleted += documentCompleted;
            browser.Navigate(url);
            Application.Run();
        });

        thread.SetApartmentState(ApartmentState.STA);
        thread.Start();

        return tcs.Task;
    }

    private static Bitmap TakeSnapshot(WebBrowser browser)
    {
         browser.Width = browser.Document.Body.ScrollRectangle.Width;
         browser.Height= browser.Document.Body.ScrollRectangle.Height;

         Bitmap bitmap = new Bitmap(browser.Width - System.Windows.Forms.SystemInformation.VerticalScrollBarWidth, browser.Height);

         browser.DrawToBitmap(bitmap, new Rectangle(0, 0, bitmap.Width, bitmap.Height));

         return bitmap;
    }
}

正是我所需要的。我寻找类似于WebBrowser.DrawToBitmap()的东西,但它在智能感知中没有出现(现在仍然没有),但编译正常。谢谢! - Maxim Gershkovich
1
此外,感谢您展示 System.Windows.Forms.SystemInformation。我之前从未见过它,显然非常有用。 - Maxim Gershkovich
太棒了,正是我所需要的。 - Tomer
没有 using 语句的代码示例。叹气. - Robert Harvey
@RobertHarvey 感谢 VS,它轻松解决了这个问题 :) - I4V
当然,如果你有 Resharper 的话。但即使是 Resharper 也不可能总是能读懂你的想法。 - Robert Harvey

-1
using (Graphics graphics = Graphics.FromImage(image))
    {
        Point p = new Point(0, 0);
        Point upperLeftSource = browser.PointToScreen(p);
        Point upperLeftDestination = new Point(0, 0);

        Size blockRegionSize = browser.ClientRectangle.Size;
        blockRegionSize.Width = blockRegionSize.Width - 15;
        blockRegionSize.Height = blockRegionSize.Height - 15;
        graphics.CopyFromScreen(upperLeftSource, upperLeftDestination, blockRegionSize);
    }

你真的需要using语句吗?

你还使用了return image,但是复制的图像如何分配给它呢?


是的,你需要一个using,除非你有充分的理由不处理IDisposable对象。至于图像,它是通过以下代码行进行分配的:Graphics graphics = Graphics.FromImage(image)。 - Maxim Gershkovich

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接