Screen Capture to the Clipboard |
|
One of the common questions that I find on the microsoft.public forums is "How do I get a bitmap of a window on the screen?" Well, here's a little subroutine I used extensively in writing Win32 Programming, because I realized that for the number of illustrations I wanted for the book, there was no hope of doing screen captures and then hand-editing just the control I wanted to show. So all my Explorers have a little button somewhere that performs a screen capture of the example window and drops it into the clipboard. I could then paste it into the document, or paste it into an editor and save it as a TIFF or JPEG as well.
To capture a window including the cursor, a more complex approach is required. This is described in an accompanying essay.
Note that I use a base CDC object and use Attach and Detach, instead of using the classes CClientDC and CWindowDC. I did this because each of these classes creates a specific kind of DC, but there was no one class that could create a DC that was either a client DC or a Window DC. Rather than introduce convoluted code to handle these cases, I just bound the HDC to a generic CDC.
ToClip.h:
void toClipboard(CWnd * wnd, BOOL FullWnd);
ToClip.cpp
#include "stdafx.h"
#include "toclip.h"
/****************************************************************
* toClipboard
* Inputs:
* CWnd * wnd: Window whose contents are to be sent
* to the clipboard
* BOOL FullWnd: TRUE for entire window,
* FALSE for client area
* Result: void
*
* Effect:
* Copies the contents of the client area or the window
* to the clipboard in CF_BITMAP format.
*****************************************************************/
void toClipboard(CWnd * wnd, BOOL FullWnd)
{
CDC dc;
if(FullWnd)
{ /* full window */
HDC hdc = ::GetWindowDC(wnd->m_hWnd);
dc.Attach(hdc);
} /* full window */
else
{ /* client area only */
HDC hdc = ::GetDC(wnd->m_hWnd);
dc.Attach(hdc);
} /* client area only */
CDC memDC;
memDC.CreateCompatibleDC(&dc);
CBitmap bm;
CRect r;
if(FullWnd)
wnd->GetWindowRect(&r);
else
wnd->GetClientRect(&r);
CString s;
wnd->GetWindowText(s);
CSize sz(r.Width(), r.Height());
bm.CreateCompatibleBitmap(&dc, sz.cx, sz.cy);
CBitmap * oldbm = memDC.SelectObject(&bm);
memDC.BitBlt(0, 0, sz.cx, sz.cy, &dc, 0, 0, SRCCOPY);
wnd->GetParent()->OpenClipboard();
::EmptyClipboard();
::SetClipboardData(CF_BITMAP, bm.m_hObject);
CloseClipboard();
memDC.SelectObject(oldbm);
bm.Detach(); // make sure bitmap not deleted with CBitmap object
// read my essay on this technique
::ReleaseDC(wnd->m_hWnd, dc.Detach()); //
}
Special thanks to Lim Bio Liong for pointing out that the CDC destructor does not call ReleaseDC. This bug was the result of converting some pure C code to MFC without my being careful enough.
Also, thanks to Charles Tam for pointing out a typo in the previous bug fix, a missing parameter.
7-Mar-02 |
Note that as of 7-Mar-02, there is an additional line (marked "New", above), which fixes a bug. |
2-Jun-07 |
The line added 7-Mar-02 was incorrect, and required a first parameter to ::ReleaseDC |
Added an explanation of why I did not use CWindowDC or CClientDC |
The views expressed in these essays are those of the author, and in no way represent, nor are they endorsed by, Microsoft.