WebBrowser.DrawToBitmap () или другие методы? - PullRequest
22 голосов
/ 12 марта 2010

Я пытаюсь перехватить содержимое элемента управления WebBrowser. DrawToBitmap() будет работать идеально, но это не поддерживается в документации для элемента управления WebBrowser. Я пытался найти другой способ перехватить содержимое элемента управления WebBrowser и сохранить его в локальном файле изображения.

Есть ли у кого-нибудь обходные пути или другие методы для сохранения содержимого элемента управления WebBrowser в локальный файл изображения?

Ответы [ 4 ]

37 голосов
/ 12 марта 2010

Control.DrawToBitmap не всегда работает, поэтому я прибег к следующим родным вызовам API, которые обеспечивают более согласованные результаты:

Класс Утилиты. Вызовите Utilities.CaptureWindow (Control.Handle) для захвата определенного элемента управления:

public static class Utilities
{
    public static Image CaptureScreen()
    {
        return CaptureWindow(User32.GetDesktopWindow());
    }

    public static Image CaptureWindow(IntPtr handle)
    {

        IntPtr hdcSrc = User32.GetWindowDC(handle);

        RECT windowRect = new RECT();
        User32.GetWindowRect(handle, ref windowRect);

        int width = windowRect.right - windowRect.left;
        int height = windowRect.bottom - windowRect.top;

        IntPtr hdcDest = Gdi32.CreateCompatibleDC(hdcSrc);
        IntPtr hBitmap = Gdi32.CreateCompatibleBitmap(hdcSrc, width, height);

        IntPtr hOld = Gdi32.SelectObject(hdcDest, hBitmap);
        Gdi32.BitBlt(hdcDest, 0, 0, width, height, hdcSrc, 0, 0, ApiConstants.SRCCOPY);
        Gdi32.SelectObject(hdcDest, hOld);
        Gdi32.DeleteDC(hdcDest);
        User32.ReleaseDC(handle, hdcSrc);

        Image image = Image.FromHbitmap(hBitmap);
        Gdi32.DeleteObject(hBitmap);

        return image;
    }
}

Класс Gdi32:

public class Gdi32
{
    [DllImport("gdi32.dll")]
    public static extern bool BitBlt(IntPtr hObject, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hObjectSource, int nXSrc, int nYSrc, int dwRop);
    [DllImport("gdi32.dll")]
    public static extern IntPtr CreateCompatibleBitmap(IntPtr hDC, int nWidth, int nHeight);
    [DllImport("gdi32.dll")]
    public static extern IntPtr CreateCompatibleDC(IntPtr hDC);
    [DllImport("gdi32.dll")]
    public static extern bool DeleteDC(IntPtr hDC);
    [DllImport("gdi32.dll")]
    public static extern bool DeleteObject(IntPtr hObject);
    [DllImport("gdi32.dll")]
    public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);
}

Класс User32:

public static class User32
{
    [DllImport("user32.dll")]
    public static extern IntPtr GetDesktopWindow();
    [DllImport("user32.dll")]
    public static extern IntPtr GetWindowDC(IntPtr hWnd);
    [DllImport("user32.dll")]
    public static extern IntPtr GetWindowRect(IntPtr hWnd, ref RECT rect);
    [DllImport("user32.dll")]
    public static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDC);
}

Используемые константы:

    public const int SRCCOPY = 13369376;

Используемые структуры:

[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
    public int left;
    public int top;
    public int right;
    public int bottom;
}

Дружественный метод расширения Control:

public static class ControlExtensions
{
    public static Image DrawToImage(this Control control)
    {
        return Utilities.CaptureWindow(control.Handle);
    }
}

Это фрагмент кода из моего проекта CC.Utilities , и я специально написал его для создания снимков экрана из элемента управления WebBrowser.

3 голосов
/ 21 декабря 2014

Следующий метод может захватить все изображение окна, даже если окно больше, чем размер экрана. Затем он может захватить изображение содержимого страницы, если размер окна будет изменен до webBrowser.Document.OffsetRectangle.Size

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

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

    public static void GetImage(object obj, Image destination, Color backgroundColor)
    {
        using(Graphics graphics = Graphics.FromImage(destination))
        {
            IntPtr deviceContextHandle = IntPtr.Zero;
            RECT rectangle = new RECT();

            rectangle.Right = destination.Width;
            rectangle.Bottom = destination.Height;

            graphics.Clear(backgroundColor);

            try
            {
                deviceContextHandle = graphics.GetHdc();

                IViewObject viewObject = obj as IViewObject;
                viewObject.Draw(1, -1, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, deviceContextHandle, ref rectangle, IntPtr.Zero, IntPtr.Zero, 0);
            }
            finally
            {
                if(deviceContextHandle != IntPtr.Zero)
                {
                    graphics.ReleaseHdc(deviceContextHandle);
                }
            }
        }
    }
}

Использование:

Bitmap screenshot = new Bitmap(1024, 768);
NativeMethods.GetImage(webBrowser.ActiveXInstance, screenshot, Color.White);
0 голосов
/ 09 апреля 2019

Я использовал метод OleDraw, как в этой теме в SO, но интегрировал его в класс, производный от WebBrowser. Это позволяет создать обычный Control.DrawToBitmap не только для WebBrowser, но и для формы с WebBrowser. Это также работает, если форма скрыта (покрыта другой формой, включая родительскую форму MDI) и должна работать, когда пользователь заблокировал сеанс с Win + L (я не проверял его).

public class WebBrowserEx : WebBrowser
{
    private const uint DVASPECT_CONTENT = 1;

    [DllImport("ole32.dll", PreserveSig = false)]
    private static extern void OleDraw([MarshalAs(UnmanagedType.IUnknown)] object pUnk,
        uint dwAspect,
        IntPtr hdcDraw,
        [In] ref System.Drawing.Rectangle lprcBounds
    );

    protected override void WndProc(ref Message m)
    {
        const int WM_PRINT = 0x0317;

        switch (m.Msg)
        {
            case WM_PRINT:
                Rectangle browserRect = new Rectangle(0, 0, this.Width, this.Height);

                // Don't know why, but drawing with OleDraw directly on HDC from m.WParam.
                // results in badly scaled (stretched) image of the browser.
                // So, drawing to an intermediate bitmap first.
                using (Bitmap browserBitmap = new Bitmap(browserRect.Width, browserRect.Height))
                {
                    using (var graphics = Graphics.FromImage(browserBitmap))
                    {
                        var hdc = graphics.GetHdc();
                        OleDraw(this.ActiveXInstance, DVASPECT_CONTENT, hdc, ref browserRect);
                        graphics.ReleaseHdc(hdc);
                    }

                    using (var graphics = Graphics.FromHdc(m.WParam))
                    {
                        graphics.DrawImage(browserBitmap, Point.Empty);
                    }
                }
                // ignore default WndProc
                return;

        }

        base.WndProc(ref m);
    }
}
0 голосов
/ 17 мая 2010

Я использую DrawToBitmap (Visual Studio 2008 C #) для захвата больших изображений (подписанные пользователем счета, содержимое за пределами экрана). В основном это работает хорошо, но я получаю пустые изображения. Около 100 сотрудников используют это программное обеспечение, примерно каждые 2 недели я вижу одно пустое изображение.

Я провел много испытаний и нашел одну забавную вещь: Я создал кнопку для генерации изображения из веб-браузера. Обычно все в порядке, но если я сначала нажму на веб-браузер, а затем на кнопку «Создать», появится пустое изображение.

...