Background info: I have this MFC application I coded and been using for a long time that pretty much automatically saves screenshots to the hard disk when the user hits the Prin
BOOL CaptureWindow(const CString& filename)
{
HWND hWnd = NULL;
hWnd = ::GetForegroundWindow();
if(!hWnd)
{
return FALSE;
}
CRect rect;
GetWindowRect(hWnd, &rect);
rect.NormalizeRect();
return DoCapture(CPoint(rect.left, rect.top), CSize(rect.Width(), rect.Height()), filename);
}
BOOL DoCapture(const POINT& coords, const SIZE& areaSize, const CString& filename)
{
CDC dc;
HDC hdc = GetDC(NULL); // <-- We use this instead of GetWindowDC.
// This is the only thing I had to change other than
// getting the window coordinates in CaptureWindow()
dc.Attach(hdc);
// Create a memory DC into which the bitmap will be captured
CDC memDC;
memDC.CreateCompatibleDC(&dc);
// If there is already a bitmap, delete it as we are going to replace it
CBitmap bmp;
bmp.DeleteObject();
ICONINFO info;
GetIconInfo((HICON)::GetCursor(), &info);
CURSORINFO cursor;
cursor.cbSize = sizeof(CURSORINFO);
GetCursorInfo(&cursor);
bmp.CreateCompatibleBitmap(&dc, areaSize.cx, areaSize.cy);
CBitmap * oldbm = memDC.SelectObject(&bmp);
// Before we copy the image in, we blank the bitmap to
// the background fill color
memDC.FillSolidRect(&CRect(0,0,areaSize.cx, areaSize.cy), RGB(255,255,255));
// Copy the window image from the window DC into the memory DC
memDC.BitBlt(0, 0, areaSize.cx, areaSize.cy, &dc, coords.x, coords.y, SRCCOPY|CAPTUREBLT);
// This part captures the mouse cursor and paints it on the image.
if(programSettings.bWantCursor)
{
int osVersion = OSCheck::GetMajorOSVersion(); // For some reason cursor icons in
// versions older than Vista are not
// top-aligned. So we compensate.
int offsetX = (osVersion >= 6) ? 0 : 10;
int offsetY = (osVersion >= 6) ? 0 : 10;
CPoint cursorOffset(cursor.ptScreenPos.x - coords.x - offsetX, cursor.ptScreenPos.y - coords.y - offsetY);
// Now draw the image of the cursor that we captured during
// the mouse move. DrawIcon will draw a cursor as well.
memDC.DrawIcon(cursorOffset, (HICON)cursor.hCursor);
}
memDC.SelectObject(oldbm);
Bitmap outputBitMap(bmp, NULL);
// Optionally copy the image to the clipboard.
if(programSettings.bWantClipboard)
{
if(OpenClipboard(NULL))
{
EmptyClipboard();
SetClipboardData(CF_BITMAP, bmp);
CloseClipboard();
}
}
BOOL success = DumpImage(&outputBitMap, filename);
DeleteObject(bmp.Detach());
DeleteDC(dc.Detach());
DeleteDC(memDC.Detach());
return success;
}
For reference: DumpImage pretty much uses the Gdi::Bitmap's Save method. It has been omitted because it has some app-specific code that isn't relevant to the example. Also an added bonus is that if you were wondering how to include the cursor in your screengrab then the code is also there. Hope it helps. Also worth mentioning, contrary to the traditional approach, this will also include any overlaid windows you might have on top of the captured window. Also, if you use this code for full screen captures, be warned that it will not capture video game windows. I'm actually wondering how to do that properly without having to resort to DirectX. This only affects Windows Vista/7 when running in Aero mode.
It's an excellent question that I unfortuneatly don't know exact answer to. My first idea was to grab the whole desktop and cut interesting part out of it.
I've dug into QT 4.5 sources to see how they do it, and found something like this. If you switch GetClientRect to GetWindowRect and strip QT boilerplate code you should get what you want. It looks like a hack though :)
QPixmap QPixmap::grabWindow(WId winId, int x, int y, int w, int h )
{
RECT r;
GetClientRect(winId, &r);
if (w < 0) w = r.right - r.left;
if (h < 0) h = r.bottom - r.top;
// Create and setup bitmap
HDC display_dc = GetDC(0);
HDC bitmap_dc = CreateCompatibleDC(display_dc);
HBITMAP bitmap = CreateCompatibleBitmap(display_dc, w, h);
HGDIOBJ null_bitmap = SelectObject(bitmap_dc, bitmap);
// copy data
HDC window_dc = GetDC(winId);
BitBlt(bitmap_dc, 0, 0, w, h, window_dc, x, y, SRCCOPY);
// clean up all but bitmap
ReleaseDC(winId, window_dc);
SelectObject(bitmap_dc, null_bitmap);
DeleteDC(bitmap_dc);
QPixmap pixmap = QPixmap::fromWinHBITMAP(bitmap);
DeleteObject(bitmap);
ReleaseDC(0, display_dc);
return pixmap;
}