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?

Zazck
  • 81
  • 2
  • 6
  • Shouldn't the `BitBlt` call be made *before* the call to `cv::Mat` ? – selbie Apr 13 '17 at 08:01
  • You definitely need to call `GetClientRect` instead of `GetWindowRect`. Otherwise your bitmap will be larger than the actual contents of hdcSys and hdcMem. – selbie Apr 13 '17 at 08:22
  • 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. – Zazck Apr 13 '17 at 08:32
  • Check the return value of `BitBlt`. If it returns `FALSE`, then when does a subsequent call to `GetLastError()` return? – selbie Apr 13 '17 at 18:01
  • @selbie BitBlt returns 1 (TRUE), GetLastError() returns 0 (no error) – Zazck Apr 14 '17 at 04:45
  • Had any lucky with that? I'm having the exactly same problem after upgrade to Windows 10 Pro 10.0.16299.X – Kyore Dec 17 '17 at 23:29

1 Answers1

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

user7860670
  • 35,849
  • 4
  • 58
  • 84
  • Could you add an example of how this is done to your answer. Posting just a link does not make a good answer. – Martin Evans Apr 13 '17 at 08:44
  • 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. – user7860670 Apr 13 '17 at 08:50
  • 1
    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. – Martin Evans Apr 13 '17 at 08:51
  • 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. – Zazck Apr 13 '17 at 09:39
  • 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. – user7860670 Apr 13 '17 at 10:27
  • 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. – Zazck Apr 13 '17 at 14:30
  • 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`. – user7860670 Apr 13 '17 at 15:07