bitblt failed on Windows 10 version 1703 (15063.138)
Asked Answered
O

1

3

using Visual Studio 2017, vc141, the following code should got a screenshot from front game window but now it return a black and blank image.

only issue with games(tried OpenGL and Vulkan, ogl return black, vulkan return white)

before upgrade to windows 10 1703, it works on windows 10 1607 and windows 7 sp1

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

code:

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;
    /*...*/
}

and still black even PrintWindow(at least we got a titlebar)

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

I send this program to my friend (without any modify, his OS=win7 x64), but he got the correct result.

so what should I do?

Oosphere answered 13/4, 2017 at 6:49 Comment(6)
Shouldn't the BitBlt call be made before the call to cv::Mat ?Harms
You definitely need to call GetClientRect instead of GetWindowRect. Otherwise your bitmap will be larger than the actual contents of hdcSys and hdcMem.Harms
thanks, but the question is I can't get bitmap now. the code works with no problem before upgrade system. I just install new version of "obs-studio",the window cap module give the same result what I got. I'm going to let my friend test it.Oosphere
Check the return value of BitBlt. If it returns FALSE, then when does a subsequent call to GetLastError() return?Harms
@Harms BitBlt returns 1 (TRUE), GetLastError() returns 0 (no error)Oosphere
Had any lucky with that? I'm having the exactly same problem after upgrade to Windows 10 Pro 10.0.16299.XBernal
T
-2

GDI is a very old technology and is slowly getting deprecated. The more reliable method to capture desktop on Windows 10 would be through Desktop Duplication API.

Tarsia answered 13/4, 2017 at 8:33 Comment(7)
Could you add an example of how this is done to your answer. Posting just a link does not make a good answer.Harold
The page my link points to contains a frame grabbing code snippet and a link to complete sample. There is not much point in copypasting it here.Tarsia
In the future, if the website removes the example, your answer becomes obsolete, which is why it is recommended to include an outline of the solution with your answer.Harold
I search some pages, but I didn't found if it could capture window on background.what I found is it can capture desktop with high performance.Oosphere
Capturing windows in background seems like a completely different matter. Event if window continues rendering while it is occluded (which it shouldn't do) capturing it's content can be quite tricky. It least I don't think of any public API for this purpose, people on the internet (and even Microsoft with their recent game recording panel) seem to install hooks into target application to achieve this.Tarsia
I hate hooks, the only way seems only 'bitblt'. but after this update, I can't use bitblt anymore, maybe it's a bug. Microsoft provides "uiAccess" for this problem, but it require a code sign, certificate. And I saw it's deprecated on MSDN(i don't sure). I think maybe I need uiAccess not only "run as Administrator". but ... it seems like a system bug.Oosphere
Actually I suspect that source DC that you obtain by calling GetDC(hWnd) may be empty and there is actually nothing to BitBlt. I suggest to add some error handling code, check that hdc is not NULL, retrieve DC properties using GetDeviceCaps, maybe inspect it's content directly with GetPixel.Tarsia

© 2022 - 2024 — McMap. All rights reserved.