01:MFC应用程序编程
一 MFC的发展
VC 1.0->VC 5.0->VC 6.0->VC2008 SP1)->VS2010
二 MFC基础
1 MFC 微软基础类库
采用类的方式,将Win32 API等进行封装,形成的库.
2 MFC相关的头文件
afx.h (application framework, X)
afxwin.h (类似于windows.h)
afxext.h (MFC扩展头文件)
...
三 MFC应用程序
MFC应用程序主要分为以下三类,对比通常的应用程序,变化的地方如下:
1 MFC的控制台程序
1.1 包含afx系列的头文件 |
1.2 CWinApp theApp - MFC的应用程序类,封装了应用程序的启动过程. |
1.3 AfxWinInit MFC初始化函数,将应用程序的信息初始化. |
2 MFC的动态库和静态库
2.1 静态库
|
||||||
2.2 动态库 动态库分类如下:
动态库与C++动态库的不同点如下:
|
3 MFC应用程序
3.1 单文档视图应用程序
|
|||||
3.2 多文档视图应用程序
|
|||||
3.3 对话框应用程序
|
四 MFC中的类
在MSDN中搜索“Hierarchy chart”就可以看到整个MFC类继承图的关系
常用的类分类如下:
1 CObject类
MFC类的基础,大部分MFC类都是它的子类。
CObject封装了MFC的基础的机制,比如:
1.1 new和Delete |
1.2 Assert |
1.3 运行式信息 |
1.4 动态创建 |
1.5 序列化 |
2 应用程序框架类
封装了应用程序启动相关信息,以及MFC消息映射机制。
3 窗口支持类
封装了窗口操作的API,各种控件及窗口的框架.
4 绘图类
提供了绘图API的封装,以及相关的GDI设备封装.
5 MFC的集合类
提供了数组、链表、映射的数据结构的操作.
6 数据库支持类
ODBC支持类和DAO的支持类。DAO的类已作废.
7 同步类
临界区/事件/互斥/信号量的封装
8 Socket类
封装了socket的编程.
9 常用数据结构
CString CRect CPoint等.
五第一个MFC程序
不使用向导,自己创建一个MFC应用程序。
1.新建一个Win32 Application项目“WinMFC” |
|||||
2.选择一个简单的Win32应用程序 |
|||||
3.修改环境为MFC应用程序的环境
|
|||||
4.增加应用程序类CWinApp
|
这样,一个基本的MFC应用程序就创建好了。编译运行,会看到如下结果:
这个MFC是怎么执行起来的呢??先别急
程序可以启动了,下面开始创建窗口
1.在WinMFC.cpp文件中编写继承自CFrameWnd类的类CMyFrameWnd类 |
2.在App的InitInstance函数定义窗口对象 |
3.创建窗口(Create)并显示(ShowWindow) |
4.将窗口设置成App的主窗口m_pMainWnd = pWnd; |
5.在CMyFrameWnd中添加窗口处理函数WindowProc,在WindowProc中处理消息 |
WinMFC.cpp完整代码 |
// WinMFC.cpp : Defines the entry point for the application. // #include "stdafx.h" //框架窗口类 class CMyFrameWnd : public CFrameWnd { public: //窗口处理函数 virtual LRESULT WindowProc( UINT message, WPARAM wParam, LPARAM lParam ); }; //窗口处理函数 LRESULT CMyFrameWnd::WindowProc( UINT message, WPARAM wParam, LPARAM lParam ) { switch( message ) { case WM_CREATE: AfxMessageBox( "WM_CREATE" ); break; case WM_PAINT: { PAINTSTRUCT ps = { 0 }; HDC hDC = ::BeginPaint( m_hWnd, &ps ); CHAR szText[] = "Hello world!"; TextOut( hDC, 100, 100, szText, strlen( szText ) ); ::EndPaint( m_hWnd, &ps ); } break; } return CFrameWnd::WindowProc( message, wParam, lParam ); } //应用程序类 class CMyApp : public CWinApp { public: virtual BOOL InitInstance( ); }; //定义CMyApp的全局变量 CMyApp theApp; //初始化函数 BOOL CMyApp::InitInstance( ) { //定义窗口对象 CMyFrameWnd * pWnd = new CMyFrameWnd(); //创建窗口 pWnd->Create( NULL, "MyApp" ); //显示窗口 pWnd->ShowWindow( SW_SHOW ); //设置主窗口 m_pMainWnd = pWnd; return TRUE; } |
六 MFC应用程序的启动
MFC应用程序与Win32程序一样,都需要程序的入口函数.
1 CWinApp
应用程序类,封装了应用程序的相关信息,可以提供初始化 消息循环等处理. CWinApp的构造函数中执行了什么:
|
2 程序的入口函数
在CMyApp::InitInstance()函数中加一个断点,然后运行,程序在断点处停下来。 这时候打开“Call Stack”窗口,观察程序的调用过程。 PS:双击调用栈里面的函数,就会跳过去~~ |
||||||||||||
根据跟踪调用过程,得到如下结论:
|
七窗口创建及窗口处理函数
1 窗口创建过程
1 窗口的参数初始化,包括窗口类,窗口风格,窗口处理函数等信息. 注意:将DefWindowProc注册成窗口处理函数. |
2 设置 "创建HOOK" (钩子) 当窗口创建的时候,调用这个HOOK函数. Wnd -> HOOK -> WndProc |
3 创建窗口 CreateWindowEx |
4 卸载 "创建HOOK" 将HOOK程序从当前程序中移除 |
2 HOOK(钩子)程序
上面说的"创建HOOK"到底做了什么:
1 使用AfxGetAfxWndProc函数获取了一个WNDPROC函数指针, 也就是AfxWndProc(AfxWndProcBase)函数地址. |
2 将这个WNDPROC函数设置成当前窗口的处理函数 |
3 将窗口句柄和窗口类的指针保存到MFC的映射数据中(afxMapHWND函数)。这样就可以通过窗口句柄获取对应的窗口对象指针了。 |
总得来说,就是在窗口创建时,将AfxWndProc函数设置为当前窗口的窗口过程函数。
3 AfxWndProc(Base)窗口处理函数
AfxGetAfxWndProc函数到底做了什么:
1 根据窗口句柄获取了相对应的窗口的CWnd *类型的指针,从映射数据中根据窗口句柄查找CWnd *指针.(afxMapHWND函数) |
2 调用AfxCallWndProc函数 |
3 在AfxCallWndProc函数中,调用CWnd的WindowProc函数 注意: 在MFC程序当中,所有窗口处理都是使用一个函数(AfxWndProc函数). |
4 窗口的创建及处理过程
经过上面的分析,总结窗口的创建及处理过程如下:
1 将DefWindowProc函数注册成当前窗口的处理函数 |
2 设置钩子函数 |
3 创建窗口,并执行钩子函数 |
4 在钩子函数中将窗口类指针和窗口句柄的对应关系保存. |
5 在钩子函数中将AfxWndProc(Base)函数设置当前窗口的窗口处理函数 |
6 在AfxWndProc(Base)收到窗口消息,从窗口对应关系中,查询相应的窗口类指针. |
7 调用窗口类WindowProc函数处理消息 |
八消息映射
为了简化Win32的API编程中复杂的switch...case...,MFC重新封装了消息的传递方式。
下面,新建一个Win32应用程序,通过修改它实现MFC的消息映射,来观察一下。(PS:其实创建过程跟上面的过程差不多,多写几遍,写到吐为止)
1.修改stdafx.h头文件,将#include <Windows.h> 改成#include <afxwin.h> |
|||
2. |
|||
3.删掉MFCMsg.cpp文件中的WinMain()函数 |
|||
4.
|
|||
5.下面是跟前面不同的地方了。 在CMsgFrame中增加消息映射的宏
|
|||
6.增加一个OnPaint函数作为WM_PAINT消息的响应函数,并在BEGIN_MESSAGE_MAP和END_MESSAGE_MAP之间添加消息映射
|
|||
此时的运行结果: |
|||
7.同理,添加一个WM_CREATE消息:
|
经过以上的分析,总结消息映射如下:
1 消息映射添加步骤
1 在FrameWnd添加消息宏定义 DECLARE_MESSAGE_MAP |
2 添加消息宏实现 |
3 添加消息处理函数 |
4 添加消息和处理函数的对应 |
2 消息宏的实现
DECLARE_MESSAGE_MAP等到底是什么呢?将这些宏展开如下:
//消息映射宏DECLARE_MESSAGE_MAP展开 private: static const AFX_MSGMAP_ENTRY _messageEntries[]; protected: static AFX_DATA const AFX_MSGMAP messageMap; static const AFX_MSGMAP* PASCAL _GetBaseMessageMap(); virtual const AFX_MSGMAP* GetMessageMap() const; |
//BEGIN_MESSAGE_MAP( CMsgFrame, CFrameWnd )展开 const AFX_MSGMAP* PASCAL CMsgFrame::_GetBaseMessageMap() { return &CFrameWnd::messageMap; } const AFX_MSGMAP* CMsgFrame::GetMessageMap() const { return &CMsgFrame::messageMap; } AFX_COMDAT AFX_DATADEF const AFX_MSGMAP CMsgFrame::messageMap = { &CMsgFrame::_GetBaseMessageMap, &CMsgFrame::_messageEntries[0] }; AFX_COMDAT const AFX_MSGMAP_ENTRY CMsgFrame::_messageEntries[] = { |
//ON_MESSAGE( WM_PAINT, OnPaint )展开 { WM_PAINT, 0, 0, 0, AfxSig_lwl,(AFX_PMSG)(AFX_PMSGW)(LRESULT (AFX_MSG_CALL CWnd::*)(WPARAM, LPARAM))&OnPaint }, |
//END_MESSAGE_MAP()展开 {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } }; |
先不要晕,将展开的代码多看几遍,分析一下就清楚了。
其中有些数据类型需要解释一下:
1 AFX_MSGMAP_ENTRY 是用于保存消息ID与对应函数指针,及相关的信息
|
|
2 AFX_MSGMAP 用于保存GetBaseMap的函数地址及AFX_MSGMAP_ENTRY数组的地址.
|
下面对上面的宏代码做一些说明
1 _messageEntries[], 静态成员,类型为 AFX_MSGMAP_ENTRY。 保存CMsgFrame中消息ID和对应的消息处理函数的数组。 |
2 messageMap, 静态成员,类型为 AFX_MSGMAP。 保存了CMsgFrame中的_GetBaseMessageMap函数指针以及_messageEntries数组地址 |
3 _GetBaseMessageMap, 静态成员 获取父类的messageMap的地址 |
4 GetMessageMap,虚函数 获取自己的messageMap地址 |
下面通过“->”来捋一下几个成员直接的关系:
CMsgFrame::GetMessageMap -〉&messageMap{ &_messageEntries[0] -> { ID<->Func, ID<->Func, .... } _GetBaseMessageMap -> &parent::messageMap { &_messageEntries[0], _GetBaseMessageMap->&parent::messageMap } } |
3 消息映射过程
1 消息处理函数WindowProc收到消息后,调用OnWndMsg处理消息,OnWndMsg如果不处理消息,那么WindowProc将调用DefWindowProc默认处理消息并返回. |
|||||||
2 OnWndMsg处理消息
|
九MFC的消息分类
MFC的消息分类按照处理方式来分。主要分为4类:
1 窗口消息
例如WM_CREATE、WM_PAINT、鼠标、键盘等消息,这些消息的处理方式是直接调用消息处理函数.
这类消息使用的宏:
ON_MESSAGE( )
ON_WM_XXXXX( ): ON_WM_CREATE()
消息处理时,采用上面的处理方式.
前面的WM_CREATE消息就不用写成ON_MESSAGE( WM_CREATE, OnCreate ),而直接写成ON_WM_CREATE即可。
函数的定义和声明也要相应地修改为:
afx_msg LRESULT OnCreate( WPARAM wParam, LPARAM lParam ); |
afx_msg int OnCreate(); |
LRESULT CMsgFrame::OnCreate( WPARAM wParam, LPARAM lParam ) { AfxMessageBox("OnCreate"); return 0; } |
int CMsgFrame::OnCreate() { AfxMessageBox("OnCreate"); return 0; } |
2 命令消息 WM_COMMAND
菜单、工具栏、按钮等点击时的命令. 消息首先发送到主窗口,由主窗口逐层向子窗口派发。
这类的消息使用的宏:
ON_COMMAND( )
ON_COMMAND_RANGE( )
消息处理时,在OnWndMsg中调用OnCommand处理函数进行消息处理.
3 通知消息 WM_NOTIFY
子窗口对父窗口的通知消息。
控件消息宏,例如: EDIT控件 ON_EN_CHANGE、ON_NOTIFY/ON_NOTIFY_RANGE消息处理时,在OnWndMsg中调用OnNotify(OnCommand)处理函数进行消息处理。
4 自注册消息
用户自注册消息的处理。
用户需调用RegisterWindowMessage函数注册消息,然后在消息映射中使用.
UINT RegisterWindowMessage(
LPCTSTR lpString //消息名字符串
);
返回注册成功的消息ID(0xC000-0xFFFF)
消息映射宏: ON_REGISTERED_MESSAGE
消息处理时, 与窗口消息处理类似,但是在查找消息处理函数和执行消息处理函数时不同.
自注册消息使用举例:
// RegisterMsg.cpp : Defines the entry point for the application. // #include "stdafx.h" //注册消息ID UINT g_nRegMsg = RegisterWindowMessage( "MYREGMSG" ); class CRegsiterFrame : public CFrameWnd { DECLARE_MESSAGE_MAP( ) public: afx_msg int OnCreate( LPCREATESTRUCT lpCreateStruct ); afx_msg void OnTest( ); //消息处理函数 afx_msg LRESULT OnRegMsg( WPARAM wParam,LPARAM lParam ); }; BEGIN_MESSAGE_MAP( CRegsiterFrame, CFrameWnd ) ON_WM_CREATE( ) ON_COMMAND( 1001, OnTest ) //消息宏映射 ON_REGISTERED_MESSAGE( g_nRegMsg, OnRegMsg ) END_MESSAGE_MAP( ) int CRegsiterFrame::OnCreate( LPCREATESTRUCT lpCreateStruct ) { //父类的OnCreate处理 /*if(!CFrameWnd::OnCreate( lpCreateStruct ) ) { return 0; }*/ //创建按钮 CreateWindow( "BUTTON", "Test", WS_CHILD|WS_VISIBLE, 50, 50, 200, 30, m_hWnd, (HMENU)1001, AfxGetApp()->m_hInstance, NULL ); return 1; } void CRegsiterFrame::OnTest( ) { //发送消息 SendMessage( g_nRegMsg ); } LRESULT CRegsiterFrame::OnRegMsg( WPARAM wParam,LPARAM lParam ) { AfxMessageBox( "OnRegMsg" ); return 0; } class CRegisterApp : public CWinApp { public: virtual BOOL InitInstance( ); }; CRegisterApp theApp; BOOL CRegisterApp::InitInstance( ) { CRegsiterFrame * pWnd = new CRegsiterFrame( ); pWnd->Create( NULL, "Register Msg" ); m_pMainWnd = pWnd; m_pMainWnd->ShowWindow( SW_SHOW ); m_pMainWnd->UpdateWindow( ); return TRUE; } |
01:MFC应用程序编程的更多相关文章
- VS2010/MFC编程入门之四(MFC应用程序框架分析)
VS2010/MFC编程入门之四(MFC应用程序框架分析)-软件开发-鸡啄米 http://www.jizhuomi.com/software/145.html 上一讲鸡啄米讲的是VS2010应用 ...
- 《Windows编程循序渐进》——建立MFC应用程序
如何建立MFC应用程序 打开VS2013:
- MFC应用程序框架(转)
对于程序员来说,如果要提高编程效率,一个好用的,功能强大的并且可以得心应手使用的编程工具往往会给我们程序员带来莫大的方便.其实对于现在的编程工具来说,使用哪一种工具都不是问题的关键,重要的是你能够使用 ...
- MFC应用程序框架入门(转)
1 MFC概述 顾名思意,MFC应用程序框架是以MFC作为框架基础的,以此程序框架模式搭建起来的应用程序在程序结构组织上是完全不同于以前的Win32 SDK编程方式的.自20世纪90年代初问世以来,M ...
- MFC下DLL编程(图解)
DLL(Dynamic Link Library,动态链接库)是微软公司为Windows和OS/2操作系统设计一种供应用程序在运行时调用的共享函数库.DLL是应用程序的一种扩展,也是软件共享和重用的传 ...
- MFC下DLL编程(图解)
MFC下DLL编程(图解) DLL(Dynamic Link Library,动态链接库)是微软公司为Windows和OS/2操作系统设计一种供应用程序在运行时调用的共享函数库.DLL是应用程序的一种 ...
- 基于MFC的socket编程
网络编程 1.windows 套接字编程(开放的网络编程接口)添加头文件#include<windows.h> 2.套接字及其分类 socket分为两种:(1)数据报socket:无连接套 ...
- MFC控件编程进度条编写
MFC控件编程进度条编写 一丶进度条编程需要用到的方法 进度条MFC已经帮我们封装好类了. 叫做 CProgressCtrl 进度条编程也很简单. 封装的方法也就那个那几个. GetPos() 获 ...
- MFC控件编程之复选框单选框分组框
MFC控件编程之复选框单选框分组框 一丶分组框 分组框 英文叫做 GroubBox 添加了分组框主要就是分组.好看.不重点介绍 二丶单选框 英文: Raido Button 单选框需要注意的事项 1. ...
随机推荐
- M1事后分析报告--We have power to change the origin state
M1事后分析报告 设计与实现 我们发的软件解决的问题?是否满足后面小组的要求?是否能够完全拟合前期目标? 答: 前期我们的软件完成量并不是特别让人满意,我们组在完成这些任务量之后,发现有很多地方是在做 ...
- linux内核分析第一周学习笔记
linux内核分析第一周学习笔记 标签(空格分隔): 20135328陈都 陈都 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.co ...
- XCODE 6.1.1 配置GLFW
最近在学习opengl的相关知识.第一件事就是配环境(好烦躁).了解了一下os x下的OpenGL开源库,主要有几个:GLUT,freeglut,GLFW等.关于其详细的介绍可以参考opengl网站( ...
- bootstrap响应式布局列子
<!DOCTYPE html><html lang="zh-CN"> <head> <meta charset="utf-8&q ...
- [财务知识] debt debit credit 的区别于联系
https://blog.csdn.net/sjpljr/article/details/70169303 剑桥词典解释分别为: Debt [C or U ] n.something, especia ...
- [转载] 什么是istio 官网内容
网址:https://preliminary.istio.io/zh/docs/concepts/what-is-istio/ mark 一下 1.0 昨天刚发布. 2018.7.31 Istio 是 ...
- blog 社会化评论插件 多说for china, disqus for global range
1.disqus https://disqus.com/ https://publishers.disqus.com/engage https://disqus.com/admin/create/ h ...
- js new关键字
实现new 关键字只需4步 1. 声明一个对象: 2. 把这个对象的__proto__ 指向构造函数的 prototype; 3. 以构造函数为上下文执行这个对象: 4. 返回这个对象. 简洁的代码示 ...
- Java之List使用场景
1.List使用场景 特点: ①在 List集合中允许出现 重复元素 <通过元素的equals方法,来比较是否为重复的元素.> ②所有元素是以一种 线性方式进行存储 ③List集合还有一个 ...
- P4932 浏览器
题目背景 __stdcall在用Edge玩slay的时候,鼠标会经常失灵,这让她十分痛苦,因此她决定也要让你们感受一下Edge制造的痛苦. 题目描述 __stdcall给了你n个点,第i个点有权值x[ ...