后台窗口的 C++ WinAPI 截图 [英] C++ WinAPI screenshot of a window in the background

查看:176
本文介绍了后台窗口的 C++ WinAPI 截图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想对没有焦点的窗口进行截图.我的功能适用于某些窗口,但不适用于所有窗口,我不知道为什么.我试着用它来捕捉 Bluestacks App Player 的窗口,它工作得很好.但是对于 Nox App Player 和其他一些游戏,它根本不起作用.我只得到一个与窗口大小相同的黑色图像.

I want to take screenshots of a window which has no focus. My function works for some windows but not for all and I don't know why. I tried it to capture the window of Bluestacks App Player and it works perfectly. But for Nox App Player and some other games it doesn't work at all. I just get a black image with the size of the window.

这是目前的代码:

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;
}

推荐答案

这可能发生在许多应用程序中,其中目标窗口只是一个容器并且不负责绘制消息.像记事本这样的标准 win32 应用程序不会这样.但是,例如,您可能会在使用许多浏览器时遇到此问题.

This can happen with many applications where the target window is simply a container and is not responsible for paint messages. Standard win32 applications like notepad don't behave as such. But you may run in to this problem with many browsers for example.

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

You can always take screenshot of desktop window. You can get the screen coordinate of target window, then bitblt that section of target window. Make the following changes to your code:

//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:

The target window must be top-most visible window on the screen before taking screen shot. For example you can call screenshot_window in this order:

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

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

Alternatively, you can use Dwm Thumbnail APIs to paint the target window in your own window. But again, you cannot use GetDC(my_hWnd) to access the bitmap from "Dwm Thumbnail" on your window. Again you would have to take screen shot of desktop window using GetDC(HWND_DESKTOP). This time make sure your own window is the top window.

应用程序必须能够识别 DPI,否则屏幕坐标将不匹配.

Application must be DPI aware otherwise screen coordinates will not match.

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

Also there is a resource leak in the original code. GetDC should be cleaned up with ReleaseDC using the same handle, not NULL

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

这篇关于后台窗口的 C++ WinAPI 截图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆