使用MSDN库进行多屏幕捕获

时间:2015-02-01 09:29:36

标签: c++ winapi gdi screen-capture

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

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

HDC hdc = GetDC(NULL);
EnumDisplayMonitors(hdc, NULL, MyCapScreenEnumProc, 0);
ReleaseDC(NULL, hdc);

对于“ BOOL CALLBACK MyCapScreenEnumProc(HMONITOR hMonitor,HDC hdcMonitor,LPRECT lprcMonitor,LPARAM dwData)”回调函数,我复制了示例函数“ int CaptureAnImage(HWND hWnd)< / em>“来自MSDN:Capturing an Image并做了以下修改:

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

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

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

1 个答案:

答案 0 :(得分: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))