CopyBitmapToClipboard这个函数。关于这个函数这里就先不提了,

后面会具体讲。

  现在的问题是:如果要使用许多截图,必须一一将截图先存成文件。原因有二,一是有的地方不支持直接Ctrl+V从

剪切板复制图像,二是不剪切板是一个公用的临时的存储区,如果有多幅截图,或是其他程序调用了剪切板,截图就丢

失了。

  那么要实现的功能就是讲截取的图像,转换为可存储的格式,最后存成文件。

1、首先,确定格式,最优先的当然是位图了。

2、其次,考虑到前面小程序截图效果的不足,这里提供一种解决方法:那就是获得待截图的窗口句柄后只提取它的位

置信息,而不直接复制图像。

3、复制图像的功能由另一个函数:CopyScreenToBitmap完成。看名字就知道这是讲屏幕的一个部分复制到位图对象

中。这就是我要介绍的第二个函数:

///
// 功能:将屏幕的某个矩形区域的图像复制到一个位图对象中
// 参数:输入一个确定矩形范围的LPRECT型数据
// 返回:一个存储了截图的bitmap句柄
///
HBITMAP CopyScreenToBitmap(LPRECT lpRect)
{
	HDC hScrDC, hMemDC; 
	// 屏幕和内存设备描述表 
	HBITMAP hBitmap, hOldBitmap; 
	// 位图句柄 
	int nX, nY, nX2, nY2; 
	// 选定区域坐标 
	int nWidth, nHeight; 
	// 位图宽度和高度 
	int xScrn, yScrn; 
	// 屏幕分辨率
	
	// 确保选定区域不为空矩形 
	if (IsRectEmpty(lpRect)) 
		return NULL; 
	
	//为屏幕创建设备描述表 
	hScrDC = CreateDC(_T("DISPLAY"), NULL, NULL, NULL); 
	//为屏幕设备描述表创建兼容的内存设备描述表 
	hMemDC = CreateCompatibleDC(hScrDC); 
	// 获得选定区域坐标 
	nX = lpRect->left; 
	nY = lpRect->top; 
	nX2 = lpRect->right; 
	nY2 = lpRect->bottom; 
	// 获得屏幕分辨率 
	xScrn = GetDeviceCaps(hScrDC, HORZRES); 
	yScrn = GetDeviceCaps(hScrDC, VERTRES); 
	//确保选定区域是可见的 
	if (nX <0) 
		nX = 0; 
	if (nY <0) 
		nY = 0; 
	if (nX2 > xScrn) 
		nX2 = xScrn; 
	if (nY2 > yScrn) 
		nY2 = yScrn; 
	nWidth = nX2 - nX; 
	nHeight = nY2 - nY; 
	// 创建一个与屏幕设备描述表兼容的位图 
	hBitmap = CreateCompatibleBitmap 
		(hScrDC, nWidth, nHeight); 
	// 把新位图选到内存设备描述表中 
	hOldBitmap = (HBITMAP)SelectObject(hMemDC, hBitmap); 
	// 把屏幕设备描述表拷贝到内存设备描述表中 
	BitBlt(hMemDC, 0, 0, nWidth, nHeight, 
		hScrDC, nX, nY, SRCCOPY); 
	//得到屏幕位图的句柄 
	hBitmap = (HBITMAP)SelectObject(hMemDC, hOldBitmap); 
	//清除 
	DeleteDC(hScrDC); 
	DeleteDC(hMemDC); 
	// 返回位图句柄 
	return hBitmap; 
}



4、既然得到了截图的HBITMAP对象,那么就可以重写CopyBitmapToClipboard方法了:

/
// 功能:将HBITMAP对象复制到剪切板
// 参数:要复制的HBITMAP对象

void CMy123Dlg::CopyBitmapToClipboard(HBITMAP hBitmap)
{
	OpenClipboard();
	EmptyClipboard();
	SetClipboardData(CF_BITMAP, hBitmap);
	CloseClipboard();
}



5、这样运行后的效果如下:

MFC截图程序的实现(五)_句柄


MFC截图程序的实现(五)_设备描述_02

与上一篇文章的截图对比,可以看到,黑色的背景不产生了。似乎效果不错。

但是,细心的朋友发现了,截取资源管理器的同时还将在其之上的程序窗口也截进去了。

所以,结论是:这样的截图方法想要截取完整的图像,必须保证窗口前无遮挡。


6、最后,贴出完整的代码:

MyPic.cpp不变, 123Dlg.cpp如下:

// 123Dlg.cpp : implementation file
//

#include "stdafx.h"
#include "123.h"
#include "123Dlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif


HWND My123Hwnd;
HWND cutWnd;
CRect cutRc;


/
// CMy123Dlg dialog

CMy123Dlg::CMy123Dlg(CWnd* pParent /*=NULL*/)
	: CDialog(CMy123Dlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CMy123Dlg)
		// NOTE: the ClassWizard will add member initialization here
	//}}AFX_DATA_INIT
	// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CMy123Dlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CMy123Dlg)
		// NOTE: the ClassWizard will add DDX and DDV calls here
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CMy123Dlg, CDialog)
	//{{AFX_MSG_MAP(CMy123Dlg)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_WM_TIMER()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/
// CMy123Dlg message handlers

BOOL CMy123Dlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon
	
	// TODO: Add extra initialization here
	
	m_pic.SubclassDlgItem(IDC_PIC,this);  // 关联控件
    My123Hwnd = m_hWnd;  // 赋值

	return TRUE;  // return TRUE  unless you set the focus to a control
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CMy123Dlg::OnPaint() 
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

// The system calls this to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CMy123Dlg::OnQueryDragIcon()
{
	return (HCURSOR) m_hIcon;
}


void CMy123Dlg::OnTimer(UINT nIDEvent) 
{
	// TODO: Add your message handler code here and/or call default
	
	if (1 == nIDEvent) {
		POINT pnt;
		RECT rc;
		HWND DeskHwnd = ::GetDesktopWindow(); //取得桌面句柄
		HDC DeskDC = ::GetWindowDC(DeskHwnd); //取得桌面设备场景
		int oldRop2 = SetROP2(DeskDC, R2_NOTXORPEN);
		::GetCursorPos(&pnt); //取得鼠标坐标
		HWND UnHwnd = ::WindowFromPoint(pnt) ; //取得鼠标指针处窗口句柄
		

		::GetWindowRect(UnHwnd, &rc); //获得窗口矩形
		cutWnd = UnHwnd;
		cutRc = rc;
		
		if( rc.left < 0 ) rc.left = 0;
		if (rc.top < 0 ) rc.top = 0;
		HPEN newPen = ::CreatePen(PS_SOLID, 3, RGB(255, 0, 0)); //建立新画笔,载入DeskDC
		HGDIOBJ oldPen = ::SelectObject(DeskDC, newPen);
		::Rectangle(DeskDC, rc.left, rc.top, rc.right, rc.bottom); //在窗口周围显示闪烁矩形
		Sleep(400); //设置闪烁时间间隔
		::Rectangle( DeskDC, rc.left, rc.top, rc.right, rc.bottom);
		::SetROP2(DeskDC, oldRop2);
		::SelectObject( DeskDC, oldPen);
		::DeleteObject(newPen);
		::ReleaseDC( DeskHwnd, DeskDC);
		DeskDC = NULL;
	}

	if (2 == nIDEvent) 
	{
		if (m_pic.GetIsFinshed()) {
			//CopyBitmapToClipboard(FromHandle(cutWnd), TRUE);
			CopyBitmapToClipboard(CopyScreenToBitmap((LPRECT)&cutRc));
		}
	}
	
	CDialog::OnTimer(nIDEvent);
}


void CMy123Dlg::CopyBitmapToClipboard(CWnd *wnd, BOOL FullWnd)
{
	CDC *dc;
	if(FullWnd)
	{ 
		/* 抓取整个窗口*/
		dc = new CWindowDC(wnd);
	}      
	else
	{ 
		/* 仅抓取客户区时*/
		dc = new CClientDC(wnd);
	} 
	
	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);
	//直接调用OpenClipboard(),而不用wnd->GetParent()->OpenClipboard();
	wnd->OpenClipboard();
	
	::EmptyClipboard();
	::SetClipboardData(CF_BITMAP, bm.m_hObject);
	CloseClipboard();
	//恢复原始环境
	memDC.SelectObject(oldbm);
	bm.Detach();  
	delete dc;
	
	KillTimer(2);

	// 加一句提示
	MessageBox(_T("复制完成"));

}

/
// 功能:将HBITMAP对象复制到剪切板
// 参数:要复制的HBITMAP对象

void CMy123Dlg::CopyBitmapToClipboard(HBITMAP hBitmap)
{
	KillTimer(2);

	OpenClipboard();
	EmptyClipboard();
	SetClipboardData(CF_BITMAP, hBitmap);
	CloseClipboard();

	// 加一句提示
	MessageBox(_T("复制完成"));

}

///
// 功能:将屏幕的某个矩形区域的图像复制到一个位图对象中
// 参数:输入一个确定矩形范围的LPRECT型数据
// 返回:一个存储了截图的bitmap句柄
///
HBITMAP CMy123Dlg::CopyScreenToBitmap(LPRECT lpRect)
{
	HDC hScrDC, hMemDC; 
	// 屏幕和内存设备描述表 
	HBITMAP hBitmap, hOldBitmap; 
	// 位图句柄 
	int nX, nY, nX2, nY2; 
	// 选定区域坐标 
	int nWidth, nHeight; 
	// 位图宽度和高度 
	int xScrn, yScrn; 
	// 屏幕分辨率
	
	// 确保选定区域不为空矩形 
	if (IsRectEmpty(lpRect)) 
		return NULL; 
	
	//为屏幕创建设备描述表 
	hScrDC = CreateDC(_T("DISPLAY"), NULL, NULL, NULL); 
	//为屏幕设备描述表创建兼容的内存设备描述表 
	hMemDC = CreateCompatibleDC(hScrDC); 
	// 获得选定区域坐标 
	nX = lpRect->left; 
	nY = lpRect->top; 
	nX2 = lpRect->right; 
	nY2 = lpRect->bottom; 
	// 获得屏幕分辨率 
	xScrn = GetDeviceCaps(hScrDC, HORZRES); 
	yScrn = GetDeviceCaps(hScrDC, VERTRES); 
	//确保选定区域是可见的 
	if (nX <0) 
		nX = 0; 
	if (nY <0) 
		nY = 0; 
	if (nX2 > xScrn) 
		nX2 = xScrn; 
	if (nY2 > yScrn) 
		nY2 = yScrn; 
	nWidth = nX2 - nX; 
	nHeight = nY2 - nY; 
	// 创建一个与屏幕设备描述表兼容的位图 
	hBitmap = CreateCompatibleBitmap 
		(hScrDC, nWidth, nHeight); 
	// 把新位图选到内存设备描述表中 
	hOldBitmap = (HBITMAP)SelectObject(hMemDC, hBitmap); 
	// 把屏幕设备描述表拷贝到内存设备描述表中 
	BitBlt(hMemDC, 0, 0, nWidth, nHeight, 
		hScrDC, nX, nY, SRCCOPY); 
	//得到屏幕位图的句柄 
	hBitmap = (HBITMAP)SelectObject(hMemDC, hOldBitmap); 
	//清除 
	DeleteDC(hScrDC); 
	DeleteDC(hMemDC);

	// 返回位图句柄 
	return hBitmap; 
}




7、完整源代码: