MFC 学习笔记
MFC 学习笔记
MFC的概念和作用
什么是MFC?
全称是Microsoft Foundation Class Library,我们称作微软基础类库
它封装了windows应用程序的各种API以及相关机制的C++类库
总结:
MFC是一个大的类库
MFC是一个应用程序框架
为什么需要MFC?
基于应用程序框架之上,使用提供的类库快速开发,提高项目的开发效率,减少开发周期和成本
常用的头文件
MFC类库常用的头文件
afx.h:将各种MFC头文件包含在内
afxwin.h:包含了各种MFC窗口类,包含了afx.h和windows.h
afxext.h:提供了扩展窗口类的支持,例如工具栏、状态栏等
...
MFC程序的分类
MFC控制台程序
和win32的控制台程序的差别
main函数不同于普通的控制台程序
CWinApp theAPP; 多了一个全局对象
经验之谈
以Afx开头可以确定为MFC库中的全局函数
以::开头可以确定为win32的API函数
MFC的库程序
使用MFC制作自己的静态库程序
MFC的动态库
使用静态的NFC库制作自己的动态库
使用动态的MFC库制作自己的动态库
MFC扩展库
规则库可以被各种程序所调用,扩展库只能被MFC程序调用
相关函数
单文档视图架构程序
CWinApp:应用程序类,负责管理应用程序的流程
CFrameWnd:框架窗口类,负责管理框架窗口
CView:视图窗口类,负责显示数据
CDocument:文档类,负责管理数据
多文档视图架构程序
CWinApp:应用程序类
CMDIFrameWnd:多文档主框架窗口类
CMDIChildWnd:多文档子框架窗口类
CView:视图窗口类,显示数据
CDocument:文档类,管理数据
对话框应用程序
CWinApp:应用程序类
CDialog:对话框窗口类
MFC库中类的简介
Object类
MFC类库中绝大部分类的父类,提供了MFC类库中一些基本的机制
对运行时类信息的支持
对动态创建的支持
对序列化的支持
CWinApp类:应用程序类,封装了应用程序、线程等信息
CDocument类:文档类,管理数据
Frame Windows:框架窗口类,封装了窗口程序组成的各种框架窗口
CSplitterWnd:用来完成拆分窗口的类
Control Bars:控制条类
DIalog Boxes:对话框类,封装了各种对话框,通用的对话框
Views:视图类,封装了各种显示窗口
Controls:控件类,封装了各种常用的控件
Exceptions:异常处理类,封装了MFC中常用的各种异常
绘图类,包括CDC类和CGdiObject类
数据集合类,CArray/CList/CMap,封装了相应的数据结构管理
非CObject类的子类
提供了各种数据结构相关的管理,CPoint、CTime、CString等
第一个MFC程序
设置开发环境
头文件为
setting当中设置使用MFC库
总结:win32程序和MFC程序区别仅仅为能不能使用MFC而已
代码书写
定义自己的框架类CMyFrameWmd,派生自CFrameWnd类
class CMyFrameWnd : public CMyFrameWnd
{
};
定义自己的应用程序类CMyWinApp,派生自CWinApp类,并定义构造以及重写InitInstance虚函数,在函数中创建并显示窗口
class CMyWinApp : public CWinApp
{
public:
CMyWinApp()
{
}
virtual BOOL InitInstance()
{
CMyFrameWnd* pFrame = new CMyFrameWnd;
pFrame->Create(NULL, "MFCBase");
m_pMainWnd = pFrame;
pFrame->ShowWindow(SW_SHOW);
pFrame->UpdateWindow();
return TRUE;
}
};
定义CMyWinApp类的对象(程序的爆破点)
CMyWinApp theApp;
MFC程序启动
入口函数
与Win32窗口程序相同,都是从WinMain入口。但是MFC库已经实现了WinMain函数,所以在程序中不需要实现。
总结:在Win32程序中WinMain由程序员自己实现,那么流程是程序员安排,但到了MFC中,由于MFC库实现WinMain,也就意味着MFC负责安排程序的流程。
执行流程
程序的启动,构造theApp对象,调用父类CWinApp的构造函数
将theApp对象的地址保存到线程状态信息中
将theApp对象的地址保存到模块状态信息中
进入WinMain函数,调用AfxWinMain函数
进入入口函数WinMain
获取应用程序类对象theApp的地址
利用theApp地址调用InitApplication,初始化当前应用程序的数据
利用theApp地址调用InitInstance函数初始化程序,在函数中我们创建窗口并显示
利用theAPp地址调用CWinApp的Run函数进行消息循环
如果没有消息,利用theApp地址调用OnIdle虚函数实现空闲处理
程序退出利用theApp地址调用ExitInstance虚函数实现退出前的善后处理工作
CWinApp的成员
成员虚函数
InitInstance:程序的初始化函数,完成了窗口创建等初始化处理
ExitInstacne:程序退出时调用,清理资源等善后工作
Run:消息循环
OnIdle:空闲处理函数
成员变量
m_pWainWnd:当前应用程序的主窗口
MFC窗口和消息
钩子函数
创建钩子
HHOOK SetWindowHookEx(
int idHook, // 钩子类型(WH_CBT)
HOOKPROC lpfn, // 钩子处理函数
HINSTANCE hMod, // 应用程序实例句柄
DWORD dwThreadId // 线程ID
);
钩子处理函数
LRESULT CALLBACK CBTProc(
int nCode, // 钩子码(HCBT_CREATEWND)
WPARAM wParam, // 刚刚创建成功的窗口句柄
LPARAM lParam
);
更改窗口处理函数
LONG_PTR SetWindowLongPtr(
HWND hWnd, // 窗口句柄
int nIndex, // GWLP_WNDPROC
LONG_PTR dwNewLong // 新的窗口处理函数名(函数地址)
);
代码书写
定义自己的框架类(CMyFrameWnd),派生自CFrameWnd
定义自己的应用程序类(CMyWinApp),并重写父类成员虚函数InitInstance
定义全局对象CmyWinApp theApp;(爆破点)
MFC窗口创建过程
加载菜单
调用CWnd::CreateEx函数创建窗口
调用PreCreateWindow函数设计和注册窗口类调用AfxDeferRegisterClass函数,在这个函数中设计窗口类:
WNDCLASS wndcls; // 设计窗口类
...
// 定义窗口的处理函数为DefWindowProc
wndcls.lpfnWndProc = DefWindowProc;
调用_AfxRegisterWithIcon函数
在函数内部,加载图标,并调用AfxRegisterClass函数,在函数内部,调用::RegisterClass win32API函数注册窗口
消息映射机制的作用
在不重写WindowProc虚函数的大前提下,仍然可以处理消息
消息映射机制的使用
类必须具备的要件
类内必须添加声明宏:DECLARE_MESSAGE_MAP()
类外必须添加实现宏:BEGIN_MESSAGE_MAP(theClass, baseClass)、END_MESSAGE_MAP()
总结
当一个类具备上述两个要件,这个类就可以按照消息映射机制来处理消息
消息映射机制的具体实施
以WM_CREATE消息为例:
BEGIN_MESSAGE_MAP()和END_MESSAGE_MAP()之间添加ON_MESSAGE(WM_CREATE, OnCreate)宏
在CMyFrameWnd类内添加OnCreate函数的声明和定义
数据结构
结构体
struct AFX_MSGMAP_ENTRY { // 静态数组每个元素的类型
UINT nMessage; // 消息ID
UINT nCode; // 通知码
UINT nID; // 命令ID
UINT nLastID; // 最后一个命令ID
UINT_PTR nSig; // 处理消息的函数类型
AFX_PMSG pfn; // 处理消息的函数名(地址)
};
宏展开各部分的作用
GetThisMessageMap() 讲台函数
作用:定义静态变量和静态数组,并返回本类静态变量地址(获取链表头)
_messageEntries[] 静态数组(进程及生命周期)
作用:数组每个元素,保存为消息ID和处理消息的函数名(地址)
messageMap 静态变量(进程及生命周期)
作用:第一个成员,保存父类宏展开的静态变量地址
第二个成员,保存本类的静态数组首地址
GetMessageMap() 虚函数
作用:返回本类静态变量首地址(获取链表头)
创建窗口的原理
消息产生进入窗口处理函数(AfxWndProc)
根据已知窗口句柄,找到和它绑定在一起的框架类对象地址(pFrame)
利用框架类对象地址(pFrame)调用框架类成员虚函数WindowProc
获取本类对应的静态变量,并到对应数组中匹配查找
如果没有找到,获取父类对应的静态变量,并到对应数组中匹配查找
如果找到了,利用找到的数组元素的最后一个成员,并调用之,完成消息处理
消息的分类
标注Windows消息
ON_WM_XXX,如ON_WM_CREATE等
自定义消息
ON_MESSAGE
命令消息
ON_COMMAND
MFC菜单
菜单相关问题
Win32:HMENU
MFC:CMenu类对象
CMenu类简介
封装了关于菜单的各种操作成员函数,另外还封装了一个非常重要的成员变量m_hMenu(菜单句柄)
菜单的使用
添加菜单资源
将菜单设置到窗口
利用pFrame调用Create函数时,传参
在处理框架窗口的WM_CREATE消息时
CMenu menu;
menu.LoadMenu(...);
消息的处理
ON_COMMAND(菜单项ID, 处理消息是函数名)
命令消息的处理顺序:框架类 -> 应用程序类
设置菜单项状态
Win32
WM_INITMENUPOPUP
::CheckMenuItem / ::EnableMenuItem
MFC
ON_WM_INITMENUPOPUP
CMenu::CheckMenuItem / CMenu::EnableMenuIten
右键菜单
Win32
WM_CONTEXTMENU
::TrackPopupMenu
MFC
ON_WN_CONTEXTMENU
CMenu::TrackPopupMenu
工具栏
相关类
CToolBarCtrl:父类CWnd,封装了关于工具栏控件的各种操作
CToolBar:父类CControlBar,封装了关于工具栏的操作,以及和框架窗口的关系
工具栏的使用
添加工具栏资源
创建工具栏:CToolBar::CreateEx
加载工具栏:CToolBar::LoadToolBar
设置工具栏的停靠
CToolBar::EnableDocking
CFrameWnd::EnableDocking
CFrameWnd::DockControlBar
运行时类信息机制
运行时类信息机制的作用
在程序运行过程中可以获知对象的类的相关信息(例如:对象是否属于某个类)
运行时类信息机制的使用
类必须派生自CObject
类内必须添加声明宏 DECLARE_DYNAMIC(theClass)
类外必须添加实现宏 IMPLEMENT_DYNAMIC(theClass, baseClass)
当一个类具备上述三个要件后,CObject::IsKingOf函数就可以正确判断对象是否属于某个类
动态创建机制
动态创建机制的作用
在不知道类名的情况下,将类的对象创建出来
动态创建机制的使用
类必须派生自CObject
类内必须添加声明宏:DECLARE_DYNCREATE(theClass)
类外必须添加实现宏:IMPLEMENT_DYNCREATE(theClass, baseClass)
当一个类具备上述三个要件后,CRuntimeClass::CreateObject(对象加工厂)函数就可以将类的对象创建出来
与运行时类信息的区别
多了一个静态函数
CObject* PASCAL CDog::CreateObject()
{
return new CDog; // new了一个CDog类对象,并返回对象地址
}
静态变量的第四个成员不再为NULL,而为新增加的这个静态函数的地址
执行过程
利用本类(CDog)的静态变量,调用CRuntimeClass的成员函数CreateObject(对象加工厂函数)
获取静态变量的第四个成员(新增加静态函数),并调用之
新增加的静态函数内部,完成类对象的创建,并返回对象地址
视图窗口
视图窗口的作用
提供一个用于显示数据的窗口
视图窗口的相关类
CView及其子类,父类为CWnd类,封装了关于视图窗口的各种操作,以及和文档类的数据交互
视图窗口的使用
定义一个自己的视图类(CMyView),派生自CView,并重写父类成员纯虚函数OnDraw(可以用于绘图)
其余框架类和应用程序类代码不变
在处理框架窗口的WM_CREATE消息时,定义CMyView类对象,并调用Create函数创建视图窗口
视图窗口的ID为AFX_IDW_PANE_FIRST
命令消息处理顺序
视图类 -> 框架类 -> 应用程序类
对象关系图
m_pMainWnd = pFrame;
m_pViewActive = pView;
theApp
m_pMainWnd(pFrame框架类对象地址)
m_pViewActive(pView视图类对象地址)
文档类
相关类
CDocument
提供了一个用于管理数据的类,封装了关于数据的管理(数据获取、数据转换、数据存储等),并和视图类进行数据交互
文档类的使用
定义一个自己的文档类(CMyDoc),派生自CDocument
对象关系图
theApp
m_pMainWnd(框架类对象地址pFrame)
m_pViewActive(视图类对象地址pView)
m_pDocument(文档类对象地址pDoc)
m_viewList(所有视图类对象地址)
文档类对象用一个链表成员变量,保存视图类对象地址
视图类用一个普通成员变量,保存文档类对象地址
经过分析可知:一个文档类对象可以对应多个视图类对象(视图窗口),而一个视图类对象(视图窗口)只能对应一个文档类对象
窗口切分
相关类
CSplitterWnd:不规则框架窗口类,封装了关于不规则框架窗口的操作
窗口切分的使用
重写CFrameWnd类的成员虚函数OnCreateClient
在虚函数中调用CSplitterWnd::CreateStatic创建不规则框架窗口
在虚函数中调用CSplitterWnd::CreateView创建视图窗口
文档类和视图类的关系
视图类成员函数
获取和视图类成员对象关联的文档类对象,调用GetDocument()
文档类成员对象
当文档类数据发生变化时,调用UpdateAllViews刷新和文档类对象相关联的视图类对象(视图窗口)
单文档视图架构
单文档视图架构特点
只能管理一个文档(只有一个文档类对象)
单文档视图架构使用
参与架构的类
CFrameWnd/CWinApp/CView/CDocument
需要用到的类
CDocTemplate(文档模板类)
CSingleDocTemplate(单模板文档类)
CDocManager(文档管理类)
参与架构的四个类除了应用程序类外,其余三个类均要支持动态创建机制
执行过程
theApp
m_pDocManager(文档管理类对象地址)
m_template(单文档模板对象地址)
CSingleDocTemplate *pTemplate
m_pOnlyDoc // 唯一的文档类对象地址
m_pDocClass // RUNTIME_CLASS(CMyDoc)
m_pFrameClass // RUNTIME_CLASS(CMyFrameWnd)
m_pViewClass // RUNTIME_CLASS(CMyView)
多文档视图架构
多文档视图架构特点
可以管理多个文档(可以有多个文档类对象)
多文档视图架构使用
参与架构的类
CMDIFrameWnd/CMDIChildWnd/CWinApp/CView/CDocument
需要用到的类
CDocTemplate(文档模板类)
CMultiDocTemplate(多文档模板类)
CDocManager(文档管理类)
MFC绘图
绘图相关类
CDC类(绘图设备类)
封装了各种绘图相关的函数,以及两个非常重要的成员变量m_hDC和m_hAttribDC
CPaintDC类,封装了在WM_PAINT消息中绘图的绘图设备
CCLientDC类,封装了在客户区绘图的绘图设备
CGdiObject类(绘图对象类)
封装了各种绘图对象相关的操作,以及一个非常重要的成员变量m_hObject(绘图对象句柄)
CPen类,封装了画笔的操作
CBrush类,封装了画刷的操作
CFont类,封装了字体的操作
CBItmap类,封装了位图的操作
序列化机制
文件操作相关类
CFIle:文件操作类,封装了关于文件读写等操作
CFile::Open
CFIle::Write/Read
CFile::Close
CFile::SeekToBegin/SeekToEnd/Seek
序列化机制的作用
以二进制流形式读写磁盘文件。但效率很高
序列化机制相关类
CFIle:文件操作类,完成磁盘文件的读写操作
CArchive:归档类,完成内存数据的读写操作
序列化机制的使用
创建或打开文件——CFile::Open
定义归档类对象——CArchive ar;
数据序列化(存储/写)——ar << 数据
关闭归档类对象——ar.Close();
关闭文件——CFile::Close();
反序列化机制的使用
打开文件——CFile::Open
定义归档类对象——CArchive ar;
数据反序列化(加载/读)——ar >> 变量
关闭归档类对象——ar.Close();
关闭文件——CFile::Close();
数据结构
class CArchive {
enum Mode { store = 0, load = 1 ...};
BOOL m_nMode; // 访问方式
int m_nBufSize; // buff的大小
CFile* m_pFile; // 操作的文件对象
BYTE* m_lpBufCur; // 当前指向
BYTE* m_lpBufMax; // 终止指向
BYTE* m_lpBufStart; // 开始指向
...
}
序列化执行过程
ar对象维护一个缓冲区
将各个数据依次序列化(存储)到ar对象维护的缓冲区中,并将m_lpBufCur的指针指向移动相应字节
如果ar维护的缓冲区不足,则将ar维护的缓冲区的数据写入硬盘文件,并重置m_lpBufCur为开始指向
当关闭ar对象时,将ar对象维护的缓冲区数据写入硬盘文件,并释放ar对象维护的缓冲区
反序列化执行过程
ar对象维护一个缓冲区
当反序列化第一个数据时,将文件数据全部读取到ar维护的缓冲区中,并将第一个数据反序列化到第一个变量,并将m_lpBufCur移动相应的字节数
依次反序列化每个数据到变量中
当关闭ar对象时,释放ar维护的缓冲区
序列化类对象
序列化类对象的使用
类必须派生自CObject
类内必须添加声明宏DECLARE_SERIAL(theClass)
类外必须添加实现宏IMPLEMENT_SERIAL(theClass, baseClass, 1)
类内必须重写虚函数Serialize
当类具备以上三个要件后,类对象就可以序列化到文件中保存了
对话框架构
对话框架构特点
对话框分类
模式对话框/无模式对话框
无模式对话框
对话框架构使用
参与架构的类
CDialog/CWinApp
代码书写
添加对话框资源
定义一个自己的对话框类(CMyDlg),管理对话框资源,派生自CDIalog或CDialogExt均可
执行过程
程序启动进入入口函数WinMain
获取应用程序类对象theApp的地址
利用theApp地址调用InitApplication,初始化当前应用程序的数据
利用theApp地址调用InitInstance函数,在函数中创建无模式对话框并显示
利用theApp地址调用CWinApp的Run()函数进行消息循环
如果没有消息,利用theApp地址调用OnIdle虚函数实现空闲处理
当对话框销毁(必须利用DestoryWindow),消息循环才可退出
退出前,利用theApp地址调用ExitInstance虚函数实现退出前的善后处理工作
模式对话框
模式对话框架构使用
参与架构的类
CDialog/CWinApp
代码书写
添加对话框资源
定义一个自己的对话框类(CMyDlg),管理对话框资源,派生自CDialog或CDialogExt均可
执行过程
DoModal内部执行过程
将父窗口设置为不可用
创建无模式对话框
进入消息循环(自带的)
退出消息循环(父类的OnOK/OnCancle导致循环退出)
将父窗口设置为可用状态
销毁无模式对话框
不再执行CWinApp的Run()函数进行的消息循环
程序结束
对象和控件绑定
绑定的作用
将控件窗口和类对象绑定具有两大作用
如果和数据类对象绑定,对象和控件可以进行数据交换
如果和控件类对象绑定,对象就可以代表整个控件
与数据类型对象绑定的使用
数据类型对象和控件可以实现数据交互
重写父类成员虚函数DoDataExchange
在函数内部通过一系列的DDX_xxx函数,实现控件和数据类型对象的数据交互
如果需要实现数据交互,调用UpdateData函数
UpdateDate(TRUE) :控件 -> 变量
UpdateData(FALSE) :变量 -> 控件
与控件类型对象绑定的使用
控件类型对象和控件可以实现对象代表整个控件
重写父类成员虚函数DoDataExchange
在函数内部通过一系列的DDX_xxx函数,实现控件句柄和控件类型对象的绑定
控件类型对象,就代表这个控件
控件消息的基本原理
控件消息的处理
WM_COMMAND消息
LOWORD(wParam)——菜单项ID,控件ID
HIWORD(wParam)——菜单项,为0
控件,通知码(控件发生的事情)
lParam——均无用
基本控件
下压式按钮
相关类
CButton类,父类为CWnd,封装了关于下压式按钮控件的各种操作
消息
当下压式按钮被单击
WM_COMMAND消息(通知码BN_CLICKED)
编辑框
相关类
CEdit类:父类为CWnd,封装了关于编辑框控件的各种操作
常见操作
获取编辑框文本内容
设置编辑框文本内容
消息
当编辑框文本内容发生变化
WM_COMMAND消息(通知码EN_CHANGE)
复选框按钮
相关类
CButton类,父类为CWnd,封装了关于复选框按钮控件的各种操作
常见操作
获取复选框状态
CButton::GetCheck
设置复选框状态
CButton::SetCheck
消息
当复选框按钮被单击
WM_COMMAND消息(通知码BN_CLICKED)
单选框按钮
相关类
CButton类,父类为CWnd,封装了关于单选框按钮控件的各种操作
常见操作
获取单选框状态
CButton::GetCheck
设置单选框状态
CButton::SetCheck
消息
当单选框按钮被单击
WM_COMMAND消息(通知码BN_CLICKED)
分组框按钮
常见操作
用于控件的分组显示,提高界面友好性
文本静态框
常见操作
常用于显示字符串,提高界面友好性
CWnd::SetWindowText
图像静态框
常见操作
常用于显示图像
CStatic::SetIcon/CStatic::SetBitmap
组合框
分类
简单式组合框/下拉式组合框/下拉列表式组合框
相关类
CComboBox类:父类为CWnd,封装了关于组合框控件的各种操作
常见操作
添加选项
CComboBox::AddString
删除选项
CComboBox::DeleteString
获取选择项索引
CComboBox::GetCurSel
设置选择项
CComboBox::SetCurSel
获取选项文本内容
CComboBox::GetItemText
设置选项附加数据
CComboBox::SetItemData
获取选项附加数据
CComboBox::GetItemData
消息
当选择项发生变化时
WM_COMMAND消息(通知码CBN_SELCHANGE)
当文本内容发生变化时
WM_COMMAND消息(通知码CBN_EDITCHANGE)
列表框
相关类
CListBox类:父类为CWnd,封装了关于列表框控件的各种操作
常见操作
添加选项
CListBox::AddString
删除选项
CListBox::DeleteString
获取选择项
CListBox::GetCurSel
设置选择项
CListBox::SetCurSel
获取选项文本内容
CListBox::GetItemText
设置选项附加数据
CListBox::SetItemData
获取选项附加数据
CListBox::GetItemData
滑块控件
相关类
CSliderCtrl类:父类为CWnd,封装了关于滑块控件的各种操作
常见操作
设置范围
CSliderCtrl::SetRange
设置位置
CSliderCtrl::SetPos
获取位置
CSliderCtrl::GetPos
消息
当滑块的位置改变后
WM_COMMAND消息(通知码NM_CUSTOMDRAW)
进度条控件
相关类
CProgressCtrl类:父类为CWnd,封装了关于进度条控件的各种操作
常见操作
设置范围
CProgressCtrl::SetRange
设置位置
CProgressCtrl::SetPos
获取位置
CProgressCtrl::GetPos
文件搜索类
相关类
CFileFInd类:封装了关于文件遍历的各种操作
常见操作
开启查找,成功返回TRUE,失败返回FALSE
CFileFind::FindFile
找到当前文件,返回下一个文件是否存在
CFileFind::FindNextFile
调用一系列GetXXX函数获取文件信息
CFileFind::GetFileName/CFileFind::GetFilePath
调用一系列IsXXX函数判断文件属性
CFileFind::IsDirectory/CFileFind::IsDot
关闭查找
CFileFind::Close
列表控件
相关类
CListCtrl类:父类为CWnd,封装了关于列表控件的各种操作
风格
图标(LVS_ICON)
小图标(LVS_SMALLICON)
列表(LVS_LIST)
报表(LVS_REPORT)
修改风格
CWnd::ModifyStyle
常见操作
添加选项
CListCtrl::InsertItem
删除选项
CListCtrl::DeleteItem/DeleteAllItems
添加列
CListCtrl::InsertColumn
设置文本内容
CListCtrl::SetItemText
设置图像
CListCtrl::SetImageList
设置附加数据
CListCtrl::SetItemData
获取附加数据
CListCtrl::GetItemData
消息
当列表控件被双击
WM_COMMAND消息(通知码NM_DBCLK)
树控件
相关类
CTreeCtrl类:父类为CWnd,封装了关于树控件的各种操作
常见操作
添加选项
CTreeCtrl::InsertItem
删除选项
CTreeCtrl::DeleteItem/DeleteAllItems
获取选择项
CTreeCtrl::GetSelectedItem
获取选项文本内容
CTreeCtrl::GetItemText
设置选项高度
CTreeCtrl::SetItemHeight
设置图像
CTreeCtrl::SetImageList

