首页 > 解决方案 > C ++,Windows(有时)在获取应用程序屏幕截图时出现白屏

问题描述

我有一个应用程序功能,可以触发所述应用程序窗口的屏幕截图。

它是这样的:

    void PlatformWindow::captureScreenshot()
{
    WIN32Window *window = (WIN32Window*)&g_window;
    if (window) {
        HWND handle = window->getWindow();
        if (handle){
            RECT client_rect = { 0 };
            GetClientRect(handle, &client_rect);
            int width = client_rect.right - client_rect.left;
            int height = client_rect.bottom - client_rect.top;


            HDC hdcScreen = GetDC(handle);
            HDC hdc = CreateCompatibleDC(hdcScreen);
            HBITMAP hbmp = CreateCompatibleBitmap(hdcScreen, width, height);
            SelectObject(hdc, hbmp);

            BitBlt(hdc, 0, 0, width, height, hdcScreen, 0, 0, SRCCOPY);

            BITMAPINFO bmp_info = { 0 };
            bmp_info.bmiHeader.biSize = sizeof(bmp_info.bmiHeader);
            bmp_info.bmiHeader.biWidth = width;
            bmp_info.bmiHeader.biHeight = height;
            bmp_info.bmiHeader.biPlanes = 1;
            bmp_info.bmiHeader.biBitCount = 24;
            bmp_info.bmiHeader.biCompression = BI_RGB;

            int bmp_padding = (width * 3) % 4;
            if (bmp_padding != 0) bmp_padding = 4 - bmp_padding;

            BYTE *bmp_pixels = new BYTE[(width * 3 + bmp_padding) * height];;
            GetDIBits(hdc, hbmp, 0, height, bmp_pixels, &bmp_info, DIB_RGB_COLORS);

            BITMAPFILEHEADER bmfHeader;
            //Make screenshot name as a time
            time_t currentTime = std::time(NULL);
            std::ostringstream oss;
            auto tm = *std::localtime(&currentTime);
            oss << std::put_time(&tm, "%d-%m-%Y_%H-%M");
            auto time_string = oss.str();
            uint id = 0;
            std::string name = "screens\\" + time_string +"_0.bmp";

            //Loop over its indexes
            while(true){
                name = "screens\\" + time_string + "_" + std::to_string(id) +".bmp";
                if (!file_exists(name)){
                    break;
                }
                id++;
            }
            LPSTR fileName = const_cast<char *>(name.c_str());
            HANDLE bmp_file_handle = CreateFile(fileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

            // Add the size of the headers to the size of the bitmap to get the total file size
            DWORD dwSizeofDIB = (width * 3 + bmp_padding) * height + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

            //Offset to where the actual bitmap bits start.
            bmfHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER);

            //Size of the file
            bmfHeader.bfSize = dwSizeofDIB;

            //bfType must always be BM for Bitmaps
            bmfHeader.bfType = 0x4D42; //BM

            DWORD dwBytesWritten = 0;
            WriteFile(bmp_file_handle, (LPSTR)&bmfHeader, sizeof(BITMAPFILEHEADER), &dwBytesWritten, NULL);
            WriteFile(bmp_file_handle, (LPSTR)&bmp_info.bmiHeader, sizeof(BITMAPINFOHEADER), &dwBytesWritten, NULL);
            WriteFile(bmp_file_handle, (LPSTR)bmp_pixels, (width * 3 + bmp_padding) * height, &dwBytesWritten, NULL);

            //Close the handle for the file that was created
            CloseHandle(bmp_file_handle);

            DeleteDC(hdc);
            DeleteObject(hbmp);
            ReleaseDC(NULL, hdcScreen);
            delete[] bmp_pixels;
        }
    }
}

它在多台机器(Windows 10、XP 等)上运行良好。但是,在 Windows 7 上存在一个罕见的情况(可能还有其他情况,我不知道这是否只是运气不好或其他原因)导致屏幕截图为空白。就是全白。我进行了一些诊断并且很方便,它可以肯定地捕获正确的窗口,但不知何故它不能很好地捕获像素。

我深入挖掘并发现,每当我在 windows -> 性能选项 -> “调整以获得最佳性能”中设置此选项时,它突然开始工作并且肯定会截取屏幕截图(不再有白屏,这很棒)。

我现在想知道的是,如果我能以某种方式让我的代码更好地掩盖这些情况,因为强制用户更改他的窗口选项并不是一个理想的场景。

@EDIT:我发现这是使它起作用的选项,如果我禁用桌面合成,它就可以正常工作。

标签: c++winapi

解决方案


推荐阅读