1

I am using the code below to capture a screenshot of the currently active window. This code comes from Capture screenshot Including Semitransparent windows in .NET, with a few small additions, i.e. it uses GetForegroundWindow and also a timer so that I can select the desired window.

On Windows 10 (x64) this works fine for Firefox browser, but it does not work with Chrome or Edge.

I find it strange that Screenshot captured using BitBlt in C# results a black image on Windows 10 [duplicate] is marked as a duplicate, because the answer from above (first link) does not solve this problem.

Any ideas why it does not work for Chrome or Edge?

Code:

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

namespace App1
{
    /// <summary>
    /// Description of MainForm.
    /// </summary>
    public partial class MainForm : Form
    {
        public MainForm()
        {
            //
            // The InitializeComponent() call is required for Windows Forms designer support.
            //
            InitializeComponent();

            //
            // TODO: Add constructor code after the InitializeComponent() call.
            //
        }

        void DoCapture()
        {
            label1.Text = "Capturing...";

              try
              {
              IntPtr hDesk = GetForegroundWindow();
              //IntPtr hDesk = GetDesktopWindow();

              var windowRect = new RECT();
              GetWindowRect(hDesk, out windowRect);

              int width = (int)(windowRect.Right - windowRect.Left);
              int height = (int)(windowRect.Bottom - windowRect.Top);      

              Size sz = new Size(width, height);
              sz.Width = (int)(sz.Width * 1.25); // this is just an adjustment for the Windows zoom factor of 125%
              sz.Height = (int)(sz.Height * 1.25);

              IntPtr hSrce = GetWindowDC(hDesk);
              IntPtr hDest = CreateCompatibleDC(hSrce);
              IntPtr hBmp = CreateCompatibleBitmap(hSrce, sz.Width, sz.Height);
              IntPtr hOldBmp = SelectObject(hDest, hBmp);
              bool b = BitBlt(hDest, 0,0, sz.Width, sz.Height, hSrce, 
                   0, 0, CopyPixelOperation.SourceCopy | CopyPixelOperation.CaptureBlt);
              Bitmap bmp = Bitmap.FromHbitmap(hBmp);
              SelectObject(hDest, hOldBmp);
              DeleteObject(hBmp);
              DeleteDC(hDest);
              ReleaseDC(hDesk, hSrce);
              bmp.Save(@"c:\temp\test.png");
              bmp.Dispose();
              label1.Text = "Done";
              }
              catch (Exception e){
                label1.Text = "Exception Occurred";
                textBox1.Text = e.ToString();
              }
        }

        void Button1Click(object sender, EventArgs e)
        {
            timer1.Enabled = true;
        }

        // P/Invoke declarations
        [DllImport("gdi32.dll")]
        static extern bool BitBlt(IntPtr hdcDest, int xDest, int yDest, int
        wDest, int hDest, IntPtr hdcSource, int xSrc, int ySrc, CopyPixelOperation rop);
        [DllImport("user32.dll")]
        static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDc);
        [DllImport("gdi32.dll")]
        static extern IntPtr DeleteDC(IntPtr hDc);
        [DllImport("gdi32.dll")]
        static extern IntPtr DeleteObject(IntPtr hDc);
        [DllImport("gdi32.dll")]
        static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, int nHeight);
        [DllImport("gdi32.dll")]
        static extern IntPtr CreateCompatibleDC(IntPtr hdc);
        [DllImport("gdi32.dll")]
        static extern IntPtr SelectObject(IntPtr hdc, IntPtr bmp);
        [DllImport("user32.dll")]
        public static extern IntPtr GetDesktopWindow();
        [DllImport("user32.dll")]
        public static extern IntPtr GetForegroundWindow();      
        [DllImport("user32.dll")]
        public static extern IntPtr GetWindowDC(IntPtr ptr);
        [DllImport("user32.dll")]
        static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);

        [StructLayout(LayoutKind.Sequential)]
        public struct RECT
        {
            public int Left;        // x position of upper-left corner
            public int Top;         // y position of upper-left corner
            public int Right;       // x position of lower-right corner
            public int Bottom;      // y position of lower-right corner
        }

        void Timer1Tick(object sender, EventArgs e)
        {
            timer1.Enabled = false;
            DoCapture();
        }       
    }
}

Running this in SharpDevelop 5.1

Community
  • 1
  • 1
RaelB
  • 3,301
  • 5
  • 35
  • 55

2 Answers2

2

Likely it doesn't work for these 2 browsers because they use a hardware-accelerated graphics context (OpenGL- or DirectX-based) to render their content, and this is incompatible with GDI-based calls.

Stefan Monov
  • 11,332
  • 10
  • 63
  • 120
2

I can suggest a simple workaround:

First use GetForegroundWindow to get the active window rect. Then call GetDesktopWindow and use that handle with the call to BitBlt:

Just add a single line to above code:

          IntPtr hDesk = GetForegroundWindow();
          ...       // get dimensions of active window    
          hDesk = GetDesktopWindow();  // add this line
          IntPtr hSrce = GetWindowDC(hDesk);
          IntPtr hDest = CreateCompatibleDC(hSrce);

Works fine.

RaelB
  • 3,301
  • 5
  • 35
  • 55
  • Worked fine with a Citrix window that rendered black when captured with GDI. The key is capturing desktop, good trick. – oscar Oct 17 '18 at 12:05
  • Unfortunately this won't work if the window is partially or completely overlapped by other windows. – Mercalli May 22 '19 at 10:29
  • @Luaan, `BitBlt` has been able to capture overlapped windows (including the overlapped parts) since DWM composition was introduced in Windows Vista. Unfortunately, for hardware-accelerated windows, `BitBlt` doesn't work at all. – Mercalli Sep 06 '19 at 01:09
  • @Luaan, the question is about capturing a window under Windows 10. And you said **"but the same thing is true for BitBlt"**. No, it isn't. `BitBlt` can capture an overlapped window in Windows 10. – Mercalli Sep 06 '19 at 13:12