Click here to Skip to main content
15,887,998 members
Articles / Desktop Programming / MFC

Capture Screen to Clipboard including dropdown menu

Rate me:
Please Sign up or sign in to vote.
4.80/5 (16 votes)
12 Mar 20034 min read 570.3K   4.7K   109   54
Capture screen image to clipboard including dropdown menu, combobox lists etc

Sample Image

Introduction

There are many shareware that capture screen and copy image to clipboard or save as a file. Some of them charge more than $15 and can't capture dropdown menu/list. The problem is when user "active CaptureScreen" program, all other windows become inactive and close their file menu or dropdown list. This simple program can capture any screen image including dropdown menu or combobox list.

Basic Idea

Since clicking on "PrintScreen" will send an entire window screen to the clipboard, we can read "entire window screen" from clipboard and then let users copy any portion of screen to clipboard. To implement this, we need:

  1. Hook keyboard: when user clicks "PrintScreen", application will be notified.
  2. Read image from clipboard.
  3. Copy image and paste to clipboard.

[DLL] Hook Keyboard

To hook system wide keyboard event, we need a DLL. The demo source code HScr2Clpbrd_DLL.cpp is pretty much similar to Joseph[2] demo program. The big difference is that Joseph[2] has a pretty neat header file that I don't use it. In case it's the first time you create a simple DLL file, you may follow this: VC++ -> File Menu -> New -> Win32 Dynamic-link Library -> A simply DLL project -> finish. you will get a DLLMain() method.
Change...

C++
BOOL APIENTRY DllMain( HANDLE hModule,DWORD  ul_reason_for_call, 
LPVOID lpReserved)

...to:

C++
BOOL APIENTRY DllMain( HINSTANCE hInstance,DWORD  ul_reason_for_call,
LPVOID lpReserved)

And implement your InstallKBHook() and UnInstallKBHook() that simply use API: SetWindowsHookEx() and UnhookWindowsHookEx(), to hook/unhook keyboard event.

C++
hookKeyBoard = SetWindowsHookEx(WH_KEYBOARD,(HOOKPROC)KeyboardHook,hInst,0);
BOOL bUnHook = UnhookWindowsHookEx(hookKeyBoard);

You may want to pay a little attention to the declaration of:

C++
__declspec(dllexport) BOOL InstallKBHook(HWND hWnd);
__declspec(dllexport) BOOL UnInstallKBHook(HWND hWnd);

Because we will export those methods to the application, we declare them as __declspec(dllexport); and, on the other hand, application need to import them (see HScr2ClpDlg.cpp):

C++
__declspec(dllimport) BOOL InstallKBHook(HWND hWnd);
__declspec(dllimport) BOOL UnInstallKBHook(HWND hWnd);

If you would like to know more about API Hook, please refer to Joseph[2], Ivo[3], and Kyle[4] for more details.

[DLL] Post Message to Application

So far, we can InstallKBHook() and UnInstallKBHook() but we need to notify our application that "PrintScreen" has been clicked. We need to register our window message:

C++
UWM_ClickPrnScrn = ::RegisterWindowMessage(
"UWM_ClickPrintScreen_B2ABC742-0A63-49c3-9ACB-CF0068027A66");

Once we have our Window Message, we can Post Message to notify our application that user clicks "PrintScreen":

C++
if( (nCode >=0) && (nCode == HC_ACTION) && (wParam == VK_SNAPSHOT))
	::PostMessage(hWndReceiver,UWM_ClickPrnScrn,0,0);

It's pretty simple to hook system wide keyboard. Isn't it? Please refer to Joseph[1] for more, wide and deep, details.

[Apps] Read Image from Clipboard

When DLL posts message, the application needs to receive the message. Since it's our, user defined, window message, we need to manually Map it.

  1. In header file, HScr2ClpDlg.h, declare this method:
    C++
    afx_msg BOOL OnClickPrintScreen(WPARAM wParam, LPARAM lParam);
  2. In cpp file, HScr2ClpDlg.cpp, message map adds this ON_REGISTERED_MESSAGE(UWM_ClickPrnScrn, OnClickPrintScreen):
    C++
    BEGIN_MESSAGE_MAP(CHScr2ClpDlg, CDialog)
    	ON_REGISTERED_MESSAGE(UWM_ClickPrnScrn, OnClickPrintScreen)
    	//{{AFX_MSG_MAP(CHScr2ClpDlg)
    	...
    	//}}AFX_MSG_MAP
    END_MESSAGE_MAP()

In the definition of OnClickPrintScreen(), we first verify using IsClipboardFormatAvailable(CF_BITMAP) that the clipboard contains BITMAP data and then opens (using OpenClipboard()) clipboard. If so far is fine, attach a CBitmap to clipboard BITMAP Handle.

C++
HBITMAP hBm;
CBitmap cBm;
hBm = (HBITMAP)GetClipboardData(CF_BITMAP);
...
if(!cBm.Attach(hBm))
...

Since we have a CBitmap now, we can Copy it to our application's panel and let user "select" any image she/he needs:

C++
CClientDC dcScreen(this);
CDC dcMem;
dcMem.CreateCompatibleDC(&dcScreen);
CBitmap* pOldBitmap = dcMem.SelectObject(&cBm);
dcScreen.BitBlt(0,0,bBm.bmWidth,bBm.bmHeight ,&dcMem,0,0,SRCCOPY);
dcMem.SelectObject(pOldBitmap);

[Apps] Copy Image and Paste to Clipboard

We copy user's "selection" from client area and paste to clipboard:

C++
BOOL CHScr2ClpDlg::CopyRect2Clipboard()
{
	CRect rect(m_ptFirst,m_ptLast);
	rect.NormalizeRect();	
	if(rect.IsRectEmpty() || rect.IsRectNull())
		return FALSE;	

	//CDC *pDc = GetWindowDC();//include Non-Client area.
	CClientDC dcScrn(this);
	CDC memDc;
	if(!memDc.CreateCompatibleDC(&dcScrn))
		return FALSE;	
	CBitmap bitmap;
	if( !bitmap.CreateCompatibleBitmap(&dcScrn,rect.Width(),
            rect.Height()))
		return FALSE;
	CBitmap* pOldBitmap = memDc.SelectObject(&bitmap);
	memDc.BitBlt(0,0,rect.Width(),rect.Height(),&dcScrn,rect.left ,
                     rect.top ,SRCCOPY );		
	if(OpenClipboard())
	{
		EmptyClipboard();
		SetClipboardData(CF_BITMAP,bitmap.GetSafeHandle());
		CloseClipboard();
	}
	memDc.SelectObject(pOldBitmap);
	return TRUE;
}

Summary

This tiny program is very simple because it doesn't have any complicated coding. You can find many articles showing you how to Copy images to the clipboard, Hook keyboard, or, Capture mouse movement... from CodeProject.com or other web sites. 

However, there is one cool/neat "mailto button" on the demo program panel; it's, IMHO, the most difficult coding in this tiny utility. Please refer to Chris Maunder's "Tray Calendar", Chris[5]. The idea of this program is "put people's idea together and have fun"; and, of course, I need to capture screen for my work. I may enhance it that provide a "Preview" panel which has "Copy", "Save as file", and "Discard" options for user when I have time. If anyone can post your enhancement, I would love to update the article, and thank you.

Known Bug

If you run a program and leave it on screen, don't minimize it to taskbar, and click "PrintScreen", apps will set itself as MostTop Window as expected. But "minimize" and "close" buttons will not work until you set it to non-top Z-order. In other words, you need to click another window to make apps "inactive" and click apps itself to make it "active"; therefore, "minimize" and "close" buttons will work as usual. On the other hand, if you "minimize" apps to taskbar and "click "PrintScreen", everything just works fine.

References

License

This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below.

A list of licenses authors might use can be found here.


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralRe: Bug Fix ? Pin
Dirk Brouns11-Nov-02 4:26
sussDirk Brouns11-Nov-02 4:26 
AnswerRe: Bug Fix ? Pin
Y. Huang17-Jan-03 7:57
Y. Huang17-Jan-03 7:57 
Questionhow capture screen of the active application? Pin
Anonymous13-Oct-02 23:12
Anonymous13-Oct-02 23:12 
AnswerRe: how capture screen of the active application? Pin
Y. Huang14-Oct-02 10:39
Y. Huang14-Oct-02 10:39 
QuestionCan put the Capture DLL inside Win Service ? Pin
10-Sep-02 23:39
suss10-Sep-02 23:39 
AnswerRe: Can put the Capture DLL inside Win Service ? Pin
Y. Huang20-Sep-02 8:19
Y. Huang20-Sep-02 8:19 
GeneralRe: Can put the Capture DLL inside Win Service ? Pin
paoming20-Sep-02 15:44
paoming20-Sep-02 15:44 
GeneralCapturing screen changes and the cursor Pin
Neal Stublen18-Mar-03 7:41
Neal Stublen18-Mar-03 7:41 
To detect display changes you can try monitoring WM_PAINT messages (and some others) in a hook function. You would at least know what window has been redrawn. (I believe this is at least part of what WinVNC does to provide Windows screen sharing. You can get the source code for WinVNC at http://www.realvnc.com, but be aware it's updating is not always perfect.)

Another possible option would be to use a mirror driver that receives notification of GDI updates. I am not fully familiar with what mirror drivers do, but I recall seeing them before and thinking they could be used to record screen activity. (Try searching MSDN for "mirror driver" for more information.)

To capture the cursor image, see the GetCursorInfo() and GetIconInfo() functions in the Win32 API.

- GetCursorInfo() will provide a handle to the current global cursor and its current position.
- GetIconInfo() will provide access to two bitmaps that can be used to redraw the cursor on your captured image. Be sure to use the hot-spot locations with the cursor position to accurately duplicate the cursor's position.


From the article:

There are many sharewares that capture screen and copy image to clipboard or save as a file. Some of them charge more than $15 and can't capture dropdown menu/list. The problem is when user "active CaptureScreen" program, all other windows become inactive and close their file menu or dropdown list. This simple program can capture any screen image including dropdown menu or combobox list.

Well, I only charge $15 and I can capture those pesky dropdown menus and lists. Smile | :)

Neal Stublen
http://www.awesoftware.com
GeneralRe: Capturing screen changes and the cursor Pin
Y. Huang18-Mar-03 8:17
Y. Huang18-Mar-03 8:17 
GeneralCapturing the cursor Pin
Neal Stublen18-Mar-03 9:04
Neal Stublen18-Mar-03 9:04 
GeneralRe: Capturing the cursor Pin
Y. Huang18-Mar-03 12:50
Y. Huang18-Mar-03 12:50 
GeneralDraw cursor using Neal's idea... Thanks. Pin
Y. Huang8-May-03 10:36
Y. Huang8-May-03 10:36 
GeneralRe: Draw cursor using Neal's idea... weird problem Pin
stealth kid3-Nov-04 23:57
stealth kid3-Nov-04 23:57 
GeneralRe: Draw cursor using Neal's idea... weird problem Pin
Y. Huang5-Nov-04 3:56
Y. Huang5-Nov-04 3:56 
QuestionHow to capture rubber-band box of a window ? Pin
cmhome27-Jun-02 22:23
cmhome27-Jun-02 22:23 
AnswerRe: How to capture rubber-band box of a window ? Pin
28-Jun-02 5:40
suss28-Jun-02 5:40 
GeneralWindows functionality?! Pin
Oha Ooh30-Apr-02 4:37
Oha Ooh30-Apr-02 4:37 
GeneralRe: Windows functionality?! Pin
30-Apr-02 6:56
suss30-Apr-02 6:56 
GeneralRe: Windows functionality?! Pin
real name13-Mar-03 23:52
sussreal name13-Mar-03 23:52 
GeneralRe: Windows functionality?! Pin
Y. Huang14-Mar-03 7:59
Y. Huang14-Mar-03 7:59 
GeneralRe: Windows functionality?! Pin
real name16-Mar-03 19:33
sussreal name16-Mar-03 19:33 
GeneralRe: Windows functionality?! Pin
Y. Huang18-Mar-03 7:37
Y. Huang18-Mar-03 7:37 
GeneralRe: Windows functionality?! Pin
Neal Stublen18-Mar-03 7:46
Neal Stublen18-Mar-03 7:46 
General棒棒棒!!! Pin
29-Apr-02 4:45
suss29-Apr-02 4:45 
GeneralRe: 棒棒棒!!! Pin
29-Apr-02 6:27
suss29-Apr-02 6:27 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.