Webbrowser.Drawtobitmap() or Other Methods

WebBrowser.DrawToBitmap() or other methods?

The Control.DrawToBitmap doesn't always work so I resorted to the following native API calls that provide more consistent results:

The Utilities class. Call Utilities.CaptureWindow(Control.Handle) to capture a specific control:

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

The Gdi32 class:

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

The User32 class:

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

The constants used:

    public const int SRCCOPY = 13369376;

The structs used:

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

A friendly Control extension method:

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

This is a code snippet from my CC.Utilities project and I specifically wrote it to take screenshots from the WebBrowser control.

WebBrowser.DrawtoBitmap() generating blank images for few sites consistently

DrawToBitmap has limitations and dont always work as expected. Try instead work with native GDI+

Here is example

Converting WebBrowser.Document To A Bitmap?

http://www.bryancook.net/2006/03/screen-capture-for-invisible-windows.html

and here:

http://www.codeproject.com/KB/graphics/screen_capturing.aspx

I believe you should get the handle of your WebBrowser control and save it's content as image like suggested in those links.

How to fix a opacity bug with DrawToBitmap on WebBrowser Control?

I'm looking for a built-in solution to take a screenshot from posted
URL in the question without HIDDEN TEXT. In other words a solution to
respect opacity.

The following code does just that: respects CSS opacity. Amongst other things, it uses a Metafile object and OleDraw API to render the web page's image.

The test HTML:

<!DOCTYPE html>
<body style='background-color: grey'>
<div style='background-color: blue; opacity: 0.2; color: yellow'>This is a text</div>
</body>

The output:

opacity

The code (console app):

using Microsoft.Win32;
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Console_21697048
{
// http://stackoverflow.com/q/21697048/1768303

class Program
{
const string HTML = "<!DOCTYPE html><body style='background-color: grey'><div style='background-color: blue; opacity: 0.2; color: yellow'>This is a text</div></body>";
const string FILE_NAME = "webpage.png";
readonly static Size IMAGE_SIZE = new Size(320, 200);

// Main
static void Main(string[] args)
{
try
{
// enable HTML5 etc (assuming we're running IE9+)
SetFeatureBrowserFeature("FEATURE_BROWSER_EMULATION", 9000);
// force software rendering
SetFeatureBrowserFeature("FEATURE_IVIEWOBJECTDRAW_DMLT9_WITH_GDI", 1);
SetFeatureBrowserFeature("FEATURE_GPU_RENDERING", 0);

using (var apartment = new MessageLoopApartment())
{
// create WebBrowser on a seprate thread with its own message loop
var webBrowser = apartment.Invoke(() => new WebBrowser());

// navigate and wait for the result
apartment.Invoke(() =>
{
var pageLoadedTcs = new TaskCompletionSource<bool>();
webBrowser.DocumentCompleted += (s, e) =>
pageLoadedTcs.TrySetResult(true);

webBrowser.DocumentText = HTML;
return pageLoadedTcs.Task;
}).Wait();

// save the picture
apartment.Invoke(() =>
{
webBrowser.Size = IMAGE_SIZE;
var rectangle = new Rectangle(0, 0, webBrowser.Width, webBrowser.Height);

// get reference DC
using (var screenGraphics = webBrowser.CreateGraphics())
{
var screenHdc = screenGraphics.GetHdc();
// create a metafile
using (var metafile = new Metafile(screenHdc, rectangle, MetafileFrameUnit.Pixel))
{
using (var graphics = Graphics.FromImage(metafile))
{
var hdc = graphics.GetHdc();
var rect = new Rectangle(0, 0, 320, 50);
OleDraw(webBrowser.ActiveXInstance, DVASPECT_CONTENT, hdc, ref rectangle);
graphics.ReleaseHdc(hdc);
}
// save the metafile as bitmap
metafile.Save(FILE_NAME, ImageFormat.Png);
}
screenGraphics.ReleaseHdc(screenHdc);
}
});

// dispose of webBrowser
apartment.Invoke(() => webBrowser.Dispose());
webBrowser = null;
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}

// interop
const uint DVASPECT_CONTENT = 1;

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

// WebBrowser Feature Control
// http://msdn.microsoft.com/en-us/library/ie/ee330733(v=vs.85).aspx
static void SetFeatureBrowserFeature(string feature, uint value)
{
if (LicenseManager.UsageMode != LicenseUsageMode.Runtime)
return;
var appName = System.IO.Path.GetFileName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName);
Registry.SetValue(@"HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Main\FeatureControl\" + feature,
appName, value, RegistryValueKind.DWord);
}
}

// MessageLoopApartment
// more info: http://stackoverflow.com/a/21808747/1768303

public class MessageLoopApartment : IDisposable
{
Thread _thread; // the STA thread

TaskScheduler _taskScheduler; // the STA thread's task scheduler

public TaskScheduler TaskScheduler { get { return _taskScheduler; } }

/// <summary>MessageLoopApartment constructor</summary>
public MessageLoopApartment()
{
var tcs = new TaskCompletionSource<TaskScheduler>();

// start an STA thread and gets a task scheduler
_thread = new Thread(startArg =>
{
EventHandler idleHandler = null;

idleHandler = (s, e) =>
{
// handle Application.Idle just once
Application.Idle -= idleHandler;
// return the task scheduler
tcs.SetResult(TaskScheduler.FromCurrentSynchronizationContext());
};

// handle Application.Idle just once
// to make sure we're inside the message loop
// and SynchronizationContext has been correctly installed
Application.Idle += idleHandler;
Application.Run();
});

_thread.SetApartmentState(ApartmentState.STA);
_thread.IsBackground = true;
_thread.Start();
_taskScheduler = tcs.Task.Result;
}

/// <summary>shutdown the STA thread</summary>
public void Dispose()
{
if (_taskScheduler != null)
{
var taskScheduler = _taskScheduler;
_taskScheduler = null;

// execute Application.ExitThread() on the STA thread
Task.Factory.StartNew(
() => Application.ExitThread(),
CancellationToken.None,
TaskCreationOptions.None,
taskScheduler).Wait();

_thread.Join();
_thread = null;
}
}

/// <summary>Task.Factory.StartNew wrappers</summary>
public void Invoke(Action action)
{
Task.Factory.StartNew(action,
CancellationToken.None, TaskCreationOptions.None, _taskScheduler).Wait();
}

public TResult Invoke<TResult>(Func<TResult> action)
{
return Task.Factory.StartNew(action,
CancellationToken.None, TaskCreationOptions.None, _taskScheduler).Result;
}
}
}


Related Topics



Leave a reply



Submit