使用MSDN库进行多屏幕捕获

Sam*_*uel 7 c++ winapi gdi screen-capture

我正在使用多个显示单元进行屏幕捕获.由于GetDesktopWindow()只获取主监视器的句柄,我尝试使用EnumDisplayMonitors()来完成这项工作.

在阅读MSDN网站后,我在main()中写了这些:

HDC hdc = GetDC(NULL);
EnumDisplayMonitors(hdc, NULL, MyCapScreenEnumProc, 0);
ReleaseDC(NULL, hdc);
Run Code Online (Sandbox Code Playgroud)

对于" BOOL CALLBACK MyCapScreenEnumProc(HMONITOR hMonitor,HDC hdcMonitor,LPRECT lprcMonitor,LPARAM dwData) "回调函数,我从MSDN复制了示例函数" int CaptureAnImage(HWND hWnd) " :捕获图像并进行了以下修改:

  1. 而不是读取HWND参数,我在函数中声明它并使用GetDesktopWindow()初始化它
  2. 删除了用于拉伸设备上下文的代码
  3. 使用参数hdcMonitor作为设备上下文
  4. 使用参数lprcMonitor作为RECT
  5. 添加了用于生成唯一文件名的代码

这是完整的代码:

BOOL CALLBACK MyCapScreenEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData)
{
    HWND hWnd = GetDesktopWindow();
    HDC hdcMemDC = NULL;
    HBITMAP hbmScreen = NULL;
    BITMAP bmpScreen;

    //generate a unique file name for the bitmaps
    static int file_number = 1;
    stringstream ss;
    ss << "all_capture_" << file_number++ << ".bmp";
    string filename = ss.str();
    wstring widestr = wstring(filename.begin(), filename.end());

    // Create a compatible DC which is used in a BitBlt from the window DC
    hdcMemDC = CreateCompatibleDC(hdcMonitor);

    if (!hdcMemDC)
    {
        MessageBox(hWnd, L"CreateCompatibleDC has failed", L"Failed", MB_OK);
        goto done;
    }

    // Get the client area for size calculation
    RECT rcClient = *lprcMonitor;

    // Create a compatible bitmap from the Window DC
    hbmScreen = CreateCompatibleBitmap(hdcMonitor, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top);

    if (!hbmScreen)
    {
        MessageBox(hWnd, L"CreateCompatibleBitmap Failed", L"Failed", MB_OK);
        goto done;
    }

    // Select the compatible bitmap into the compatible memory DC.
    SelectObject(hdcMemDC, hbmScreen);

    // Bit block transfer into our compatible memory DC.
    if (!BitBlt(hdcMemDC,
                0, 0,
                rcClient.right - rcClient.left, rcClient.bottom - rcClient.top,
                hdcMonitor,
                0, 0,
                SRCCOPY))
    {
        MessageBox(hWnd, L"BitBlt has failed", L"Failed", MB_OK);
        goto done;
    }

    // Get the BITMAP from the HBITMAP
    GetObject(hbmScreen, sizeof(BITMAP), &bmpScreen);

    BITMAPFILEHEADER   bmfHeader;
    BITMAPINFOHEADER   bi;

    bi.biSize = sizeof(BITMAPINFOHEADER);
    bi.biWidth = bmpScreen.bmWidth;
    bi.biHeight = bmpScreen.bmHeight;
    bi.biPlanes = 1;
    bi.biBitCount = 32;
    bi.biCompression = BI_RGB;
    bi.biSizeImage = 0;
    bi.biXPelsPerMeter = 0;
    bi.biYPelsPerMeter = 0;
    bi.biClrUsed = 0;
    bi.biClrImportant = 0;

    DWORD dwBmpSize = ((bmpScreen.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmpScreen.bmHeight;

    // Starting with 32-bit Windows, GlobalAlloc and LocalAlloc are implemented as wrapper functions that
    // call HeapAlloc using a handle to the process's default heap. Therefore, GlobalAlloc and LocalAlloc
    // have greater overhead than HeapAlloc.
    HANDLE hDIB = GlobalAlloc(GHND, dwBmpSize);
    char *lpbitmap = (char *)GlobalLock(hDIB);

    // Gets the "bits" from the bitmap and copies them into a buffer
    // which is pointed to by lpbitmap.
    GetDIBits(hdcMonitor, hbmScreen, 0,
              (UINT)bmpScreen.bmHeight,
              lpbitmap,
              (BITMAPINFO *)&bi, DIB_RGB_COLORS);




    // A file is created, this is where we will save the screen capture.
    HANDLE hFile = CreateFile(widestr.c_str(),
                              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 = dwBmpSize + 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(hFile, (LPSTR)&bmfHeader, sizeof(BITMAPFILEHEADER), &dwBytesWritten, NULL);
    WriteFile(hFile, (LPSTR)&bi, sizeof(BITMAPINFOHEADER), &dwBytesWritten, NULL);
    WriteFile(hFile, (LPSTR)lpbitmap, dwBmpSize, &dwBytesWritten, NULL);

    //Unlock and Free the DIB from the heap
    GlobalUnlock(hDIB);
    GlobalFree(hDIB);

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

    //Clean up
    done:
    DeleteObject(hbmScreen);
    DeleteObject(hdcMemDC);
    return TRUE;
}
Run Code Online (Sandbox Code Playgroud)

然而,事实证明它捕获主屏幕两次.第二个捕获的屏幕尺寸与我的第二个显示器相同.我不知道代码有什么问题.任何人都可以指出它或建议更好的方法来完成任务吗?谢谢!

Rom*_* R. 5

您需要BitBlt从监视器提供的坐标lprcMonitor,而不是从零点:

// Bit block transfer into our compatible memory DC.
if (!BitBlt(hdcMemDC,
            0, 0,
            rcClient.right - rcClient.left, rcClient.bottom - rcClient.top,
            hdcMonitor,
            lprcMonitor->left, lprcMonitor->top, // <<--- !!!
            SRCCOPY))
Run Code Online (Sandbox Code Playgroud)