1.1 加载菜单
1.2 调用CWnd::CreateEx创建窗口
1.2.1 调用 PreCreateWindow 设计并注册窗口类
1.2.2 调用AfxHookWindowCreate设置钩子————HOOK(钩子)-将自己的处理函数,挂接入系统的处理过程。
调用SetWindowsHookEx,创建了一个WH_CBT类型的钩子,_AfxCbtFilterHook钩子的回调函数。
1.2.3 调用CreateWindowEx创建窗口,立即调用_AfxCbtFilterHook
1.2.4 调用AfxUnhookWindowCreate 卸载钩子
1.3 _AfxCbtFilterHook函数执行
1.3.1 获取MFC提供的窗口处理函数的地址(AfxWndProcBase的地址)
1.3.2 使用SetWindowLong将窗口的处理函数由原来的DefWindowProc替换为AfxWndProcBase
Windows程序中的消息处理是在winproc函数中,通过switch结构实现的。但当处理的消息比较多时,switch-case结构将变得分支很多,影响程序的可读性。而在MFC中,则采用消息映射的结构进行结构化消息处理。进行MFC消息处理,程序员要做的就是为每个要处理的消息提供一个消息处理函数,然后系统通过MFC提供的一套消息映射系统来调用相应的消息处理函数。消息映射就是消息与消息处理函数一对一的联系。MFC的消息映射采用消息映射宏的方式,把消息和消息处理函数一一对应起来。在其头文件中可以找到DECLARE_MESSAGE_MAP()宏。
1.1 在窗口类的定义中添加消息映射的声明宏
DECLARE_MESSAGE_MAP()
1.2 在窗口类的实现中添加消息映射的实现宏
BEGIN_MESSAGE_MAP(CMsgFrame,CFrameWnd)
END_MESSAGE_MAP()
1.3 添加消息处理函数的定义和实现
1.4 添加消息标识与消息处理函数的映射关系
BEGIN_MESSAGE_MAP(CMsgFrame,CFrameWnd)
ON_MESSAGE(WM_CREATE,OnCreate)
ON_MESSAGE(WM_PAINT,OnPaint)
END_MESSAGE_MAP()
MFC把消息分为5类:窗口消息,控件通知消息,命令消息,用户自定义消息,系统注册消息
大部分的窗口,键盘,鼠标等消息,如当创建窗口,绘制窗口,移动窗口,销毁窗口,以及使用键盘,鼠标等与操作窗口有关的动作时,产生的消息均属于窗口消息。窗口消息有MFC的窗口(CWnd)对象来处理。系统提供了标准的消息处理函数。
消息映射宏:ON_WM_XXX()
控件是一个windows的一个子窗口(eg:对话框中的按钮,编辑框等),控件通知消息是指在事件发生时,由控件或其他类型的子窗口发送到父窗口的消息。它通知父窗口,该控件接受了某操作,为父窗口进一步控制子窗口提供了条件。
消息映射宏:ON_xx_CLICKED/DBCLK(控件ID,响应函数)
菜单,工具栏以及按钮等消息。一般与处理用户的请求有关,主要是来自菜单,工具栏和加速键的通知消息。从CCmdTarget派生的类(eg:文档,文档模板,应用程序对象,窗口和视图等)都能处理命令消息。
消息映射宏:ON_COMMAND(命令ID,响应函数)
ON_COMMAND_RANGE(起始ID,终止ID,响应函数)
响应函数基本是:afx_msg void memberFun()
所有由用户定义的命令消息也由ON_COMMAND定义消息映射关系。
命令消息的执行过程:
2.1在CWnd::OnWndMsg函数中,判断消息如果是WM_COMMAND调用OnCommand函数
2.2 在OnCommand函数,调用CCmdTarget::OnCmdMsg函数
2.3 在OnCmdMsg函数中,查找消息映射。
通过#define定义消息标识,eg #define WM_USERDEFMSG (WM_USER+101)
消息映射宏:ON_MESSAGE(命令ID,响应函数)
响应函数的原型:afx_msg LRESULT memberFun(WPARAM,LPARAM)
UINT RegisterWindowMessage(LPCTSTR lpString //消息名称);//返回值消息标识
消息映射宏:ON_REGISTERED_MESSAGE(注册ID,响应函数)
响应函数的原型:afx_msg LRESULT memberFun(WPARAM,LPARAM)
eg:const UINT WM_USERDEFMSG=::RegisterWindowMessage(_T("UserDefMsg"));
ON_REGISTERED_MESSAGE(WM_USERDEFMSG,OnUserDefMsg)
1.新建工程MFCBase
新建MFC工程(view或者dialog),删除系统生产的文件,就剩下stdafx.h和stdafx.cpp,新建MFCBase.cpp文件,
或者,新建Win32工程来改成MFC工程,删除系统生产的文件,就剩下stdafx.h(在其内部添加afxwin.h头文件)和stdafx.cpp,新建MFCBase.cpp文件;并修改工程的属性,常规-->MFC的使用:把使用标准windows库 改成 在静态库中使用MFC
#include "stdafx.h" class CMainFrame:public CFrameWnd { public: virtual LRESULT WindowProc( UINT message, WPARAM wParam, LPARAM lParam ); }; LRESULT CMainFrame::WindowProc( UINT message, WPARAM wParam, LPARAM lParam ) { switch (message) { case WM_CREATE: MessageBox(L"OnCreate"); break; case WM_PAINT: PAINTSTRUCT ps={0}; CDC* pDC=BeginPaint(&ps); pDC->TextOut(50,100,L"Hello MFC"); EndPaint(&ps); break; } return CFrameWnd::WindowProc(message,wParam,lParam); } class CMyWinApp:public CWinApp { public: virtual BOOL InitInstance(); }; BOOL CMyWinApp::InitInstance() { CMainFrame* pFrame=new CMainFrame; pFrame->Create(NULL,L"MFCWnd"); m_pMainWnd=pFrame; pFrame->ShowWindow(SW_SHOW); pFrame->UpdateWindow(); return TRUE; } CMyWinApp theApp;
2.新建工程MFCCmd
创建工程同上,新建文件MFCCmd.cpp文件
// MyMFCCmd.cpp : 定义应用程序的入口点。 #include "stdafx.h" #define ON_CMD_T1 1025//菜单消息标识 #define ON_CMD_T2 1026//菜单消息标识 #define ON_CMD_T3 1027//菜单消息标识 #define ON_CMD_EDIT 1028//菜单消息标识 #define WM_USERDEFMSG (WM_USER+101)//用户自定义消息标识 const UINT WM_USERDEFMSG2=RegisterWindowMessage(L"UserDefMsg2");//系统注册消息标识 class CMainFrame:public CFrameWnd { DECLARE_MESSAGE_MAP() protected: afx_msg int OnCreate( LPCREATESTRUCT lpCreateStruct ); afx_msg void OnTest1(); afx_msg void OnTestRange(UINT nID); afx_msg void OnEditChange(); afx_msg LRESULT OnUserDefMsg(WPARAM wParam,LPARAM lParam);//用户自定义消息函数 afx_msg LRESULT OnUserDefMsg2(WPARAM wParam,LPARAM lParam);//系统注册消息函数 }; BEGIN_MESSAGE_MAP(CMainFrame,CFrameWnd) ON_WM_CREATE()//窗口消息 ON_COMMAND(ON_CMD_T1,OnTest1)//命令消息 ON_MESSAGE(WM_USERDEFMSG,OnUserDefMsg)//用户自定义消息 ON_REGISTERED_MESSAGE(WM_USERDEFMSG2,OnUserDefMsg2)//系统注册消息 ON_EN_CHANGE(ON_CMD_EDIT,OnEditChange)//控件消息 ON_COMMAND_RANGE(ON_CMD_T2,ON_CMD_T3,OnTestRange) END_MESSAGE_MAP() int CMainFrame::OnCreate( LPCREATESTRUCT lpCreateStruct ) { //创建按钮 CreateWindow(L"BUTTON",L"Test1",WS_CHILD|WS_VISIBLE,50,50,60,40,m_hWnd,(HMENU)ON_CMD_T1,AfxGetInstanceHandle(),NULL); CreateWindow(L"BUTTON",L"Test2",WS_CHILD|WS_VISIBLE,50,100,60,40,m_hWnd,(HMENU)ON_CMD_T2,AfxGetInstanceHandle(),NULL); CreateWindow(L"BUTTON",L"Test3",WS_CHILD|WS_VISIBLE,50,150,60,40,m_hWnd,(HMENU)ON_CMD_T3,AfxGetInstanceHandle(),NULL); CreateWindow(L"EDIT",L"",WS_CHILD|WS_VISIBLE|WS_BORDER,150,90,70,50,m_hWnd,(HMENU)ON_CMD_EDIT,AfxGetInstanceHandle(),NULL); return 0; } LRESULT CMainFrame::OnUserDefMsg(WPARAM wParam,LPARAM lParam) { MessageBox(L"OnUserDefMsg"); return 0; } LRESULT CMainFrame::OnUserDefMsg2(WPARAM wParam,LPARAM lParam) { MessageBox(L"OnUserDefMsg2"); return 0; } void CMainFrame::OnTest1() { //MessageBox(L"OnTest1"); SendMessage(WM_USERDEFMSG,NULL,NULL);//发送系统注册消息 SendMessage(WM_USERDEFMSG2,NULL,NULL);//发送用户自定义消息 } void CMainFrame::OnTestRange(UINT nID) { CString strID; strID.Format(L"%d",nID); MessageBox(strID); } void CMainFrame::OnEditChange() { CWnd* pWnd=GetDlgItem(ON_CMD_EDIT); CString strContent; pWnd->GetWindowTextW(strContent); MessageBox(strContent); } class CCmdApp:public CWinApp { public: virtual BOOL InitInstance(); }; BOOL CCmdApp::InitInstance() { CMainFrame* pFrame=new CMainFrame; pFrame->Create(NULL,L"MFCCmd"); m_pMainWnd=pFrame; pFrame->ShowWindow(SW_SHOW); pFrame->UpdateWindow(); return TRUE; } CCmdApp theApp;
3.分析这两个工程的异同
相同点:各自都创建了CFrameWin框架窗口子类和CWinApp窗口应用子类,且重写窗口应用的初始化成员函数InitInstance()基本相同
不同点:主要表现在创建创建窗口的过程和消息的处理方面。工程MFCBase主要根据win32窗口应用程序的思想来创建窗口和处理消息的。而工程MFCCmd用到了MFC的消息映射来创建窗口和处理消息的。