【问题标题】:C++ WinAPI screenshot of a window in the background后台窗口的 C++ WinAPI 屏幕截图
【发布时间】:2017-12-07 13:07:35
【问题描述】:

我想截取一个没有焦点的窗口。我的功能适用于某些窗口,但不适用于所有窗口,我不知道为什么。我尝试用它来捕捉 Bluestacks App Player 的窗口,并且效果很好。但对于 Nox App Player 和其他一些游戏,它根本不起作用。我只是得到一个窗口大小的黑色图像。

这是目前为止的代码:

void screenshot_window(HWND 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;
    HANDLE bmp_file_handle = CreateFile("TestFile.bmp", 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;
}

【问题讨论】:

    标签: c++ winapi screenshot


    【解决方案1】:

    这可能发生在目标窗口只是一个容器并且不负责绘制消息的许多应用程序中。像记事本这样的标准 win32 应用程序的行为并非如此。但是你可能会在很多浏览器上遇到这个问题。

    您可以随时截取桌面窗口。你可以得到目标窗口的屏幕坐标,然后bitblt目标窗口的那个部分。对您的代码进行以下更改:

    //GetClientRect(handle, &client_rect);
    GetWindowRect(handle, &client_rect);
    
    //HDC hdcScreen = GetDC(handle);
    HDC hdcScreen = GetDC(HWND_DESKTOP);
    
    //BitBlt(hdc, 0, 0, width, height, hdcScreen, 0, 0, SRCCOPY);
    BitBlt(hdc, 0, 0, width, height, hdcScreen, client_rect.left, client_rect.top, SRCCOPY);
    
    //ReleaseDC(NULL, hdcScreen);
    ReleaseDC(HWND_DESKTOP, hdcScreen);
    

    在截屏之前,目标窗口必须是屏幕上最顶部的可见窗口。例如,您可以按此顺序调用screenshot_window

    HWND hwnd = FindWindow(0, L"Calculator");
    SetForegroundWindow(hwnd);
    Sleep(1000);
    screenshot_window(hwnd);
    

    或者,您可以使用Dwm Thumbnail API 在您自己的窗口中绘制目标窗口。但同样,您不能使用GetDC(my_hWnd) 从窗口上的“Dwm Thumbnail”访问位图。同样,您必须使用GetDC(HWND_DESKTOP) 截取桌面窗口的屏幕截图。这次确保您自己的窗口是顶部窗口。

    应用程序必须支持 DPI,否则屏幕坐标将不匹配。


    原始代码中也存在资源泄漏。 GetDC 应该用 ReleaseDC 清理,使用相同的 handle,而不是 NULL

    HDC hdcScreen = GetDC(handle);
    ...
    //ReleaseDC(NULL, hdcScreen);
    ReleaseDC(handle, hdcScreen);
    

    【讨论】:

    • 最后一行应该是ReleaseDC(handle, hdcScreen);
    • 感谢您的回答,有没有办法在它们被其他窗口重叠时捕获这些窗口而不将窗口置于前台?
    • 您可以使用Dwm Thumbnail API 在您自己的窗口中绘制目标窗口。确保您自己的窗口是顶部窗口。同样,您必须使用 GetDC(HWND_DESKTOP) 截取桌面窗口的屏幕截图
    猜你喜欢
    • 2019-03-25
    • 1970-01-01
    • 2017-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-28
    相关资源
    最近更新 更多