bitblt 在 Windows 10 版本 1703 (15063.138) 上失败

Posted

技术标签:

【中文标题】bitblt 在 Windows 10 版本 1703 (15063.138) 上失败【英文标题】:bitblt failed on Windows 10 version 1703 (15063.138) 【发布时间】:2017-09-09 04:03:45 【问题描述】:

使用 Visual Studio 2017,vc141,以下代码应该从游戏前窗口获取屏幕截图,但现在它返回一个黑色和空白的图像。

只有游戏有问题(试过 OpenGL 和 Vulkan,ogl 返回黑色,vulkan 返回白色)

在升级到 windows 10 1703 之前,它适用于 windows 10 1607 和 windows 7 sp1

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>

代码:

BOOL ScreenShot(cv::Mat *img, HWND hWnd = NULL) 
    HBITMAP hBitmap;
    HDC hdcSys = GetDC(hWnd);
    HDC hdcMem = CreateCompatibleDC(hdcSys);

    void *ptrBitmapPixels;
    BITMAPINFO bi;
    HDC hdc;

    RECT rect;

    if (!GetWindowRect(hWnd, &rect) || (hWnd == NULL)) 
        return FALSE;
    

    ZeroMemory(&bi, sizeof(BITMAPINFO));

    LONG lWidth = rect.right - rect.left;
    LONG lHeight = rect.bottom - rect.top;

    bi.bmiHeader.biSize = sizeof(BITMAPINFO);
    bi.bmiHeader.biWidth = lWidth;
    bi.bmiHeader.biHeight = -lHeight;
    bi.bmiHeader.biPlanes = 1;
    bi.bmiHeader.biBitCount = 32;

    hdc = GetDC(hWnd);
    hBitmap = CreateDIBSection(hdc, &bi, DIB_RGB_COLORS, &ptrBitmapPixels, NULL, 0);

    SelectObject(hdcMem, hBitmap);

    *img = cv::Mat(lHeight, lWidth, CV_8UC4, ptrBitmapPixels, 0);

    BitBlt(hdcMem, 0, 0, lWidth, lHeight, hdcSys, 0, 0, SRCCOPY);

    //DeleteObject(hBitmap);
    DeleteDC(hdcMem);
    ReleaseDC(hWnd, hdcSys);
    ReleaseDC(hWnd, hdc);

    return TRUE;


BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)

    /*...*/
    HotKeyId = GlobalAddAtom(L"DBKGNDSCREENSHOT");
    RegisterHotKey(hWnd, HotKeyId, NULL, VK_F10);
    /*...*/

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 
    /*...*/
    case WM_HOTKEY:
        if (wParam == HotKeyId) 
            cv::Mat t;
            HWND MainHWND;
            MainHWND = GetForegroundWindow();

            ScreenShot(&t, MainHWND);
            cv::imshow("1", t);
        
        break;
    /*...*/

甚至 PrintWindow 仍然是黑色的(至少我们有一个标题栏)

PrintWindow(hWnd, hdcMem, 0);
//BitBlt(hdcMem, 0, 0, lWidth, lHeight, hdcSys, 0, 0, SRCCOPY);

我将此程序发送给我的朋友(没有任何修改,他的 OS=win7 x64),但他得到了正确的结果。

那我该怎么办?

【问题讨论】:

不应该在之前调用BitBlt 调用cv::Mat 吗? 你肯定需要打电话给GetClientRect而不是GetWindowRect。否则你的位图会比 hdcSys 和 hdcMem 的实际内容大。 谢谢,但问题是我现在无法获取位图。该代码在升级系统之前没有问题。我只是安装了新版本的“obs-studio”,窗口盖模块给出了与我相同的结果。我打算让我朋友测试一下。 查看BitBlt的返回值。如果它返回FALSE,那么随后对GetLastError() 的调用什么时候返回? @selbie BitBlt 返回 1 (TRUE),GetLastError() 返回 0(无错误) 【参考方案1】:

GDI 是一项非常古老的技术,正在慢慢被弃用。在 Windows 10 上捕获桌面的更可靠方法是通过 Desktop Duplication API。

【讨论】:

您能否在您的答案中添加一个如何完成此操作的示例。仅发布一个链接并不能作为一个好的答案。 我的链接指向的页面包含一个帧抓取代码sn-p和一个完整示例的链接。在这里复制粘贴没什么意义。 将来,如果网站删除了示例,您的答案就会过时,这就是为什么建议在您的答案中包含解决方案的大纲。 我搜索了一些页面,但是我没有发现它是否可以捕获背景窗口。我发现它可以捕获高性能的桌面。 在后台捕获窗口似乎是完全不同的事情。如果窗口在被遮挡时继续渲染(它不应该这样做),则捕获其内容的事件可能非常棘手。至少我没有想到任何用于此目的的公共 API,互联网上的人们(甚至是微软最近的游戏录制面板)似乎在目标应用程序中安装了挂钩来实现这一点。

以上是关于bitblt 在 Windows 10 版本 1703 (15063.138) 上失败的主要内容,如果未能解决你的问题,请参考以下文章

BitBlt 仅捕获部分屏幕

BitBlt 无法正确捕获标题栏

如何在 C# 中捕获 Windows 应用商店应用程序的窗口内容

未定义对 CreateCompatibleDC、BitBlt 等的引用?

如何加快 BitBlt 以使用 aero 捕获屏幕?

在 Windows 版本 10.0.18363 中无法切换到 Docker 中的 Windows 容器