前面介绍了一些自定义截图的实现问题,现在接着上篇文章继续介绍如何实现自定义截图。
前面讲了,为了确定截图范围,要让用户画矩形。这里再讲细一点,问题就出来了:“这个矩形要画在哪里?”
一开始我想的是直接调用GetDesktopWindow()和GetWindowDC()直接在桌面环境上绘图,但是实际操作后,效果并不理想,主要是刷新的问题。矩形框画不出来或是一画出来就消失了。当然,在不刷新的窗口画出来的矩形很完整,基本达到了预期的目的。但是,我们不能期待截图时所有的窗口都不刷新。
所以,我采用了另一种方法,即将矩形画到对话框上,这个对话框有以下几个要求:(1)无标题栏、无边框;(2)最大化显示;(3)置顶显示(即在所有窗口之上);(4)最重要的是透明。
另外,说明一点:这个对话框的作用只是为了提供一个用户画矩形的载体,而用户画矩形的目的是为了得到截图的矩形区域,所以在确定了这个矩形范围之后,这个对话框(在调用截图函数之前)会马上关闭。
下面是具体的实现过程:
1、新建一个基于基本对话框的MFC工程,名为Custom_CutScreen。
2、关于最大化的透明对话框,决定采用模态对话框,因为该窗口只在画矩形时出现。
3、打开ResourceView页,插入一个ID为IDD_DIALOG_COVER的新对话框,通过ClassWizard为其添加一个新类CCoverDlg。
4、打开ClassWizard为CCoverDlg类添加以下几个消息的响应函数:WM_INITDIALOG、WM_LBUTTONDOWN、WM_LBUTTONUP、WM_MOUSEMOXE、WM_PAINT 和 WM_TIMER。
6.设置窗口透明用到了一个函数:SetLayeredWindowAttributes。
BOOL SetLayeredWindowAttributes(
HWND hwnd, // 指定分层窗口句柄
COLORREF crKey, // 指定需要透明的背景颜色值,可用RGB()宏
BYTE bAlpha, // 设置透明度,0表示完全透明,255表示不透明
DWORD dwFlags // 透明方式
);
其中,dwFlags参数可取以下值:
LWA_ALPHA时:crKey参数无效,bAlpha参数有效;
LWA_COLORKEY:窗体中的所有颜色为crKey的地方将变为透明,bAlpha参数无效。
LWA_ALPHA | LWA_COLORKEY:crKey的地方将变为全透明,而其它地方根据bAlpha参数确定透明度。
注:此函数在VC6.0中没有声明,需要自定义后在user32.dll后动态获取地址后调用。在VC9.0(VS2008)及其之后版本中可以直接调用。
7、下面贴出代码:
CoverDlg.cpp
// CoverDlg.cpp : implementation file
//
#include "stdafx.h"
#include "Custom_CutScreen.h"
#include "CoverDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/
// CCoverDlg dialog
CCoverDlg::CCoverDlg(CWnd* pParent /*=NULL*/)
: CDialog(CCoverDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CCoverDlg)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
}
void CCoverDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CCoverDlg)
// NOTE: the ClassWizard will add DDX and DDV calls here
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CCoverDlg, CDialog)
//{{AFX_MSG_MAP(CCoverDlg)
ON_WM_PAINT()
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
ON_WM_MOUSEMOVE()
ON_WM_TIMER()
ON_WM_RBUTTONUP()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/
// CCoverDlg message handlers
void CCoverDlg::OnPaint()
{
CPaintDC dc(this); // device context for painting
// TODO: Add your message handler code here
//绘制背景
CRect rect;
GetClientRect(&rect);
CBrush bruDB(GetSysColor(COLOR_3DFACE));//背景颜色
dc.FillRect(&rect, &bruDB);
//绘制拖动矩形
if (IsLBtnDown)
{
CPen pen(PS_SOLID,6,RGB(234,23,53));
CPen *pOldPen=dc.SelectObject(&pen);
CBrush *pBrush=CBrush::FromHandle((HBRUSH)
GetStockObject(NULL_BRUSH));
CBrush *pOldBrush=dc.SelectObject(pBrush);
dc.Rectangle(CRect(startPoint, endPoint));
dc.SelectObject(pOldPen);
dc.SelectObject(pOldBrush);
}
// Do not call CDialog::OnPaint() for painting messages
}
/************************************************************************/
/* 鼠标左键按下的响应函数 */
/************************************************************************/
void CCoverDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
startPoint = point;
endPoint = point;
IsLBtnDown = true;
CDialog::OnLButtonDown(nFlags, point);
}
/************************************************************************/
/* 鼠标左键弹起的响应函数 */
/************************************************************************/
void CCoverDlg::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
endPoint = point;
IsLBtnDown = false;
IsLBtnUp = true;
CDialog::OnLButtonUp(nFlags, point);
}
/************************************************************************/
/* 鼠标移动的响应函数 */
/************************************************************************/
void CCoverDlg::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
endPoint = point;
CDialog::OnMouseMove(nFlags, point);
}
/************************************************************************/
/* 计时器函数 */
/************************************************************************/
void CCoverDlg::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
if (nIDEvent == 1)
{
// 如果鼠标左键弹起,则关闭对话框
if (IsLBtnUp)
{
SendMessage(WM_CLOSE);
KillTimer(1);
}
// 如果鼠标右键弹起(用户取消了截图),关闭对话框
if (IsRBtnUp)
{
SendMessage(WM_CLOSE);
KillTimer(1);
}
Invalidate(FALSE);//更新界面
}
CDialog::OnTimer(nIDEvent);
}
/************************************************************************/
/* 对话框的初始化函数 */
/************************************************************************/
BOOL CCoverDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// TODO: Add extra initialization here
// 变量初始化
IsLBtnUp = false;
IsLBtnDown = false;
IsRBtnUp = false;
ShowWindow(SW_MAXIMIZE); // 窗口最大化
SetWindowPos(&wndTopMost,0,0,0,0, SWP_NOMOVE | SWP_NOSIZE); // 窗口置顶
// 设置窗体透明
COLORREF maskColor = GetSysColor(COLOR_3DFACE); // 获取窗体颜色
SetWindowLong(GetSafeHwnd(),GWL_EXSTYLE,GetWindowLong(GetSafeHwnd(),GWL_EXSTYLE)|0x00080000);
HINSTANCE hInst = LoadLibrary(_T("User32.dll"));
if (hInst)
{
typedef BOOL (WINAPI *MyFun)(HWND,COLORREF,BYTE,DWORD);
MyFun myfun = NULL;
myfun = (MyFun)GetProcAddress(hInst, "SetLayeredWindowAttributes");
if (myfun) myfun(GetSafeHwnd(),maskColor,100,2); // 100是透明度(范围0-255)
FreeLibrary(hInst);
}
// 设置计时器
SetTimer(1, 100, NULL);
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
/************************************************************************/
/* 鼠标右键弹起的响应函数 */
/************************************************************************/
void CCoverDlg::OnRButtonUp(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
IsRBtnUp = true;
CDialog::OnRButtonUp(nFlags, point);
}
POINT CCoverDlg::GetStartPoint()
{
return startPoint;
}
POINT CCoverDlg::GetEndPoint()
{
return endPoint;
}
bool CCoverDlg::GetWhetherCancel()
{
return IsRBtnUp;
}
Custom_CutScreenDlg.cpp
// Custom_CutScreenDlg.cpp : implementation file
//
#include "stdafx.h"
#include "Custom_CutScreen.h"
#include "Custom_CutScreenDlg.h"
#include "CoverDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/
// CCustom_CutScreenDlg dialog
CCustom_CutScreenDlg::CCustom_CutScreenDlg(CWnd* pParent /*=NULL*/)
: CDialog(CCustom_CutScreenDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CCustom_CutScreenDlg)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CCustom_CutScreenDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CCustom_CutScreenDlg)
// NOTE: the ClassWizard will add DDX and DDV calls here
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CCustom_CutScreenDlg, CDialog)
//{{AFX_MSG_MAP(CCustom_CutScreenDlg)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BTN_CUT, OnBtnCut)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/
// CCustom_CutScreenDlg message handlers
BOOL CCustom_CutScreenDlg::OnInitDialog()
{
CDialog::OnInitDialog();
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
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 CCustom_CutScreenDlg::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();
}
}
HCURSOR CCustom_CutScreenDlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}
void CCustom_CutScreenDlg::OnBtnCut()
{
// TODO: Add your control notification handler code here
CCoverDlg dlg;
if(IDCANCEL == dlg.DoModal())
{
if (!dlg.GetWhetherCancel())
{
POINT startPoint = dlg.GetStartPoint();
POINT endPoint = dlg.GetEndPoint();
CRect rect;
rect.left = startPoint.x;
rect.right = endPoint.x;
rect.bottom = endPoint.y;
rect.top = startPoint.y;
HBITMAP hb = CopyScreenToBitmap((LPRECT)&rect);
CString path = _T("C:\\test.bmp");
SaveBitmapToFile(hb, path);
}
}
}
/************************************************************************/
/* 将屏幕指定区域存成图片 */
/************************************************************************/
HBITMAP CCustom_CutScreenDlg::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("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;
}
/************************************************************************/
/* 将内容保存成文件 */
/************************************************************************/
BOOL CCustom_CutScreenDlg::SaveBitmapToFile(HBITMAP hBitmap, LPCSTR lpFileName)
{
HDC hDC; //设备描述表
int iBits; //当前显示分辨率下每个像素所占字节数
WORD wBitCount; //位图中每个像素所占字节数
DWORD dwPaletteSize=0, //定义调色板大小, 位图中像素字节大小 ,位图文件大小 , 写入文件字节数
dwBmBitsSize,
dwDIBSize, dwWritten;
BITMAP Bitmap; //位图属性结构
BITMAPFILEHEADER bmfHdr; //位图文件头结构
BITMAPINFOHEADER bi; //位图信息头结构
LPBITMAPINFOHEADER lpbi; //指向位图信息头结构
HANDLE fh, hDib, hPal,hOldPal=NULL; //定义文件,分配内存句柄,调色板句柄
//计算位图文件每个像素所占字节数
HDC hWndDC = CreateDC(_T("DISPLAY"),NULL,NULL,NULL);
hDC = ::CreateCompatibleDC( hWndDC ) ;
iBits = GetDeviceCaps(hDC, BITSPIXEL) * GetDeviceCaps(hDC, PLANES);
DeleteDC(hDC);
if (iBits <= 1)
wBitCount = 1;
else if (iBits <= 4)
wBitCount = 4;
else if (iBits <= 8)
wBitCount = 8;
else if (iBits <= 24)
wBitCount = 24;
else
wBitCount = 24 ;
//计算调色板大小
if (wBitCount <= 8)
dwPaletteSize = (1 << wBitCount) * sizeof(RGBQUAD);
//设置位图信息头结构
GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&Bitmap);
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = Bitmap.bmWidth;
bi.biHeight = Bitmap.bmHeight;
bi.biPlanes = 1;
bi.biBitCount = wBitCount;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
dwBmBitsSize = ((Bitmap.bmWidth * wBitCount+31)/32) * 4 * Bitmap.bmHeight ;
//为位图内容分配内存
hDib = GlobalAlloc(GHND,dwBmBitsSize+dwPaletteSize+sizeof(BITMAPINFOHEADER));
lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDib);
*lpbi = bi;
// 处理调色板
hPal = GetStockObject(DEFAULT_PALETTE);
if (hPal)
{
hDC = ::GetDC(NULL);
hOldPal = ::SelectPalette(hDC, (HPALETTE)hPal, FALSE);
RealizePalette(hDC);
}
// 获取该调色板下新的像素值
GetDIBits(hDC, hBitmap, 0, (UINT) Bitmap.bmHeight,
(LPSTR)lpbi + sizeof(BITMAPINFOHEADER)
+dwPaletteSize,
(LPBITMAPINFO )
lpbi, DIB_RGB_COLORS);
//恢复调色板
if (hOldPal)
{
SelectPalette(hDC, (HPALETTE)hOldPal, TRUE);
RealizePalette(hDC);
::ReleaseDC(NULL, hDC);
}
//创建位图文件
fh = CreateFile(lpFileName, GENERIC_WRITE,
0, NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if (fh == INVALID_HANDLE_VALUE)
return FALSE;
// 设置位图文件头
bmfHdr.bfType = 0x4D42; // "BM"
dwDIBSize = sizeof(BITMAPFILEHEADER)
+ sizeof(BITMAPINFOHEADER)
+ dwPaletteSize + dwBmBitsSize;
bmfHdr.bfSize = dwDIBSize;
bmfHdr.bfReserved1 = 0;
bmfHdr.bfReserved2 = 0;
bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER)
+ (DWORD)sizeof(BITMAPINFOHEADER)
+ dwPaletteSize;
// 写入位图文件头
WriteFile(fh, (LPSTR)&bmfHdr, sizeof(BITMAPFILEHEADER), &dwWritten, NULL);
// 写入位图文件其余内容
WriteFile(fh, (LPSTR)lpbi, dwDIBSize,
&dwWritten, NULL);
//清除
GlobalUnlock(hDib);
GlobalFree(hDib);
CloseHandle(fh);
return TRUE;
}
8、完整源代码(免积分):http://download.csdn.net/detail/wwkaven/7502599