WebBrowser Html Документ в изображение - PullRequest
2 голосов
/ 17 марта 2020

Я пытаюсь сделать изображение веб-страницы, но на некоторых страницах отображается белая страница.

В редакторе реестра найдите \ HKEY_CURRENT_USER \ Software \ Microsoft \ Inte rnet Explorer \ Main \ FeatureControl \ FEATURE_BROWSER_EMULATION \ и добавьте туда следующее:

  • WindowsFormsApp1.exe с десятичное значение 11000

  • WindowsFormsApp1.vshost.exe с десятичным значением 11000

Вот мой код:

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");
    }
}
}

Windows From Designer

Ответы [ 2 ]

2 голосов
/ 18 марта 2020

Чтобы напечатать содержимое Html элемента управления WebBrowser, необходимо учесть несколько моментов:

  1. Нам нужно использовать DocumentCompleted WebBrowser событие для определения того, когда текущий документ загружается и отображается
  2. Один документ может (будет) содержать более одного вложенного документа, обычно содержащегося внутри фреймов / фреймов. Каждый IFrame содержит свой собственный документ: при загрузке документа, содержащегося в IFrame, значение DocumentCompleted возвращается. Это означает, что событие может и будет вызываться несколько раз, когда WebBrowser переходит к URL-адресу.

    Примечания здесь объясняют больше: Как получить значение HtmlElement внутри Frames / IFrames?

  3. Управляемые свойства WebBrowser не всегда отражайте реальные ценности DOM. Например, фактические размеры документа Html, когда рендеринг завершен, нигде не отражаются, поэтому нам нужно получить эти меры из DOM самостоятельно. На текущие размеры, отображаемые в DOM, ссылаются:

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

    См .: Измерение размера и местоположения элемента с помощью CSSOM в Windows Inte rnet Explorer

  4. Элемент управления WebBrowser DrawToBitmap () метод получен из Control, но на самом деле он не реализован, как мы могли ожидать. То же самое относится и к другим элементам управления: известно, что RichTextBox печатает пустой контент при использовании этого метода.

  5. A Html Размер документа может превышать максимальный размер, поддерживаемый растровым изображением. Существует также более тонкий предел памяти: объект растрового изображения должен хранить свое содержимое в непрерывном пространстве памяти, поэтому ограничение размера растрового изображения на самом деле сложно предопределить и может вызвать исключения, когда мы можем этого не ожидать.
  6. Для функции эмуляции элемента управления WebBrowser должно быть установлено значение Inte rnet Explorer 11. См .:
    Как получить элемент управления WebBrowser для отображения современного содержимого?
    Проблема эмуляции элемента управления веб-браузера (FEATURE_BROWSER_EMULATION)

Для продолжения сначала подпишитесь на событие DocumentCompleted элемента управления WebBrowser.

A Dictionary<Uri, Bitmap> используется здесь для хранения растрового изображения, представляющего Html содержимое URL-адресов, посещенных в сеансе.
Когда возникает событие DocumentCompleted, мы добавляем новый элемент словаря, когда текущий URL никогда ранее не посещался.
Если Uri уже сохранен, мы обновили связанный растровый объект, поэтому в нем присутствует только самый последний снимок документа Html. Коллекция.

Я использую класс поддержки для обработки создания растровых изображений и для объявления собственного COM-интерфейса, используемого для создания растрового изображения из текущего ISurfacePresenter .
Поскольку элемент управления WebBrowser заставил использовать VIEW_OBJECT_COMPOSITION_MODE_LEGACY в качестве CompositionMode для всех сайтов, внутренний GetPrintBitmap вызывает метод IViewObject Interface Draw() в этом мы тоже.

Чтобы напечатать содержимое (все содержимое) текущего Html документа, вызовите DrawContent(WebBrowser browser) stati c метод 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;
        }
    }
}

Вот как это работает:

Полный документ захвачен. Конечно, растровое изображение также может быть ограничено указанным максимальным / минимальным размером c, чтобы захватить только часть документа Html.

WebBrowser ScreenShots:

Пример проекта WinForms на Google Диске.

0 голосов
/ 17 марта 2020

попробуйте настроить User Agent следующим образом

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