MFC截图工具
1.新建MFC应用程序
(1)新建基于对话框的MFC应用程序
(2)加入按钮并命名
2.新建一个模态窗口
(1)在资源视图中,右击资源项目添加资源
(2)删除控件,更改属性
(3)更改窗口ID名称
(4)对此模态窗口添加类
3.主对话框窗口操作
(1)在主对话框窗口,即之前带按钮的窗口
在主窗口的头文件中加入
#include "Resource.h"
#include "CaptureDlg.h"
(2)回到主窗口界面双击截图按钮
(3)在按钮事件函数中加入
CCaptureDlg dlg;
dlg.DoModal();
(4)点击运行,再点击按钮,可以看到,弹出了一个模态对话框
4.再次对模态窗口操作
(1)CaptureDlg.h头文件操作
在CaptureDlg.h即模态窗口的头文件中输入以下代码
public:
virtual BOOL OnInitDialog();
CBitmap m_ScreenBmp;
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
CRectTracker m_rectTracker;
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnPaint();
afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message);
afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point);
(2)CaptureDlg.cpp文件操作
将下面除了包含头文件之外的所有代码复制到CaptureDlg.cpp文件中,注意CaptureDlg.cpp中包含的头文件不要改变,只将包含头文件意外的代码替换掉即可。
CaptureDlg.cpp
// CaptureDlg.cpp : 实现文件
//
#include "stdafx.h"
#include "screenCapture.h"
#include "CaptureDlg.h"
#include "afxdialogex.h"
BOOL copyScreenToBitmap(CBitmap &tBmp){ //为了修改值使用引用
//获取屏幕当前分辨率的宽度和高度(以像素为单位,传递参数不同X,Y)
int screenWidth = GetSystemMetrics(SM_CXSCREEN);
int screenHeight = GetSystemMetrics(SM_CYSCREEN);
//获取整个屏幕的DC
//(DC是windows系统描画各种东西的时候都要用到的东西,可以叫做设备描述表,
//你想要画的信息都要通过DC的对象去描画,任何窗口都有自己的DC,用自己的设备描述表进行描画,
//比如画图像、画文字,在画文字的时候指定文字的宽度、高度、字体等,都可以通过DC指定描画,在画
//任何东西时,都要先获取到当前窗口的设备描述表DC,用它去描画,才能在屏幕上显示出来)
//获取屏幕的DC,对屏幕截图,屏幕的东西就是屏幕的DC画上去的,要获取屏幕DC ,将内容拷贝到我们的DC中就可以截图了
CDC *pDC = CDC::FromHandle(::GetDC(NULL)); //::GetDC(NULL)表示或取整个屏幕DC,要获取某一个窗口的DC,它的参数就是某一窗口的窗口句柄
//::GetDC(NULL)返回的是HDC(win32的对象),而我们使用MFC,需要将HDC对象转换成CDC类型的对象,MFC已经将HDC封装了
//此时pDC指向的对象就是屏幕的DC
//屏幕DC有了,要将屏幕DC的内容拿过来拷贝到自己DC上来,然后才能进行内容保存,保存到图片或者剪切板,所以要建立自己的DC,在内存中建立与屏幕相容
//创建内存DC
CDC memDc;
memDc.CreateCompatibleDC(pDC); //创建一个与屏幕DC兼容的内存DC
//内存DC创建好后,还要在内存DC上画东西,但内存DC无任何画板,是画不上去的
//要建立Bitmap,让Bitmap作为内存DC的画板,把屏幕上的一些东西画到bitmap上面来,通过Bitmap将数据保存
//创建画板
CBitmap *pOldBmp = NULL;
tBmp.CreateCompatibleBitmap(pDC, screenWidth, screenHeight); //创建一个与屏幕DC兼容的一个Bitmap,画板的信息,参数1:DC,参数2、3:宽高
//画布建好了,要将Bitmap选入到内存DC中,这样在内存DC画东西是就会画到Bitmap上面
pOldBmp = memDc.SelectObject(&tBmp); //SelectObject返回原来的旧的画布,定义pOldBmp接收,还会用到
//将屏幕DC上的内容拷贝到内存DC上面来(BitBlt比较重要的函数)
memDc.BitBlt(0, 0, screenWidth, screenHeight, pDC, 0, 0, SRCCOPY);
//参数1,2:左上角坐标,参数3,4:宽高,参数5:源DC,参数6,7:从源DC的那个位置开始拷贝,参数8:直接拷贝源矩形到目标矩形
//善后操作
memDc.SelectObject(pOldBmp); //将pOldBmp选择到内存DC中
memDc.DeleteDC(); //还要对内存DC在用完了后清除(create与delete成对使用)
::ReleaseDC(NULL, pDC->m_hDC); //还要释放屏幕DC(getDC与releaseDC成对使用)
return TRUE;
}
// CCaptureDlg 对话框
IMPLEMENT_DYNAMIC(CCaptureDlg, CDialog)
CCaptureDlg::CCaptureDlg(CWnd* pParent /*=NULL*/)
: CDialog(CCaptureDlg::IDD, pParent)
{
copyScreenToBitmap(m_ScreenBmp); //将屏幕内容拷贝到Bitmap类型的对象中
}
CCaptureDlg::~CCaptureDlg()
{
}
void CCaptureDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CCaptureDlg, CDialog)
ON_WM_ERASEBKGND()
ON_WM_LBUTTONDOWN()
ON_WM_PAINT()
ON_WM_SETCURSOR()
ON_WM_LBUTTONDBLCLK()
END_MESSAGE_MAP()
// CCaptureDlg 消息处理程序
BOOL CCaptureDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// TODO: 在此添加额外的初始化
//获取屏幕当前分辨率的宽度和高度(以像素为单位,传递参数不同X,Y)
int screenWidth = GetSystemMetrics(SM_CXSCREEN);
int screenHeight = GetSystemMetrics(SM_CYSCREEN);
//调用MoveWindow或者SetWindowPos将当前的窗口设置成与屏幕大小相同
//使用两个,软件写好后要使用SetWindowPos,但使用SetWindowPos设置成顶层窗口就不能调试了,使用MoveWindow进行调试
MoveWindow(-3, -3, screenWidth + 6, screenHeight + 6); //比屏幕膜大3个像素不然白边出现,好看一些
//SetWindowPos(&wndTopMost, -3, -3, screenWidth + 6, screenHeight + 6, SWP_SHOWWINDOW);
//橡皮筋类的操作
m_rectTracker.m_nStyle = CRectTracker::resizeOutside | CRectTracker::dottedLine; //矩形框虚线
m_rectTracker.m_rect.SetRect(0, 0, 0, 0); //初始化矩形大小
return TRUE; // return TRUE unless you set the focus to a control
// 异常: OCX 属性页应返回 FALSE
}
BOOL CCaptureDlg::OnEraseBkgnd(CDC* pDC)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
//将bitmap对象作为背景画到对话框上
//创建内存DC
CDC memDC;
memDC.CreateCompatibleDC(pDC); //使内存DC与pDC兼容
memDC.SelectObject(&m_ScreenBmp); //选入设备环境
//将内容从内存DC拷贝到pDC中(本模态对话框窗口的DC)
CRect rect;
GetClientRect(&rect); //获取对话框大小,在初始化时设置了
pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &memDC, 0, 0, SRCCOPY); //将内容从内存DC拷贝到pDC中
memDC.DeleteDC(); //释放
return TRUE; //直接在此返回,不进行下一步操作,否则就画不上了
return CDialog::OnEraseBkgnd(pDC);
}
void CCaptureDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
//如果点击绘制橡皮筋区域的外部,重新构建一个可拖拽的区域
if (m_rectTracker.HitTest(point)==CRectTracker::hitNothing)
{
m_rectTracker.TrackRubberBand(this, point, TRUE);
}
else{
//点击在了区域内部,允许用户大小调整进行区域描画
m_rectTracker.Track(this, point, TRUE);
m_rectTracker.m_rect.NormalizeRect(); //NormalizeRect可以进行左右上下值调整,从右下向左上框柱
}
Invalidate(TRUE); //更新,使WM_PAINT描画消息触发
CDialog::OnLButtonDown(nFlags, point);
}
void CCaptureDlg::OnPaint()
{
CPaintDC dc(this); // device context for painting
// TODO: 在此处添加消息处理程序代码
// 不为绘图消息调用 CDialog::OnPaint()
//CPaintDC只适合OnPaint里面,所以使用GetDC来获取DC,进行描绘
CDC *pDC = GetDC();
m_rectTracker.Draw(pDC);
ReleaseDC(pDC); //get与release成对使用
}
BOOL CCaptureDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
//如果传递的是本窗口,SetCursor成功了
if (pWnd == this && m_rectTracker.SetCursor(this, nHitTest)){
return TRUE;
}
else{ //如果失败了
return CDialog::OnSetCursor(pWnd, nHitTest, message);
}
}
void CCaptureDlg::OnLButtonDblClk(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
//如果双击在了矩形区域的内部就进行保存工作
if (m_rectTracker.HitTest(point) != CRectTracker::hitMiddle)
{
return;
}
CDC *pDC = GetDC();
CDC memDC;
memDC.CreateCompatibleDC(pDC);
memDC.SelectObject(&m_ScreenBmp);
CRect rect;
rect = m_rectTracker.m_rect;
CBitmap mBmp, *pOldBmp = NULL;
mBmp.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
CDC dstDC;
dstDC.CreateCompatibleDC(pDC);
pOldBmp = dstDC.SelectObject(&mBmp);
//内容拷贝到目标,目标肯定比屏幕小,所以坐标0,0,宽高用户选择的,rect.left, rect.top是源缓冲区的坐标
dstDC.BitBlt(0, 0, rect.Width(), rect.Height(), &memDC, rect.left, rect.top, SRCCOPY);
CImage Img;
Img.Attach(mBmp); //关联
Img.Save(_T("./capture.jpg")); //保存地址
mBmp.DeleteObject();
memDC.DeleteDC();
dstDC.DeleteDC();
ReleaseDC(pDC);
//保存完了后,说明本次截图操作完成了,要把当前显示的模态对话框,全屏的对话框关闭了
CDialog::OnCancel();
//CDialog::OnLButtonDblClk(nFlags, point);
}
5.父窗口指针传递子窗口
参考链接:https://blog.csdn.net/yeqingbo2010/article/details/11724937
- 最近要将截取的图片显示到主窗口上,需要窗口间的数据传递。
- 数据在各个窗口之间的流动。如子窗口修改的数据,要保存到父窗口中进行保存。这就涉及到父窗口的指针传到子窗口的问题,在子窗口中,声明一个void * 型指针,然后再父窗口创建子窗口对象时,将父窗口的this传递给这个成员变量。当在子窗口中要使用父窗口指针时,可以用父窗口的类型强制转换为父窗口对象指针。非常好用。
- 为什么不能在子窗口中声明父窗口的指针变量呢?
那是因为子窗口时父类的一部分,如果父窗口指针又声明为子窗口的一部分,这样必然导致一个鸡生蛋,蛋生鸡的问题,编译不通过。
6.使用操作
最后,运行此程序,点击截图按钮即可进行截图操作
点击截图按钮后,左键按住鼠标拖拽,会出现一个由虚线构成的矩形窗口,此窗口可以进行拖拽、移动、更改边界大小,选择好区域后,在选择的区域内进行双击操作就可以保存截图了,默认保存在此项目的main函数所在文件夹下,名称为capture.jpg