65.9K
CodeProject 正在变化。 阅读更多。
Home

捕获屏幕到剪贴板,包括下拉菜单

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.80/5 (14投票s)

2002年4月28日

4分钟阅读

viewsIcon

576268

downloadIcon

4766

捕获屏幕图像到剪贴板,包括下拉菜单、组合框列表等

Sample Image

引言

市面上有很多共享软件可以捕获屏幕并将其复制到剪贴板或保存为文件。其中一些收费超过15美元,但却无法捕获下拉菜单/列表。问题在于,当用户“激活屏幕捕获”程序时,所有其他窗口都会变得非活动化,并关闭其文件菜单或下拉列表。这个简单的程序可以捕获任何屏幕图像,包括下拉菜单或组合框列表。

基本思想

由于点击“PrintScreen”会将整个窗口屏幕发送到剪贴板,因此我们可以从剪贴板读取“整个窗口屏幕”,然后让用户复制屏幕的任何部分到剪贴板。要实现这一点,我们需要:

  1. 键盘挂钩:当用户点击“PrintScreen”时,应用程序会收到通知。
  2. 从剪贴板读取图像。
  3. 将图像复制并粘贴到剪贴板。

[DLL] 键盘挂钩

要挂钩系统范围内的键盘事件,我们需要一个 DLL。演示源代码 *HScr2Clpbrd_DLL.cpp* 与 Joseph[2] 的演示程序非常相似。最大的区别在于 Joseph[2] 有一个很棒的头文件,我没有使用它。如果您是第一次创建简单的 DLL 文件,可以按照以下步骤操作:VC++ -> 文件菜单 -> 新建 -> Win32 动态链接库 -> 一个简单的 DLL 项目 -> 完成。您将得到一个 `DLLMain()` 方法。
修改...

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

...

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

并实现您的 `InstallKBHook()` 和 `UnInstallKBHook()`,它们只是使用 API:`SetWindowsHookEx()` 和 `UnhookWindowsHookEx()` 来挂钩/解除挂钩键盘事件。

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

您可能需要稍微注意一下声明:

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

由于我们将这些方法**导出**到应用程序,因此我们将其声明为 `__declspec(dllexport)`;另一方面,应用程序需要**导入**它们(请参阅 *HScr2ClpDlg.cpp*)。

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

如果您想了解更多关于 API 挂钩的信息,请参阅 Joseph[2]、Ivo[3] 和 Kyle[4] 的详细介绍。

[DLL] 向应用程序发送消息

到目前为止,我们可以 `InstallKBHook()` 和 `UnInstallKBHook()`,但我们需要通知我们的应用程序“PrintScreen”已被点击。我们需要注册我们的窗口消息。

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

一旦有了我们的窗口消息,我们就可以**发送消息**来通知我们的应用程序用户点击了“PrintScreen”。

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

挂钩系统范围内的键盘非常简单,不是吗?请参阅 Joseph[1] 获取更多**广泛而深入**的细节。

[应用程序] 从剪贴板读取图像

当 `DLL` 发送消息时,应用程序需要接收该消息。由于这是我们用户定义的窗口消息,因此我们需要手动**映射**它。

  1. 在头文件 *HScr2ClpDlg.h* 中,声明此方法:
    afx_msg BOOL OnClickPrintScreen(WPARAM wParam, LPARAM lParam);
  2. 在 cpp 文件 *HScr2ClpDlg.cpp* 中,**消息映射**添加此 `ON_REGISTERED_MESSAGE(UWM_ClickPrnScrn, OnClickPrintScreen)`。
    BEGIN_MESSAGE_MAP(CHScr2ClpDlg, CDialog)
    	ON_REGISTERED_MESSAGE(UWM_ClickPrnScrn, OnClickPrintScreen)
    	//{{AFX_MSG_MAP(CHScr2ClpDlg)
    	...
    	//}}AFX_MSG_MAP
    END_MESSAGE_MAP()

在 `OnClickPrintScreen()` 的定义中,我们首先使用 `IsClipboardFormatAvailable(CF_BITMAP)` 验证剪贴板是否包含 `BITMAP` 数据,然后打开(使用 `OpenClipboard()`)剪贴板。如果到目前为止一切正常,则将 `CBitmap`**附加**到剪贴板的 `BITMAP` 句柄。

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

由于我们现在有了一个 `CBitmap`,我们可以将其**复制**到应用程序的面板上,并让用户“选择”她/他需要的任何图像。

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);

[应用程序] 复制图像并粘贴到剪贴板

我们将用户从客户区域“选择”的图像复制并粘贴到剪贴板。

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;
}

摘要

这个小程序非常简单,因为它没有任何复杂的编码。您可以在 CodeProject.com 或其他网站上找到许多文章,介绍如何将图像复制到剪贴板、挂钩键盘或捕获鼠标移动等。

但是,演示程序的面板上有一个很酷/很棒的“mailto 按钮”; IMHO,这是这个小工具中最难编写的代码。请参考 Chris Maunder 的“Tray Calendar”,Chris[5]。这个程序的核心思想是“汇集大家的想法,一起玩乐”;当然,我需要为我的工作捕获屏幕。当我有时间时,我可能会增强它,提供一个带有“预览”面板,为用户提供“复制”、“另存为文件”和“放弃”选项。如果有人可以发布您的增强功能,我很乐意更新这篇文章,并表示感谢。

已知错误

如果您运行一个程序并将其留在屏幕上,不要将其最小化到任务栏,然后点击“PrintScreen”,应用程序将按预期将自身设置为最顶层窗口。但是,“最小化”和“关闭”按钮将不起作用,直到您将其设置为非顶层 Z 顺序。换句话说,您需要单击另一个窗口使应用程序“非活动化”,然后单击应用程序本身使其“活动化”;这样,“最小化”和“关闭”按钮才能正常工作。另一方面,如果您将应用程序“最小化”到任务栏然后“点击”“PrintScreen”,一切都会正常工作。

参考文献

  • [1]:“消息管理” (Message Management),Joseph M. Newcomer,2000年5月17日
  • [2]:“挂钩与 DLL” (Hooks and DLLs),Joseph M. Newcomer,2001年4月1日
  • [3]:“API 挂钩揭秘” (API hooking revealed),Ivo Ivanov,2002年4月6日
  • “Win32 挂钩” (Win32 Hooks),Kyle Marsh,1993年7月29日,MSDN
  • [5]:“托盘日历” (Tray Calendar),Chris Maunder,2002年4月6日

许可证

本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。

作者可能使用的许可证列表可以在此处找到。

© . All rights reserved.