Skip to content

Instantly share code, notes, and snippets.

@jankurianski
Created December 2, 2014 07:18
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save jankurianski/f3419d4580517516c24b to your computer and use it in GitHub Desktop.
Save jankurianski/f3419d4580517516c24b to your computer and use it in GitHub Desktop.
Takes a screenshot of a Windows Forms control using BitBlt, making it work with CefSharp's WinForms.ChromiumWebBrowser
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Windows.Forms;
public class ControlSnapshot
{
public static Bitmap Snapshot(Control c)
{
int width = 0, height = 0;
IntPtr hwnd = IntPtr.Zero;
IntPtr dc = IntPtr.Zero;
c.Invoke(new MethodInvoker(() => {
width = c.ClientSize.Width;
height = c.ClientSize.Height;
hwnd = c.Handle;
dc = GetDC(hwnd);
}));
Bitmap bmp = new Bitmap(width, height, PixelFormat.Format32bppRgb);
if (dc != IntPtr.Zero) {
try {
using (Graphics g = Graphics.FromImage(bmp)) {
IntPtr bdc = g.GetHdc();
try {
BitBlt(bdc, 0, 0, width, height, dc, 0, 0, TernaryRasterOperations.SRCCOPY);
} finally {
g.ReleaseHdc(bdc);
}
}
} finally {
ReleaseDC(hwnd, dc);
}
}
return bmp;
}
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr GetDC(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
[DllImport("gdi32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, TernaryRasterOperations dwRop);
public enum TernaryRasterOperations : uint
{
/// <summary>dest = source</summary>
SRCCOPY = 0x00CC0020,
/// <summary>dest = source OR dest</summary>
SRCPAINT = 0x00EE0086,
/// <summary>dest = source AND dest</summary>
SRCAND = 0x008800C6,
/// <summary>dest = source XOR dest</summary>
SRCINVERT = 0x00660046,
/// <summary>dest = source AND (NOT dest)</summary>
SRCERASE = 0x00440328,
/// <summary>dest = (NOT source)</summary>
NOTSRCCOPY = 0x00330008,
/// <summary>dest = (NOT src) AND (NOT dest)</summary>
NOTSRCERASE = 0x001100A6,
/// <summary>dest = (source AND pattern)</summary>
MERGECOPY = 0x00C000CA,
/// <summary>dest = (NOT source) OR dest</summary>
MERGEPAINT = 0x00BB0226,
/// <summary>dest = pattern</summary>
PATCOPY = 0x00F00021,
/// <summary>dest = DPSnoo</summary>
PATPAINT = 0x00FB0A09,
/// <summary>dest = pattern XOR dest</summary>
PATINVERT = 0x005A0049,
/// <summary>dest = (NOT dest)</summary>
DSTINVERT = 0x00550009,
/// <summary>dest = BLACK</summary>
BLACKNESS = 0x00000042,
/// <summary>dest = WHITE</summary>
WHITENESS = 0x00FF0062,
/// <summary>
/// Capture window as seen on screen. This includes layered windows
/// such as WPF windows with AllowsTransparency="true"
/// </summary>
CAPTUREBLT = 0x40000000
}
}
@HaHaItsJake
Copy link

Thanks for this! I have one problem that puts a dipper in my current project. I'm using MetroFramework.Forms and it's only returning a white image. Any advice would be appreciated.

@franzalex
Copy link

I suggest you make it an extension method by making the class static and changing the main method's argument as follows:

public static class ControlSnapshot                 // <-- make the class static
{
    public static Bitmap Snapshot(this Control c)   // <-- add the `this` keyword to make an extension method
    {
        // code goes here
    }

    // other methods and enums
}

You may additionally want to change the helper [P/Invoke] methods to private to keep them in the scope of this class only.

@turgaygorgel
Copy link

I run at my windows 10 pc well. But in windows server r2 2008 standart server. It worked. But It gets screen image when the app minimized.

@qjimbo
Copy link

qjimbo commented Nov 29, 2018

Just a quick note that this may return a white or black screen (as it did for me), the solution was to disable GPU acceleration in CefSharp, then it worked fine.
settings.CefCommandLineArgs.Add("disable-gpu", "");

@shahulshas94
Copy link

If you are calling a method like below...

Bitmap img = ControlSnapshot.Snapshot(SessionKeywords.driver);
img.Save(@"E\shahul.png", ImageFormat.Png);

Got Exception :
Exception thrown: 'System.Runtime.InteropServices.ExternalException' in System.Drawing.dll

Any update ? on this?

@jankurianski
Copy link
Author

If you are calling a method like below...

Bitmap img = ControlSnapshot.Snapshot(SessionKeywords.driver);
img.Save(@"E\shahul.png", ImageFormat.Png);

Got Exception :
Exception thrown: 'System.Runtime.InteropServices.ExternalException' in System.Drawing.dll

Any update ? on this?

@shahulshas94 the problem is most likely related to the SessionKeywords.driver control not being valid. This type of question is better suited to stackoverflow.com - but before posting the question, ensure you can create a minimal reproducible example: https://stackoverflow.com/help/minimal-reproducible-example

@kevalkothari
Copy link

Hi All,
I am getting empty screenshot even browser have page open. Any clue what should I look in code for this issue?
Screenshot getting:
demo

Actual Form:
Actual Image

Thanks in advance.

@jankurianski
Copy link
Author

@kevalkothari maybe you are passing in the wrong control. Are you passing in an instance of WinForms.ChromiumWebBrowser?

@kevalkothari
Copy link

kevalkothari commented Nov 19, 2019

Hi @jankurianski

I think I am passing correct reference, See details below.

Defining ChromiumWebBrowser below:

using CefSharp.MinimalExample.WinForms.Controls;
using CefSharp.WinForms;
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace CefSharp.MinimalExample.WinForms
{

   
    public partial class BrowserForm : Form
    {
        private readonly ChromiumWebBrowser browser;
        
...

Passing reference of that browser as below:


   private void button1_Click(object sender, EventArgs e)
        {
          Bitmap img = ControlSnapshot.Snapshot(browser);
          img.Save(@"C:\test\demo_new.bmp", ImageFormat.Png);
          
        }

does it correct reference?

@rajesh-smartwebtech
Copy link

Hi @jankurianski ,
is it possible to hole page screenshot in cefsharp browser ?

@jankurianski
Copy link
Author

@kevalkothari your saved file name extension should be .png, not .bmp.

@jankurianski
Copy link
Author

@rajesh-smartwebtech you have to implement whole page screenshot yourself, by scrolling the page and taking multiple screenshots. I do not have example code for this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment