Multiple Screen Capture Using MSDN Library

I am working on screen capture with multiple display units. Since GetDesktopWindow () only gets the primary monitor handler, I tried to use EnumDisplayMonitors () to complete this task.

After reading the MSDN website, I wrote them in main ():

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

And for the BOOL CALLBACK callback function MyCapScreenEnumProc (HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData), I copied a sample function "int CaptureAnImage (HWND hWnd)" from MSDN: image capture and made the following modification:

  • instead of reading the HWND parameter, I declared it in the function and initialized it with GetDesktopWindow ()
  • Codes removed to stretch device context.
  • used the hdcMonitor parameter as a device context
  • used the lprcMonitor parameter for RECT
  • added codes to generate unique file names

Here is the complete code:

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

However, it turns out that he captures the main screen twice. With a second capture screen size, the same as my second monitor. I do not know what is wrong with the codes. Can someone point this out or suggest a better way to accomplish this task? Thanks!

+5
source share
1 answer

You need BitBlt from the monitor coordinates provided to you in lprcMonitor , and not from the zero point:

 // 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)) 
+4
source

Source: https://habr.com/ru/post/1212365/


All Articles