Screenshot with BitBlt results in black image on Windows 10

放肆的年华 提交于 2019-12-08 05:47:32

问题


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


回答1:


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.




回答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.



来源:https://stackoverflow.com/questions/43595289/screenshot-with-bitblt-results-in-black-image-on-windows-10

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!