Windows-消息映射机制原理和简单的绘图技术
1.MFC消息映射机制
众所周知,Windows程序是基于消息编程的,但是在MFC中已经为我们封装好了这个框架的消息机制,我们需要了解它的实现原理,才能深入的学习和精通Visual C++。
**(1).消息映射机制的原理:
MFC消息映射机制的具体实现方法是,在每个能接收和处理消息的类中,定义一个消息和消息函数静态对照表,即消息映射表。在消息映射表中,消息与对应的消息处理函数指针是成对出现的。某个类能处理的所有消息及其对应的消息处理函数的地址都列在这个类所对应的静态表中。当有消息需要处理的时候,程序只要搜索该消息静态表,查看表中是否含有该消息,就可知道该类能否处理此消息。如果能处理该消息,则同样依照静态表能很容易找到并调用对应的消息处理函数。**
(2).在Visual C++中增加一个消息所需要的三处代码
头文件中第一处:
这里在头文件中定义了该类能够处理的消息
源文件中第二处:
源文件的开头定义该类的消息映射表
源文件中第三处:
这里源文件中消息的具体逻辑代码实现
(3).消息映射机制的底层实现过程
MFC在后台维护了一个窗口句柄与对应的C++对象指针的对照表,当窗口收到某个消息时,消息的第一个参数就指定了该消息和哪个窗口句柄相关,通过对照表,就可以找到与之相关的C++对象指针,然后把这个指针传递给应用程序框架窗口类的基类,后者会调用一个名为WindowProc的窗口过程函数,该函数的定义位于WinCore.cpp文件中,这个函数又会调用OnWndMsg函数在完成真正的消息路由,消息映射就是由此函数完成的。
WindProc
OnWndMsg代码过长,这里给出声明和文件路径,大家可以自己去分析,这里面的代码和用平台SDK编写一个窗口的回调函数是差不多的,就是对消息的判断和处理。
路径:C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\atlmfc\src\mfc\wincore.cpp
图解底层实现机制:
2.MFC绘制线条的几种方法
(1).利用Platform SDK全局函数绘制
//平台的SDK,HDC是设备上下文
HDC hdc = ::GetDC(m_hWnd);//获取DC
MoveToEx(hdc, m_ptOrigin.x, m_ptOrigin.y, nullptr);
LineTo(hdc, point.x, point.y);
::ReleaseDC(m_hWnd, hdc);//记得释放DC
(2).利用MFC的CDC类绘制
CDC类是MFC中一个专门用于绘图的类
//需要手动管理CDC对象的创建和释放
CDC* pDC = GetDC();//得到DC
pDC->MoveTo(m_ptOrigin);
pDC->LineTo(point);//画线的API
ReleaseDC(pDC);//释放DC
(3).利用CClientDC
继承CDC,可以自动获取和释放DC
//自动创建和释放CDC对象
CClientDC dc(this);//画到视图窗口上
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
画在主窗口上
//自动创建和释放CDC对象
CClientDC dc(GetParent());//画到主窗口上
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
(4).利用CWindowDC
//Windows屏幕的DC,画出的线条支持直接出现在屏幕上
//CWindowDC dc(this);//画在视图上
//CWindowDC dc(GetParent());//画在主框架窗口上
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
(5).在桌面窗口中画线
CWindowDC dc(GetDesktopWindow());//直接画在屏幕上
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
(6).绘制彩色线条
CPen pen(PS_DOT, 1, RGB(255, 1, 1));//画红色的点线
CClientDC dc(this);
CPen* pOldPen = dc.SelectObject(&pen);//保存先前的画笔,必要的一步
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
dc.SelectObject(pOldPen);//恢复先前的画笔
(7).利用画刷CBrush绘图
//画出图片bitmap
CBitmap bitmap;
//加载位图资源
bitmap.LoadBitmap(IDR_MAINFRAME);//位图的资源ID
CBrush brush(&bitmap);
CClientDC dc(this);
//填充矩形
dc.FillRect(CRect(m_ptOrigin, point), &brush);
(8).绘制连续的线条
CClientDC dc(this);
if (m_bDraw)//m_bDraw是鼠标左键是否按下的BOOL成员变量,这里必须为TRUE
{
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
m_ptOrigin = point;//这次的终点作为下一次绘制线段的起点
}
(9).绘制连续的带颜色的线条
CClientDC dc(this);
//改变画笔的颜色
CPen pen(PS_SOLID, 1, RGB(255, 0, 0));
CPen* pOldPen = dc.SelectObject(&pen); //将画笔选入设备描述表
if (m_bDraw)//m_bDraw是鼠标左键是否按下的BOOL成员变量,这里必须为TRUE
{
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
m_ptOrigin = point;
}
dc.SelectObject(pOldPen);//恢复之前的画笔
(10).画扇形
CClientDC dc(this);
CPen pen(PS_SOLID, 1, RGB(255, 0, 0));
CPen* pOldPen = dc.SelectObject(&pen);
if (m_bDraw)
{
dc.MoveTo(m_ptOrigin);//起点不变,画出来的是扇形
dc.LineTo(point);
}
dc.SelectObject(pOldPen);
(11).画出带边界的扇形
CClientDC dc(this);
CPen pen(PS_SOLID, 1, RGB(255, 0, 0));
CPen* pOldPen = dc.SelectObject(&pen);
if (m_bDraw)
{
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
dc.LineTo(m_ptOld);//这次的线段终点画到上一条线段的终点就可以形成边界
m_ptOld = point;//保存上一条线段的终点
}
dc.SelectObject(pOldPen);
(12).画矩形
//画出矩形并且使用画刷来填充颜色
CClientDC dc(this);
CBrush brush(RGB(255, 0, 0));
dc.FillRect(CRect(m_ptOrigin, point), &brush);
画透明的矩形
CClientDC dc(this);
//创建透明的画刷,GetStockObject返回的是HGDIOBJ,需要强制转换成HBRUSH,而且还需要将句柄转换成对象指针,要使用FromHandle
//这个静态函数
CBrush* pBrush = CBrush::FromHandle(static_cast<HBRUSH>(GetStockObject(NULL_BRUSH)));
//保存旧的画刷
CBrush* pOldBrush = dc.SelectObject(pBrush);
//画出矩形
dc.Rectangle(CRect(m_ptOrigin, point));
//恢复旧的画刷
dc.SelectObject(pOldBrush);
(13).设置绘图模式
CClientDC dc(this);
dc.SetROP2(R2_BLACK);//设置绘图的模式, 查看MSDN的详细介绍
3.MFCBuild
Build命令可以直接用F7来运行
4.总结
在使用MFC的时候,我们需要了解它的底层实现机制,这样我们在遇到问题的时候就知道是哪个环节出了问题,这是我们必经之路,另外在绘图的时候,需要清除我们画的的父视图是哪个。
下面是我总结的思维导图,方便大家形成良好的思维,一种有两个,是不同的时间做的。
(1).
(2).
图片的资源分享:https://yunpan.cn/cPczaZE4HU5c4 访问密码 f09a