【问题标题】:WebBrowser Html Document to ImageWebBrowser Html 文档到图像
【发布时间】:2020-06-28 13:17:29
【问题描述】:

我正在尝试制作网页图像,但有些页面显示为白页。

在注册表编辑器中浏览 \HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION\ 并在其中添加:

  • 十进制值为 11000 的 WindowsFormsApp1.exe

  • 十进制值为 11000 的 WindowsFormsApp1.vshost.exe

这是我的代码:

using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;

namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
    Dictionary<Uri, Bitmap> browserShots = new Dictionary<Uri, Bitmap>();
    WebBrowser browser = new WebBrowser();
    public Form1()
    {
        InitializeComponent();
        browser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(browser_DocumentCompleted);
    }
    //=========================================MADE BY JIMY====================================
    private void browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
    {
        var browser = sender as WebBrowser;
        if (browser.ReadyState != WebBrowserReadyState.Complete) return;

        var bitmap = WebBrowserExtender.DrawContent(browser);
        if (bitmap != null)
        {
            if (!browserShots.ContainsKey(browser.Url))
                browserShots.Add(browser.Url, bitmap);
            else
            {
                browserShots[browser.Url]?.Dispose();
                browserShots[browser.Url] = bitmap;
            }
            // Show the Bitmap in a  PictureBox control, eventually
            pictureBox1.Image = browserShots[browser.Url];
        }
    }
    public class WebBrowserExtender
    {
        public static Bitmap DrawContent(WebBrowser browser)
        {
            if (browser.Document == null) return null;
            Size docSize = Size.Empty;
            Graphics g = null;
            var hDc = IntPtr.Zero;

            try
            {
                docSize.Height = (int)((dynamic)browser.Document.DomDocument).documentElement.scrollHeight;
                docSize.Width = (int)((dynamic)browser.Document.DomDocument).documentElement.scrollWidth;
                docSize.Height = Math.Max(Math.Min(docSize.Height, 32750), 1);
                docSize.Width = Math.Max(Math.Min(docSize.Width, 32750), 1);

                var previousSize = browser.ClientSize;
                browser.ClientSize = new Size(docSize.Width, docSize.Height);

                var bitmap = new Bitmap(docSize.Width, docSize.Height, PixelFormat.Format32bppArgb);
                g = Graphics.FromImage(bitmap);
                var rect = new RECT(0, 0, bitmap.Width, bitmap.Height);
                hDc = g.GetHdc();
                var view = browser.ActiveXInstance as IViewObject;
                view.Draw(1, -1, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, hDc, ref rect, IntPtr.Zero, IntPtr.Zero, 0);
                browser.ClientSize = previousSize;
                return bitmap;
            }
            catch
            {
                // This catch block is like this on purpose: nothing to do here
                return null;
            }
            finally
            {
                if (hDc != null) g?.ReleaseHdc(hDc);
                g?.Dispose();
            }
        }

        [ComImport]
        [Guid("0000010D-0000-0000-C000-000000000046")]
        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        interface IViewObject
        {
            void Draw(uint dwAspect, int lindex, IntPtr pvAspect, [In] IntPtr ptd,
                      IntPtr hdcTargetDev, IntPtr hdcDraw, ref RECT lprcBounds,
                      [In] IntPtr lprcWBounds, IntPtr pfnContinue, uint dwContinue);
        }

        [StructLayout(LayoutKind.Sequential, Pack = 4)]
        struct RECT
        {
            public int Left;
            public int Top;
            public int Right;
            public int Bottom;
            public RECT(int left, int top, int width, int height)
            {
                Left = left; Top = top; Right = width; Bottom = height;
            }
        }
    }
    //=========================================MADE BY JIMY====================================}

    private void button1_Click(object sender, EventArgs e)
    {
        browser.Navigate(textBox1.Text, null, null, "User-Agent: User agent");
    }
}
}

【问题讨论】:

  • 谢谢,但这并没有解决我的问题
  • 您没有阅读或应用这些注释中描述的内容。不考虑框架/IFrame,这:Thread thread = new Thread(...) ... while (browser.ReadyState != WebBrowserReadyState.Complete) { Application.DoEvents(); } 被构建为以多种不同方式失败。
  • 对不起,我想我误解了你的建议。我试过if (browser.ReadyState! = WebBrowserReadyState.Complete) return;,但没有任何改变。现在我尝试删除thread,但也没有结果,如果我删除它while (browser.ReadyState! = WebBrowserReadyState.Complete) {Application.DoEvents ();},我会崩溃。代码的变化在上面。

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


【解决方案1】:

为了打印WebBrowser Control的Html内容,有几点需要考虑:

  1. 我们需要使用 WebBrowser 的 DocumentCompleted 事件来确定当前 Document 的加载和渲染时间
  2. 单个文档可能(将)包含多个子文档,通常包含在 Frames/IFrames 中。每个 IFrame 都包含自己的 Document:当 IFrame 中包含的 Document 被加载时,DocumentCompleted 被触发。这意味着当 WebBrowser 导航到 URL 时,可以并且将多次引发该事件。

    这里的注释解释更多:How to get an HtmlElement value inside Frames/IFrames?

  3. WebBrowser 的托管属性并不总是反映 DOM 的真实值。比如 Html Document 的实际尺寸,当渲染完成时,并没有体现在任何地方,所以我们需要自己从 DOM 中获取这些度量。当前 DOM 渲染的维度被引用:

    [WebBrowser].Document.DomDocument.documentElement.scrollHeight;
    [WebBrowser].Document.DomDocument.documentElement.scrollWidth;
    

    见:Measuring Element Dimension and Location with CSSOM in Windows Internet Explorer

  4. WebBrowser 控件DrawToBitmap() 方法派生自Control,但实际上并没有像我们预期的那样实现。这同样适用于其他控件:已知 RichTextBox 在使用此方法时会打印空白内容。

  5. Html 文档可能大于位图支持的最大大小。还有一个更微妙的内存限制:Bitmap 对象需要将其内容存储在连续的内存空间中,因此 Bitmap 的大小限制实际上很难预先确定,并且可能会在我们意想不到的情况下导致异常。
  6. WebBrowser 控件的 Emulation Feature 必须设置为 Internet Explorer 11。请参阅:
    How can I get the WebBrowser control to show modern contents?
    Web browser control emulation issue (FEATURE_BROWSER_EMULATION)

要继续,首先订阅 WebBrowser 控件的 DocumentCompleted 事件。

Dictionary&lt;Uri, Bitmap&gt; 在这里用于存储表示会话中访问的 URL 的 Html 内容的位图。
当引发DocumentCompleted 事件时,如果当前 URL 以前从未被访问过,我们会向 Dictionary 添加一个新元素。
如果Uri 已存储,我们更新了相关的位图对象,因此集合中仅存在 Html 文档的最新快照。

我正在使用支持类来处理位图的创建,并声明用于从当前ISurfacePresenter 生成位图的本机 COM 接口。
由于WebBrowser 控件强制VIEW_OBJECT_COMPOSITION_MODE_LEGACY 用作所有站点的CompositionMode,内部GetPrintBitmap 方法在这种情况下调用IViewObject InterfaceDraw() 方法,我们也是如此。

要打印当前 Html 文档的内容(所有内容),调用 DrawContent(WebBrowser browser) WebBrowserExtender 类的静态方法:

Dictionary<Uri, Bitmap> browserShots = new Dictionary<Uri, Bitmap>();

private void browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
    var browser = sender as WebBrowser;
    if (browser.ReadyState != WebBrowserReadyState.Complete) return;

    var bitmap = WebBrowserExtender.DrawContent(browser);
    if (bitmap != null) {
        if (!browserShots.ContainsKey(browser.Url)) {
            browserShots.Add(browser.Url, bitmap);
        }
        else {
            browserShots[browser.Url]?.Dispose();
            browserShots[browser.Url] = bitmap;
        }
        // Show the Bitmap in a  PictureBox control, eventually
        [PictureBox].Image = browserShots[browser.Url];
    }
}

WebBrowserExtender 支持类:

using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Windows.Forms;

public class WebBrowserExtender
{
    public static Bitmap DrawContent(WebBrowser browser)
    {
        if (browser.Document == null) return null;
        Size docSize = Size.Empty;
        Graphics g = null;
        var hDc = IntPtr.Zero;

        try {
            docSize.Height = (int)((dynamic)browser.Document.DomDocument).documentElement.scrollHeight;
            docSize.Width = (int)((dynamic)browser.Document.DomDocument).documentElement.scrollWidth;

            var screenWidth = Screen.FromHandle(browser.Handle).Bounds.Width;
            docSize.Width = Math.Max(Math.Min(docSize.Width, screenWidth), 1);
            docSize.Height = Math.Max(Math.Min(docSize.Height, 32750), 1);

            var previousSize = browser.ClientSize;
            browser.ClientSize = new Size(docSize.Width, docSize.Height);

            var bitmap = new Bitmap(docSize.Width, docSize.Height, PixelFormat.Format32bppArgb);
            g = Graphics.FromImage(bitmap);
            var rect = new RECT(0, 0, bitmap.Width, bitmap.Height);
            hDc = g.GetHdc();
            var view = browser.ActiveXInstance as IViewObject;
            view.Draw(1, -1, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, hDc, ref rect, IntPtr.Zero, IntPtr.Zero, 0);
            browser.ClientSize = previousSize;
            return bitmap;
        }
        catch {
            // This catch block is like this on purpose: nothing to do here
            return null;
        }
        finally {
            if (hDc != null) g?.ReleaseHdc(hDc);
            g?.Dispose();
        }
    }

    [ComImport]
    [Guid("0000010D-0000-0000-C000-000000000046")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    interface IViewObject
    {
        void Draw(uint dwAspect, int lindex, IntPtr pvAspect, [In] IntPtr ptd, 
                  IntPtr hdcTargetDev, IntPtr hdcDraw, ref RECT lprcBounds, 
                  [In] IntPtr lprcWBounds, IntPtr pfnContinue, uint dwContinue);
    }

    [StructLayout(LayoutKind.Sequential, Pack = 4)]
    struct RECT
    {
        public int Left;
        public int Top;
        public int Right;
        public int Bottom;

        public RECT(int left, int top, int width, int height)
        {
            Left = left; Top = top; Right = width; Bottom = height;
        }
    }
}

它是这样工作的:

完整的文档被捕获。当然,也可以将位图限制为特定的最大/最小大小,以仅捕获 Html 文档的一部分。

Sample WinForms Project 在 Google 云端硬盘上。

【讨论】:

  • 哇,非常感谢你。这太棒了,但这仍然没有等待我的on.load function()...我找到了适合我的this 解决方案,但是没有MessageBox.Show(); 它不能正确显示。我尝试过任务和威胁延迟,但没有任何效果。你能知道如何解决这个问题吗?抱歉这么愚蠢的问题,我是编程c#新手
  • 看动画。它就是这样工作的(与您在此处看到的完全相同的代码)。
  • 在我的情况下,它可能不会是尺寸问题,但它会是别的问题,因为当我使用我发送的方法尝试它时,一切正常,直到我删除该行MessageBox.Show("window.onload was fired");。这对我来说没有意义
  • 所以,不要使用这种方法,而是使用我在此处发布的内容,因为它有效。最重要的是,不要使用Application.DoEvents()。永远。
  • 我们不明白 :) 你的方法对我不起作用,我使用它时仍然只能看到白页,或者我可能用错了。我发送的内容有效,但仅适用于消息框,当我想制作多个页面的图像时,该消息框很烦人。
【解决方案2】:

尝试像这样设置用户代理

browser.Navigate(url, null, null, "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0");

【讨论】:

  • 你应该把它改成"User-Agent: User agent" 否则整个用户界面都会受到干扰
  • 谢谢大家,您的解决方案适用于 [link](www.google.com),但这只是我无法上班的页面示例,它是公开的。我真正的问题是我的内部页面,它仅由 javascripts 和 vb 脚本制成,称为window.onload=function()。我希望这个问题和谷歌上的一样,但它没有解决这个问题......当我在我的页面上尝试browser.Document.Body.ScrollRectangle.Height 时,它返回 0
猜你喜欢
  • 1970-01-01
  • 2011-02-13
  • 2021-07-23
  • 2014-05-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多