MFC开发--截图工具
近期学习了MFC的相关知识,MFC(Microsoft Foundation Classes)是微软公司提供的一个类库,可以这样简单理解,就是对于Win32的封装(MFC对windows API函数的封装),但是MFC主要还是引入面向对象的开发思维,即一切用对象进行调用,我认为对巩固C++面向对象的思维有很大的帮助,所以进行了学习。并且开发了一个具有简单功能的截图工具,因为之前学习过一段时间的WIn32知识,所以在接下来的叙述中,主要以 对比Win32和C++的相关思想 为介入点进行介绍。
MFC介绍(有MFC基础的朋友,可以跳过这一段)
1.消息映射表
在WIn32中的消息响应是在消息循环中,通过Switch-case来实现的,其实在MFC中,消息循环这一部分用遍历数组来代替了,并且在各个类中加入了 DECLARE_MESSAGE_MAP(),BEGIN_MESSAGE_MAP(),END_MESSAGE_MAP(),这个数组里存放了各种消息对应的函数指针,而且将消息和消息处理函数构成了一个映射表,又叫消息映射表。这方面和博客中另一篇文章 动态创建数组有异曲同工之妙。
2.MFC框架
MFC的框架由系统先搭好的窗口和CWinApp,CFrameWnd,CView,CDocument这些类构成,而这些类层次,我总结如下图所示
3.MFC消息分类
这个和Win32中消息有所不同,不同于键盘,鼠标这些客观发送的消息类型,在MFC中分为标准/系统消息,命令消息,控件/通知消息,自定义消息,同样我也总结了分别在哪使用,和如何进行添加,如图:
在这需要注意的一点就是在自定义消息中,用户发送的两个函数有实现上的区别,SendMessage()是直接发送到目标类中,而PostMessage()是发送到消息队列中,由目标类自己去截获,消息少时没有影响,又称为同步和异步消息投放函数。
接下来就介绍一下,这个截屏工具的开发思想,首先获取桌面屏幕信息,再将桌面信息存入客户区内,没错就是这么简单,附加的功能就是可以画图和撤销
截图工具开发
1.修改MFC框架中窗口样式
这个因人而异,我是删除了状态栏,工具栏,边框(WS_POPUP)并且弹出就是全屏状态(值得注意的是在MFC中一个变量就表示了多种样式,微软的开发人员用位运算或来加各种属性,我相信日常开发人员在加属性时,这也是日常操作,所以在修改时,删去属性用异或,或者取反与也可以)
2.添加一系列控件,并且加入消息处理函数
这些都属于命令消息,需要注意的是在给关闭控件添加消息的时候,不能直接在关闭消息函数里就直接PostQuitMessage(),因为直接调用的话会出现内存泄漏,如下图:
因为这就是关闭消息的过程,WM_CLOSE->destroy->...->PostQuitMessage,由此可以看出最后一步才是直接发送退出消息,所以在点击退出的同时,给主窗口(CMainFrme类)发送一个关闭的消息
this->PostMessage(WM_CLOSE);
3.截屏
接下来就是截屏工具的主要功能,我分为三步,第一步,拿去桌面图片,第二步,保存,第三步,贴图至客户区
在这就要介绍hdc在MFC中的封装,hdc是一种包含有关某个设备(如显示器或打印机)的绘制属性信息的 Windows 数据结构。所有绘制调用都通过设备上下文对象进行,说白了就相当于画板,而在MFC中将GetDC,ReleaseDC这个函数都封装在CDC这些类的构造和析构中,除了不用手动调用创建和释放以外,还不用获取窗口句柄了,谁调用就是获取谁的,我也总结了一些常用的DC类,
CClientDC dc(this) | 视图客户区 |
CClientDC dc(AfxGetMainWnd()) | 获取主窗口 |
CWindowDC dc(AfxGetMainWnd()) | 窗口 |
CWindowDC dc(GetDesktopWindow()) | 桌面窗口 |
CMainFrame::CMainFrame()
{
//============获取桌面图片 保存=============== //--------------获取屏幕参数----------------
m_nScreenCX = ::GetSystemMetrics(SM_CXSCREEN);
m_nSCreenCY = ::GetSystemMetrics(SM_CYSCREEN);
//--------------获取屏幕参数---------------- //------------- 获取桌面图片----------------
CWindowDC DesktopDC(GetDesktopWindow());
CDC cdc;
cdc.CreateCompatibleDC(&DesktopDC); CBitmap *bitmap = new CBitmap;
bitmap->CreateCompatibleBitmap(&DesktopDC,m_nScreenCX,m_nSCreenCY);
cdc.SelectObject(bitmap);
cdc.BitBlt(,,m_nScreenCX,m_nSCreenCY,&DesktopDC,,,SRCCOPY);
//------------- 获取桌面图片---------------- //-----------------保存--------------------
sk.push(bitmap); //-----------------保存-------------------- //============获取桌面图片 保存===============
}
值得注意的是,我选择了栈作为保存方式,主要是因为我考虑到之后要写撤销的功能,而之后事实证明,利用栈这一数据结构来保存好处不止这一点,此时的效果如图
4.画图控件处理
我添加了 曲线,直线,长方形,圆形,三角形这些画图功能,在MFC中 ON_COMMAND_RANGE(起始ID,终止ID,处理函数地址)这个函数可以此命令可以处理相同操作的ID,因为无论什么形状,最终的功能就是画图,接下来就是如何判断点击的是何种控件,在这个地方用if去判断也可以,我利用资源中这个控件图片宏的连续性(不连续可以改连续),和枚举来进行判断,避免了大量的if判断
enum {PEN,LINE,RETANGLE,CIRCULAR,TANGLE}; void CCUTBITMAPView::OnChooseTool(UINT nID)
{
m_nDrawStyle = nID - ID_TOOL_PEN;
}
接下来就是画图功能,来分析一下,在选择后鼠标按下开始画,鼠标松开时结束画,所以添加了WM_LBUTTONDOWN,WM_LBUTTONUP,WM_MOUSEMOVE三个命令消息,并且建立一个标记来记录鼠标是否按下了,不然的话,打开后不用点击就会自动开始画,除了曲线以外都可以用MFC库函数来进行查询(简单练习不建议使用太复杂的多边形,因为到最后就是复杂的高中几何题呀),曲线的画法就像微积分中的微分思维一样,当一条曲线切割的足够短时,就是N多条直线组成,那么只要起始和结束坐标切换足够快的话就是曲线了。
5.bug处理
到这一步就可以在截屏的图案上进行绘画了,但是会发现之前画的痕迹并没有消除,有点3D的感觉,哈哈哈,如何处理这个问题呢,重复贴图,利用之前的栈顶元素来擦除痕迹,接下来痕迹是没有了,但是不停的在闪烁,常见问题--闪烁就用双缓冲来解决吧,不过MFC和WIn32这个思维转化的有点困难,对象调用的思维还得多熟悉熟悉,把代码贴上来,有MFC的初学者也可以看看
void CCUTBITMAPView::OnMouseMove(UINT nFlags, CPoint point)
{
CMainFrame *pFrame = (CMainFrame*)AfxGetMainWnd();
if(m_bIsLButtonDown == true)
{
CClientDC dc(this);
if(m_nDrawStyle == PEN)
{
dc.MoveTo(FirstPoint);
dc.LineTo(point);
FirstPoint = point;
return;
}
//------------双缓冲------------
CDC hMenDC;
hMenDC.CreateCompatibleDC(&dc);
CBitmap bitmap;
bitmap.CreateCompatibleBitmap(&dc,pFrame->m_nScreenCX,pFrame->m_nSCreenCY);
hMenDC.SelectObject(bitmap); //------------双缓冲------------ //-----------用栈顶元素 擦除轨迹------------
CDC cdc;
cdc.CreateCompatibleDC(&dc);
cdc.SelectObject(pFrame->sk.top());
hMenDC.BitBlt(,,pFrame->m_nScreenCX,pFrame->m_nSCreenCY,&cdc,,,SRCCOPY);
//-----------用栈顶元素 擦除轨迹------------
switch (m_nDrawStyle)
{
case LINE:
{
hMenDC.MoveTo(FirstPoint);
hMenDC.LineTo(point);
}
break;
case RETANGLE:
{
hMenDC.Rectangle(FirstPoint.x,FirstPoint.y,point.x,point.y);
}
break;
case CIRCULAR:
{
hMenDC.Ellipse(FirstPoint.x,FirstPoint.y,point.x,point.y);
}
break;
case TANGLE:
{
POINT tangle[] = {
{(FirstPoint.x+point.x)/,FirstPoint.y},
{point.x,point.y},
{FirstPoint.x,point.y}
};
hMenDC.Polygon(tangle,);
}
break;
}
dc.BitBlt(,,pFrame->m_nScreenCX,pFrame->m_nSCreenCY,&hMenDC,,,SRCCOPY);
} CView::OnMouseMove(nFlags, point);
}
但是每一次画的,第二次点击,都会覆盖掉,解决这个,就是和当时截取桌面画面一样,只不过这次在鼠标抬起时,截取客户区,存入栈中,同样每次拿栈顶消除痕迹就会留下痕迹了,代码如下
void CCUTBITMAPView::OnLButtonUp(UINT nFlags, CPoint point)
{
m_bIsLButtonDown = false;
//----------------------留下之前的痕迹-----------------------
CMainFrame *pFrame = (CMainFrame*)AfxGetMainWnd(); CClientDC dc(this); CDC cdc;
cdc.CreateCompatibleDC(&dc);
CBitmap *pBitMap = new CBitmap;
pBitMap->CreateCompatibleBitmap(&dc,pFrame->m_nScreenCX,pFrame->m_nSCreenCY);
cdc.SelectObject(pBitMap);
cdc.BitBlt(,,pFrame->m_nScreenCX,pFrame->m_nSCreenCY,&dc,,,SRCCOPY); pFrame->sk.push(pBitMap);
//----------------------留下之前的痕迹-----------------------
CView::OnLButtonUp(nFlags, point);
}
6.撤销功能
Ctrl+Z首先先添加到键盘映射表中
主要思路就是不停的将栈顶元素弹出栈中,就可以了,代码如下
void CMainFrame::OnAccelerator32778()
{
//撤销的函数
if(sk.size() > )
{
delete sk.top();
sk.pop();
GetActiveView()->SendMessage(WM_PAINT);
}
}
接下来,还有保存等其他的功能,未完待续。。。,先放一张实现的截图
最后,记录一个我觉得挺重要MFC的消息流向(因为知道,什么消息写在哪个类中)
标准消息 | WM_XXX | 子类到父类,查找消息映射表 |
命令消息 | WM_COMMAND | 消息路由 CView-->CDoc-->CDocTemplate-->CFrameWnd-->CWinApp |
控件/通知消息 | WM_NOTIFY | 子窗口到父窗口,查找消息映射表 |
自定义消息 | UM_XXX | 确定了,查找调用者那个类 |
2019-07-21 19:17:07 编程小菜鸟自我总结,来往的朋友可以留下自己的建议和意见,谢谢!!!
MFC开发--截图工具的更多相关文章
- MFC 之 截图工具
这个截图工具能实现最主要的截图功能,并保存为bmp图片. 编写环境是vs2005,使用Unicode,基于对话框. 没什么难度,直接看代码 项目名称为CutOut // CutOutDlg.h : 头 ...
- [C# 开发技巧]实现属于自己的截图工具
[C# 开发技巧]实现属于自己的截图工具 一.引言 之前一直都是写一些C#基础知识的内容的,然而有些初学者可能看完了这些基础知识之后,会有这样一个疑惑的——我了解了这些基础知识之后,我想做一些工具怎么 ...
- 人生第一次研读MFC截图工具的笔记心得
截图工具: 其中用到了动态链接库DLL技术(Dynamic Link Library)技术,键盘钩子技术,光标捕获技术,类橡皮类CRectTracker 头文件:后缀名为.cpp,主要是定义和声明之类 ...
- 使用MFC开发有十多年了,结合自身的体会,随便说几句(不能样样都依赖别人,C体系的人,绝对不怕人踢馆)
挺长时间了吧,这个帖子还没沉下去,使用MFC开发有十多年了,结合自身的体会,随便说几句:1.MFC是一个C++的基础类库,封装了绝大多数的API函数,主要是用来创建带UI的应用程序,服务端程序或着不带 ...
- IE8"开发人员工具"使用详解下(浏览器模式、文本模式、JavaScript调试、探查器)
来源: http://www.cnblogs.com/JustinYoung/archive/2009/04/03/kaifarenyuangongju2.html 在上一篇文章IE8“开发人员工具” ...
- Web前端开发必备工具推荐
http://gaohaixian.blog.163.com/blog/static/12326010520114265223489/不管你做前端开发还是网页重构,前端工具都起着非常重要的作用,这里向 ...
- 利器推荐-Snipaste截图工具
利器推荐-Snipaste截图工具 一.引言 接触这个工具之前一直用QQ的ctrl+alt功能进行截图,但是有时候QQ没有登陆,或者没网的环境就没法使用:这时候可能会使用windows自带的截图工具, ...
- Mac开发必备工具(二)—— iTerm 2
iTerm 2 简介 iTerm 2 is a terminal emulator for Mac OS X that does amazing things. iTerm 2 有很多能够提升效率的实 ...
- Mac开发必备工具(一)—— Homebrew
Homebrew 简介 macOS 缺失的软件包管理器.使用 Homebrew 安装 Apple 没有预装但 你需要的东西.官网有中文说明. 安装与配置 Homebrew 的安装非常简单,将下面这条命 ...
随机推荐
- Oracle 宣布 Java 7 生命周期终结
快速使用Romanysoft LAB的技术实现 HTML 开发Mac OS App,并销售到苹果应用商店中. <HTML开发Mac OS App 视频教程> 土豆网同步更新:http: ...
- QML被系统缓存的原理是比较时间戳
Gunnar Roth January 25, 2017 at 17:07 Afaik the cached qml file contains a checksum of the original ...
- JavaWeb实现上传文件
需要 commons-io与commons-fileupload 首先在jsp中创建一下布局 <%@ page contentType="text/html;charset=UTF-8 ...
- DLL中类的显式链接(用虚函数进行显式链接)
DLL的显式链接在某些时候比隐式链接具有更大的灵活性.比如,如果在运行时发现DLL无法找到,程序可以显示一个错误信息并能继续运行.当你想为你的程序提供插件服务时,显式链接也很有用处. 显式链接到全局C ...
- 如何保证MQ消息必达
此文章属于笔记,原属58沈剑 一.MQ消息必达,架构上的两个核心设计点: 消息落地 消息超时.重传.确认 四大部件:发送端 接收端 服务端 固化存储组成 二.上半场消息必达以及消息重复问题 上半场的流 ...
- Python连载11-Python中os.path模块简介
一.os.path(和路径相关的木块) 1.函数:abspath() (1)含义:将路径转化为绝对路径的形式(absolute path) (2)格式:os.path.abspath(相对路径) (3 ...
- WebP 大战 JPEG,谁才是真正的王者?
目前在互联网上,图片流量仍占据较大的一部分.因此,在保证图片质量不变的情况下,节省流量带宽是大家一直需要去解决的问题.传统的图片格式,如 JPEG,PNG,GIF 等格式图片已经没有太多的优化空间.因 ...
- 解码mmo游戏服务器三:大地图同步(aoi)
问题引入:aoi(area of interest).在大地图中,玩家只需要关心自己周围的对象变化,而不需要关心距离较远的对象的变化.所以大地图中的数据不需要全部广播,只要同步玩家自己视野范围的消息即 ...
- 【转载】Chrome使用自定义协议打开本地程序并运行IE打开网页
部分内容转载自: http://blog.sina.com.cn/s/blog_e2b8213a0102wqby.html 项目中遇到某需求:chorme要运行IE并打开网页.解决方案之一就是通过自定 ...
- 微信商城小程序 带java后台源码
微信小程序商城(Java版) 技术选型 1 后端使用技术 1.1 spring-web-4.0.2.RELEASE 1.2 mybatis3.2.8 1.3 shiro1.2.3 1.4 servle ...