1

I'm trying to take a screen capture of the main dialog in my MFC application and save it as an image file. I tried about every example I could find online and always end up with the same result: the image file has the correct dimensions (I tried this with dialogs other than the main one just to be sure), but it is all black. My most recent solution is using the CBitmap class to transfer the main dialog handle to a CImage. Here is my code:

CWnd* mainWindow;
CDC* mainWindowDC;
CBitmap bitmapToSave;
CImage imageToSave;
CRect windowRect;

//Get main window context and create bitmap from it
mainWindow = AfxGetMainWnd();
mainWindowDC = mainWindow->GetWindowDC();
mainWindow->GetWindowRect(&windowRect);
bitmapToSave.CreateCompatibleBitmap(mainWindowDC, windowRect.Width(), windowRect.Height());
imageToSave.Attach(bitmapToSave);
imageToSave.Save("C:\\Capture\\image.bmp", Gdiplus::ImageFormatBMP);
jimturmoy
  • 33
  • 8

2 Answers2

1

Here is the way to do it:

HRESULT CaptureScreen(const CString& sImageFilePath)
{
   CWnd* pMainWnd = AfxGetMainWnd();
   CRect rc;
   pMainWnd->GetWindowRect(rc);
   CImage img;
   img.Create(rc.Width(), rc.Height(), 24);
   CDC memdc;
   CDC* pDC = pMainWnd->GetWindowDC();
   memdc.CreateCompatibleDC(pDC);
   CBitmap* pOldBitmap = memdc.SelectObject(CBitmap::FromHandle((HBITMAP)img));
   memdc.BitBlt(0, 0, rc.Width(), rc.Height(), pDC, 0, 0, SRCCOPY);
   memdc.SelectObject(pOldBitmap);
   return img.Save(sImageFilePath, Gdiplus::ImageFormatPNG);
}

Please also take a look at this nice implementation: http://www.codeguru.com/cpp/article.php/c18347/C-Programming-Easy-Screen-Capture-Using-MFCATL.htm

Andrew Komiagin
  • 6,446
  • 1
  • 13
  • 23
  • 1
    This is returning a white image for me – jimturmoy Apr 14 '15 at 19:19
  • I've just updated the code that captures the main window of MFC app. Now it works as expected (tested on my machine). – Andrew Komiagin Apr 14 '15 at 20:09
  • This seems to have worked, thanks a lot! Should I call ReleaseDC or DeleteDC as well in that situation? – jimturmoy Apr 15 '15 at 15:05
  • Yes you need to call `ReleaseDC()` for `pDC` (retrieved via `GetWindowDC()`). The rest of DCs (created on stack) will be destroyed automatically when they go out of scope (destructor calls `ReleaseDC()` ). – Andrew Komiagin Apr 15 '15 at 17:14
  • So the ideal is like capture desktop screen but find the wanted window rectangle (postion and size) and capture that area only. But it only work properly if that window is the foreground window. – 123iamking Dec 17 '16 at 03:53
1

You created the bitmap, but you didn't put anything into it. You need to blit from one DC to another to make a copy of what's on the screen.

// ...
CMemDC dcMem;
dcMem.CreateCompatibleDC(&mainWindowDC);
CBitmap * pOld = dcMem.SelectObject(&bitmapToSave);
dcMem.BitBlt(0, 0, windowRect.Width(), windowRect.Height(), &mainWindowDC, windowRect.left, windowRect.top, SRCCOPY);
dcMem.SelectObject(pOld);
// ...
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622