BitBlt 屏幕截图在 Windows 10 上不起作用

Posted

技术标签:

【中文标题】BitBlt 屏幕截图在 Windows 10 上不起作用【英文标题】:BitBlt screen capture not working on Windows 10 【发布时间】:2018-05-27 20:56:33 【问题描述】:

我正在使用此代码在后台捕获进程窗口:

IntPtr = Process.GetProcessByName("memu")[0].MainWindowHandle;
RECT rc;
GetClientRect(hwnd, out rc);

IntPtr hdcFrom = GetDC(hwnd);
IntPtr hdcTo = CreateCompatibleDC(hdcFrom);

int Width = rc.right;
int Height = rc.bottom;

Bitmap bmp = null;

IntPtr hBitmap = CreateCompatibleBitmap(hdcFrom, Width, Height);
if (hBitmap != IntPtr.Zero) 
   IntPtr hLocalBitmap = SelectObject(hdcTo, hBitmap);

   BitBlt(hdcTo, 0, 0, Width, Height, hdcFrom, 0, 0, CopyPixelOperation.SourceCopy);
   SelectObject(hdcTo, hLocalBitmap);

   DeleteDC(hdcTo);
   ReleaseDC(hwnd, hdcFrom);

   bmp = Image.FromHbitmap(hBitmap);
   DeleteObject(hBitmap);
   return bmp;

此代码是捕获一个名为 MEmu 的 android 模拟器,它使用 DirectX 来呈现内容。但是这段代码在 Windows 10 更新到 16299 版本后停止工作(之前可以正常工作),它仍然可以在启用 Aero 模式的 Windows 7 上工作。

当我在 Windows 10 Pro v16299.X 中使用此方法时,它只会返回白色图像,或者返回模拟器“加载屏幕”,而不是运行内容。在 Windows 7 上,如果我删除 Aero 模式,它的行为也会相同,捕获“加载屏幕”,因此看起来新 Windows 10 专业版更新中透明度的工作方式发生了某种变化。

我已经尝试了一切,尝试安装一些模块以强制 Aero 模式在 Windows 10 上运行,尝试 PrintWindow 在后台捕获屏幕,但仍然相同。

任何想法可能会发生什么?还是可能的解决方案?或者在最新的 Windows 10 Pro 版本中发生了哪些可能破坏该代码的变化?

谢谢!

【问题讨论】:

16299.64 更改日志显示“Microsoft 图形组件的安全更新”,可能这些更新破坏了 DWM 中的某些内容作为副作用。没有其他 API 可以捕获隐藏的窗口,因此您可以希望 MS 修复它,或者使用 DirectX 挂钩等一些技巧。 仅供参考,它在我们的 Windows 10 Pro 64 位版本 1607 中工作:获取全黑位图。 在调用BitBlt 之后,立即执行以下操作:var error = Marshal.GetLastWin32Error();,如果您得到不同于 0(零)的任何值,请验证错误代码 HERE。为了使其正常工作,您需要在 DllImport 语句中添加 SetLastError = true。这将只给出最后一次 Win32 API 调用的错误,因此您可能需要对每个调用都执行此操作。 @IgorM - 至于报告错误的位置 - Windows 10 附带名为“Centrum Feedback”的应用程序,您可以使用它来报告建议/错误。 几个月后问题依然存在,经过一些研究和测试,我和一些朋友找到了一些可能的“解决方案”解决问题HERE,但它仍然不是100%有效,也有API 调用中没有错误,有人在这个问题上运气好吗? 【参考方案1】:

希望这能解决问题。 .net 框架中内置了一种捕获屏幕的方法,该方法可能有效。不确定它是否会捕获 DirectX 内容,但可能值得一试。

请注意,此解决方案捕获当前屏幕,但您可能可以对其进行修改以仅捕获您感兴趣的区域。

我在这里找到了这个解决方案:https://www.c-sharpcorner.com/UploadFile/2d2d83/how-to-capture-a-screen-using-C-Sharp/

private void CaptureMyScreen()

      try
      
           //Creating a new Bitmap object
          Bitmap captureBitmap = new Bitmap(1024, 768, PixelFormat.Format32bppArgb);

         //Bitmap captureBitmap = new Bitmap(int width, int height, PixelFormat);
         //Creating a Rectangle object which will  
         //capture our Current Screen
         Rectangle captureRectangle = Screen.AllScreens[0].Bounds;

         //Creating a New Graphics Object
         Graphics captureGraphics = Graphics.FromImage(captureBitmap);

        //Copying Image from The Screen
        captureGraphics.CopyFromScreen(captureRectangle.Left,captureRectangle.Top,0,0,captureRectangle.Size);

        //Saving the Image File (I am here Saving it in My E drive).
        captureBitmap.Save(@"E:\Capture.jpg",ImageFormat.Jpeg);

        //Displaying the Successfull Result

        MessageBox.Show("Screen Captured");
    
    catch (Exception ex)
    
        MessageBox.Show(ex.Message);
    

【讨论】:

【参考方案2】:

对我来说,这适用于 Windows 10 11.02.2021:

static void hflipAndSwapRandB(unsigned char* data, int w, int h, int dim)
    
        int y = 0;
        int x = 0;
        int d;
        unsigned char swap = 0;
        int hh = h / 2;
        for (y = 0; y < hh; y++)
        
            for (x = 0; x < w; x++)
            
                for (d = 0; d < dim; d++)
                
                    swap = data[y * dim * w + dim * x + d];
                    data[y * dim * w + dim * x + d] = data[(h - 1 - y) * dim * w + dim * x + dim - 1 - d];
                    data[(h - 1 - y) * dim * w + dim * x + dim - 1 - d] = swap;
                
            
        
    

static void copyScreen(unsigned char* pixels_out, int x, int y, int width, int height)

    HDC hScreenDC = GetDC(GetDesktopWindow());
    HDC hMemoryDC = CreateCompatibleDC(hScreenDC);

    if (width == -1 || height == -1)
    
        width = GetDeviceCaps(hScreenDC, HORZRES);
        height = GetDeviceCaps(hScreenDC, VERTRES);
    

    HBITMAP hBitmap = CreateCompatibleBitmap(hScreenDC, width, height);
    HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemoryDC, hBitmap);

    BitBlt(hMemoryDC, x, y, width, height, hScreenDC, x, y, SRCCOPY);
    hBitmap = (HBITMAP)SelectObject(hMemoryDC, hOldBitmap);
    BITMAPINFOHEADER   bi;

    bi.biSize = sizeof(BITMAPINFOHEADER);
    bi.biWidth = width-x;
    bi.biHeight = height-y;
    bi.biPlanes = 1;
    bi.biBitCount = 24;
    bi.biCompression = BI_RGB;
    bi.biSizeImage = 0;
    bi.biXPelsPerMeter = 0;
    bi.biYPelsPerMeter = 0;
    bi.biClrUsed = 0;
    bi.biClrImportant = 0;
    GetDIBits(hMemoryDC, hBitmap, 0, height-y, pixels_out, (BITMAPINFO*)&bi, DIB_RGB_COLORS);
    hflipAndSwapRandB(pixels_out, width, height, 3);

    DeleteDC(hMemoryDC);
    DeleteDC(hScreenDC);
    DeleteObject(hBitmap);

【讨论】:

以上是关于BitBlt 屏幕截图在 Windows 10 上不起作用的主要内容,如果未能解决你的问题,请参考以下文章

BitBlt 无法正确捕获标题栏

BitBlt 仅捕获部分屏幕

c#利用bitblt如何获取最小化后的窗口截图

隐藏窗口的 PrintWindow 和 BitBlt

C#搞跨平台桌面UI,分别实现Windows,Mac,Linux屏幕截图

C语言 服务项进行全屏幕截图 但是截图后是黑屏怎么办?