Introduction
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
Capture
Screen"
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 "Print
Screen" 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
-
Hook keyboard: when user clicks
"Print
Screen", application
will be notified.
-
Read
image from
clipboard.
-
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 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
BOOL APIENTRY DllMain( HANDLE hModule,DWORD ul_reason_for_call,
LPVOID lpReserved)
toBOOL 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.hookKeyBoard = SetWindowsHookEx(WH_KEYBOARD,(HOOKPROC)KeyboardHook,hInst,0);
BOOL bUnHook = UnhookWindowsHookEx(hookKeyBoard);
You may
want to pay a little attention to
the declaration of
__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
)__declspec(dllimport) BOOL InstallKBHook(HWND hWnd);
__declspec(dllimport) BOOL UnInstallKBHook(HWND hWnd);
If you
like to know more about API Hook,
please refer Joseph[2], Ivo[3], and
Kyle[4] for more detail.
[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
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"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
Joseph[1] for more, wide and deep,
detail
[Apps] Read
image from
clipboard
When
DLL
post message, application need 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
afx_msg BOOL OnClickPrintScreen(WPARAM wParam, LPARAM lParam);
2. in cpp file,
HScr2ClpDlg.cpp
,
message map add this
ON_REGISTERED_MESSAGE(UWM_ClickPrnScrn,
OnClickPrintScreen)
BEGIN_MESSAGE_MAP(CHScr2ClpDlg, CDialog)
ON_REGISTERED_MESSAGE(UWM_ClickPrnScrn, OnClickPrintScreen)
...
END_MESSAGE_MAP()
In the
definition of
OnClickPrintScreen()
,
we first verify using
IsClipboardFormatAvailable(CF_BITMAP)
that the clipboard contains BITMAP
data and then open (using
OpenClipboard())
clipboard. If so far is fine, attach
a
CBitmap
to clipboard BITMAP HandleHBITMAP 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 needsCClientDC 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
BOOL CHScr2ClpDlg::CopyRect2Clipboard()
{
CRect rect(m_ptFirst,m_ptLast);
rect.NormalizeRect();
if(rect.IsRectEmpty() || rect.IsRectNull())
return FALSE;
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 ucancode.net 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. 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 program and leave it on
screen,
don't minimize it to tucancode.netbar, and
click "Print
Screen", 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 tucancode.netbar and "click "Print
Screen", everything just
works fine.