MFC流程笔记
2017vs
使用标准的windows库--win32程序
使用MFC静态/动态库--MFC程序
MFC各基类含义
1、单文档视图程序
CWinApp类-【应用程序类】。维护整个应用程序的运行过程
CFrameWnd类-主框架窗口类。创建主框架窗口。【框架类】
CEditView类-视图类。创建视图窗口,显示数据。
CDocument类-文档类。管理数据,包括数据的保存和加载
2、多文档视图程序
CMDIFrameWnd类-多文档的主框架窗口类。
CMDIChildWnd类-多文档的子框架窗口类。
CWinApp类-应用程序类。维护整个应用程序的运行过程
CEditView类-视图类。创建视图窗口,显示数据。
CDocument类-文档类。管理数据,包括数据的保存和加载
3、基于对话框的程序
CWinApp类-应用程序类。维护整个应用程序的运行过程
CDialogEx类-【对话框类】。创建对话框窗口。
MFC成员虚函数:
InitInstance():程序的初始化函数,完成窗口的创建等初始化工作。
ExitInstance():程序退出时调用,清理资源等善后工作。
Run():消息循环。
OnIdle:空闲处理函数。
MFC成员变量:
m_pMainWnd:当前应用程序主窗口。主框架窗口的对象地址pFrame。
最简版,不含消息循环
框架类CFrameWnd,定义一个自己的框架类。
#include <afxwin.h> class CMyFrameWnd:public CFrameWnd //①定义一个自己的【框架类】,【公有派生】自[框架类CFrameWnd] { }; class CMyWinApp:public CWinApp //②在定义一个自己的【应用程序类】,公有派生自【CWinApp类】 { public: CMyWinApp();//③两个成员函数,一个是构造函数。什么也不写 virtual BOOL InitInstance();//④重写一个【父类】,的虚函数。 }; CMyWinApp::CMyWinApp() { } BOOL CMyWinApp::InitInstance() //声明与实现可以分开规范,声明都放头文件,实现单独。 { CMyFrameWnd *pFrame=new CMyFrameWnd; //⑤new一个,自己写的框架类的对象,CMyFrameWnd的对象。 pFrame->Create(NULL,"MFC base"); //⑥然后,用这个对象pFrame,调用【框架类】的成员函数creat。 m_pMainWnd=pFrame; //简单的【赋值语句】。 pFrame->ShowWindow(SW_SHOW); //调用showwindow。 pFrame->UpdateWindow(); //调用updatewindos。 return true; } //5、创建一个程序类对象(触发点): CMyWinApp theApp; //⑦定义一个,全局的,CMyWinApp的对象。程序各处都会调用它。
MFCbase用到的类
红线内,mfc库的类,外自己写的类。m_pMainWnd成员变量中保存pFrame的地址。
如何运行,入口函数在哪?
①构造全局对象CMyWinApp,过程的关键几个mfc库内流程:
mfc库,封装了入口函数。
C++全局对象,优先于入口函数。所以需要在,实例化CMyWinApp全局对象theApp处打断点。
//mfc库内有三个全局变量 AFX_MODULE_STATE aaa; // 当前程序【模块状态】信息,的 AFX_MODULE_THREAD_STATE bbb; // 当前程序【线程状态】信息 CWinApp::CWinApp() { AFX_MOUDLE_STATE* pModuleState = AfxGetModuleState(); // 获取全局变量aaa的地址 &aaa,其实是宏_AFX_CMDTARGET_GETSTATE,此处已替换。 AFX_MODULE_THREAD_STATE* pThreadState = pModuleState->m_thread; // 获取全局变量 &bbb 其中pModuleState是上获得的aaa的地址,故bbb地址保存于aaa的成员 pThreadState->m_pCurrentWinThread = this; // 将this是theApp的地址,保存到bbb(pThreadState)的一个成员中。 ASSERT(AfxGetThread() == this); //断言判断:括号内必须成立,否则抛出错误,停止运行。afx开头,mfc全局函数,返回theapp的地址。 // 调用 AfxGetThread() 和 AfxGetApp() 返回的都是 theApp 的地址。 pModuleState->m_pCurrentWinApp = this; //将theapp的地址,又保存到aaa(pModuleState)的成员变量中。 ASSERT(AfxGetApp() == this);//断言判断:返回theapp的地址。 AfxGetApp{afxCurrentWinApp AfxGetModuleState()->m_pCurrentWinApp};//返回一个宏,宏替换的内容。 }
②入口函数,及调用创建及显示窗口
initInstance()函数,内部任意处下断,通过【调用堆栈】查看函数调用关系,谁调用了initInstance()函数,能“刨到祖坟”上。
注意:调用关系,是已经运行过的,所以下断后,重新运行。
利用多态虚函数,【重写虚函数】把自己的代码加到MFC,实现流程管理。
vs->debug->windows->call stack
WinMain(...) //参数没什么用,简化。关注,是否theapp对象指导向哪走。 { AfxWinMain(...) { CWinThread* pThread = AfxGetThread(); //刚刚在构造中看到的函数,返回theApp的地址。 CWinApp* pApp = AfxGetApp(); //AfxGetApp()在构造中看到,返回theApp的地址。上来先拿theapp,由theapp指导流程。 pApp->InitApplication(); //【pApp】是上获得的theapp的地址,利用theApp调用【应用程序类】的成员[虚函数](初始化),自己写的子类重写过,会调用子类的。 //mfcbase没重写。 pThread->InitInstance() //【pThread】也是theapp的对象,利用theApp调用应用程序类的成员虚函数。重写过,创建显示窗口。 { //回到自己的代码,内部this,为theApp地址。 CMyFrameWnd *pFrame = new CMyFrameWnd;//先new个【框架类】对象。 pFrame->Create(NULL, TEXT("MFC base"));//后面再关注,用【框架类对象】调用creat创建窗口。 m_pMainWnd = pFrame;//pFrame是【框架类】的地址,m_pMainWnd前面隐含,theapp的地址,this->m_pMainWnd。将【框架类地址】赋值给theapp。 pFrame->ShowWindow(SW_SHOW); pFrame->UpdateWindow(); return true; //return回到mfc库内,自己写的代码,加入到mfc库内,流程内。很多虚函数,让程序员重写,将自己流程加入到mfc库中。 } pThread->Run() //创建、显示完窗口,改消息循环了。利用theApp调用应用【程序类】的成员[虚函数],函数内部this为theApp的地址。 { CWinThread::Run() //函数内部this为theApp的地址,父类的CWinApp::Run()内部,在调用爷类的run函数CWinThread::Run()。 { for (;;)//进入到,一个死的for循环。即消息循环。 while (没有消息)//bIdle上面定义就true,::peekmessage是API函数,当没有消息时返回0,“侦察兵”。 { OnIdle(); //利用theApp调用应用程序类的成员[虚函数](做空闲处理),隐含this指针。 } do { if (GetMessage捕获WM_QUIT) //内部,AfxInternalPumpMessage中有api函数,getmessage()。“抓消息” //下面::TranslateMessage、::DispatchMessage;在PumpMessage内部,”翻译消息“和”派发消息“的都在。 return ExitInstance(); //return意味能跳出死循环, pumpmess()函数返回0时。利用theApp调用【应用程序类】的成员[虚函数](善后处理) } while (...); } } } }
③创建窗口的过程
create()打断点,谁调用改函数,改函数内部this指针就是谁,create()内部this指针,即pFrame,而pFrame即自己CMyFrameWnd地址。
CMyFrameWnd *pFrame = new CMyFrameWnd; pFrame->Create(NULL, L"MFCbase") //函数内部this为pFrame(自己new的框架类的地址),第一个参数lpszClassName,为NULL。 { //创建窗口的第一个步骤,先加载菜单 hMenu = ::LoadMenu(hInst, lpszMenuName); //但此处不可,因为lpszMenuName形参,未传参。 //创建窗口 CreateEx(..., NULL, ...) //函数内部this为pFramen,补充this->CreateEx。此处单步,先进入【参数的函数】内部,后进入CreaeEx()函数。 { CREATESTRUCT cs; //CREATESTRUCT结构体,有12个成员,与cs中就是CreateWindowEx中的12个参数对应。 ...... //给cs结构体赋值。 cs.lpszClass = NULL; //窗口类名,怎么可能为NULL,下面会重新赋值。 ...... cs.hInstance = AfxGetInstanceHandle(); //拿到WinMain的第一个参数,当前实例的句柄。以后可通过AfxGetInstanceHandle()拿到。 ...... PreCreateWindow(cs) //内部会给cs.lpszClass重新赋值。并且【注册窗口类】。 { if (cs.lpszClass == NULL) { AfxDeferRegisterClass(...) //这个函数内,注册窗口类,并赋值。 { WNDCLASS wndcls; ...... wndcls.lpfnWndProc = DefWindowProc; //默认消息处理,函数名,我们怎么处理消息????? 后面改 ...... _AfxRegisterWithIcon(&wndcls, L"AfxFrameOrView120sud", ...) //内部要给窗口类名称赋值了。 { &wndcls->lpszClassName = L"AfxFrameOrView120sud";//形参名,已改。 ...... ::AfxRegisterClass(&wndcls) //内部,注册窗口类。::AfxCtr派遣,既有::又有afx。 { RegisterClass(&wndcls); //注册窗口类 } } } cs.lpszClass = _afxWndFrameOrView; // 是个数组,存的长串字符串:L"AfxFrameOrView120sud" //此处也if内。 } } AfxHookWindowCreate(pFrame) //如无此处,消息都默认DefWindowProc()处理了。this是pFrame,自己new的【框架对象】的地址。 { _AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData(); //获取全局变量(当前程序线程【信息】)的地址,获取全局变量ccc //与bbb差个【 状态信息】。 ::SetWindowsHookEx(WH_CBT, _AfxCbtFilterHook, NULL, ::GetCurrentThreadId()); //利用win32API函数在程序中埋下一个类型为WH_CBT的钩子,只对WM_Create消息感兴趣。 pThreadState->m_pWndInit = pFrame; //将自己new的框架类对象地址pFrame,保存到【全局变量】ccc中。 } ::CreateWindowEx(...); //此函数一旦执行完成,WM_CREATE消息立即被钩子钩走(钩到【钩子处理函数】_AfxCbtFilterHook中,) //窗口一旦创建完成,就产生WM_CREAT消息。此处,运行,断在已下断的【钩子处理函数】处。 //注意!!!【钩子处理函数】是产生[消息]后,[操作系统]调用的。!!!! } } //钩子处理函数。【钩子处理函数】是产生[消息]后,[操作系统]调用的。!!!! //在windows【句柄】能代表窗口,将pFrame与【句柄】一一对应绑定。 LRESULT CALLBACK _AfxCbtFilterHook(int code, WPARAM wParam, LPARAM lParam) { _AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData(); //获取全局变量ccc(当前程序线程信息)的地址 ... CWnd* pWndInit = pThreadState->m_pWndInit; //从当前程序线程信息,重新拿到【pFrame】===pWndInit ... HWND hWnd = (HWND)wParam; //函数传入的参数就是窗口句柄,获取刚创建的【窗口句柄】 ... pWndInit->Attach(hWnd) //将【pFrame】与【窗口句柄】绑定,函数内部this为pFrame。 { ... CHandleMap* pMap = afxMapHWND(TRUE) //CHandleMap是MFC封装的映射类。 { AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState(); //获取全局变量bbb的地址,准备要存东西。 ... pState->m_pmapHWND = new CHandleMap(...); //new了一个【映射类对象】,并将对象地址[全局变量]bbb的成员中 .... return pState->m_pmapHWND;//return刚刚【映射类】对象的地址。pMap接收。 } ... pMap->SetPermanent(pFrame->m_hWnd = hWnd, pFrame) //函数内部this指针为pMap(映射类对象地址),第一个参数窗口句柄,第二个pFrame { pMap->m_permanentMap[hWnd] = pFrame;//补上this指针。这是一个数组,拿【窗口句柄】当下标。故已知【句柄】则可得到【pFrame】。 } } WNDPROC afxWndProc = AfxGetAfxWndProc(); //第二件事,更改窗口处理函数。获取AfxWndProc函数地址 oldWndProc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC, (DWORD_PTR)afxWndProc);//SetWindowLongPtr是API函数,更改窗口的处理函数。改为AfxWndProc(真正的窗口处理函数) //就不是默认的处理函数了。 }
MFCbase改进,重写虚函数,开始处理消息
#include <afxwin.h> //----------------------------------改变的部分------------------------------------------// class CMyFrameWnd:public CFrameWnd //①定义一个自己的【框架类】,【公有派生】自[框架类CFrameWnd] { public: virtual LRESULT WindowProc(UINT msgID, WPARAM wParam, LPARAM lParam); //类内声明 }; //类外实现。//钩子函数写的AfxWndProc真正的窗口处理函数,会调用这个WindowProc() LRESULT CMyFrameWnd::WindowProc(UINT msgID, WPARAM wParam, LPARAM lParam) { //此函数的this为pFram switch (msgID) {case WM_CREATE: AfxMessageBox(L"WM_CREATE消息被处理"); break;
case 其他消息 } return CFrameWnd::WindowProc(msgID, wParam, lParam);//处理完关注的消息后,这个处理默认的消息。 //调用一次,被重写覆盖的版本,处理其他消息。 } //------------------------------------------------------------------------------------// class CMyWinApp:public CWinApp //②在定义一个自己的【应用程序类】,公有派生自【CWinApp类】 { public: CMyWinApp();//③两个成员函数,一个是构造函数。什么也不写 virtual BOOL InitInstance();//④重写一个【父类】,的虚函数。 }; CMyWinApp::CMyWinApp() { } BOOL CMyWinApp::InitInstance() //声明与实现可以分开规范,声明都放头文件,实现单独。 { CMyFrameWnd *pFrame=new CMyFrameWnd; //⑤new一个,自己写的框架类的对象,CMyFrameWnd的对象。 pFrame->Create(NULL,"MFC base"); //⑥然后,用这个对象pFrame,调用【框架类】的成员函数creat。 m_pMainWnd=pFrame; //简单的【赋值语句】。 pFrame->ShowWindow(SW_SHOW); //调用showwindow。 pFrame->UpdateWindow(); //调用updatewindos。 return true; } //5、创建一个程序类对象(触发点): CMyWinApp theApp; //⑦定义一个,全局的,CMyWinApp的对象。程序各处都会调用它。
[消息]进入【窗口处理函数】开始,如何一步步回到自己写的
【重写WndProc()】方式处理消息,与【消息映射】方式处理消息。
跟踪WM_CREATE、WM_PAINT消息(0x0001消息号)。
进入AfxWndProc()--> 根据已知句柄,查询pFrame-->调用成员虚函数WndProc()。
//队列消息、非队列消息,最终目的都是【窗口处理函数】 //看WM_CREATE消息流程。当收到消息时, AfxWndProc(...) { //窗口处理函数,参数是【句柄】,只有拿pFrame才能调用虚函数,句柄不能掉成员函数。 CWnd* pWnd = CWnd::FromHandlePermanent(hWnd) //获取pFrame,已知【窗口句柄】给进去,返回类的【对象地址】 { CHandleMap* pMap = afxMapHWND() //映射类对象 { AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState(); //获取全局变量bbb的地址 ... return pState->m_pmapHWND; //返回全局变量bbb的成员,【映射类】对象地址。 } pWnd = (CWnd*)pMap->LookupPermanent(hWnd) //参数上面传进来的,函数内部this为【映射类对象】地址pMap { return (CObject*)m_permanentMap.GetValueAt((LPVOID)hWnd); //补上this指针pMap,m_permanentMap类似数组成员, //GetValueAt()获取下标为窗口句柄的值,即pFrame。然后强转(CObject*)。 } return pWnd; //接返回值pFrame,等同于pFrame。 } ... return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam) //参数的pWnd即为pFrame,后四个为窗口处理 { ... lResult = pWnd->WindowProc(nMsg, wParam, lParam); //用pFrame调用虚函数,回到自己写的的代码。 ... } }
用--消息映射机制--方式处理消息
重写WndProc()方式也能处理,但没用MFC。
消息映射机制--在不重写WndProc()的前提下,仍然可以处理消息。
使用方式:
类内必须添加声明宏:DECLARE_MESSAGE_MAP()
类外添加实现宏:BEGIN_MESSAGE_MAP(theClass, baseClass)
中间要处理的消息
END_MESSAGE_MAP()
要处理的消息,对应使用相应宏:
ON_MESSAGE()是[通用宏]任意消息均可,
WINDOWS标准消息一般用:ON_WM_XXX()宏。省事,专职宏,这种就把[消息]写死了,没有ON_MESSAGE()的两个个参数。【处理函数名】也写死了,OnCreate(),On+消息名()。
声明原型,去手册里查询。MSDN Library -October 2001。
自定义消息用:ON_MESSAGE()宏。
命令消息用:ON_COMMAND()宏。WM_COMMAND(),菜单消息、加速键消息。
自定义消息,操作系统不知到何时发送,故需要自己发送。POSTMESSAGE()
使用例子:以处理WM_CREATE为例
#include <afxwin.h> //【一】【二】【三】【四】的顺序 class CMyFrameWnd:public CFrameWnd //①定义一个自己的【框架类】,【公有派生】自[框架类CFrameWnd] { DECLARE_MESSAGE_MAP() //【一】、类内声明宏 public: LRESULT OnCreate(WPARAM wParam, LPARAM lParam);//【四】好习惯,类内声明函数,类外实现函数。 }; BEGIN_MESSAGE_MAP(theClass, baseClass) //【二】、类外实现,第一个参数【本类的类名】CMyFrameWnd,第二个参数【父类的类名】CFrameWnd。 //自己要处理的消息,放中间 ON_MESSAGE(WM_CREATE,OnCreate) //【三】、ON_MESSAGE第一个参数,要自己处理的消息ID,参2:哪个函数处理这个消息。 END_MESSAGE_MAP() LRESULT CMyFrameWnd::OnCreate(WPARAM wParam, LPARAM lParam) //【五】消息处理函数。 { AfxMessageBox("WM_CREATE消息被处理"); return 0; } //---------------------------------未变部分----------------------------------------------------// class CMyWinApp:public CWinApp //②在定义一个自己的【应用程序类】,公有派生自【CWinApp类】 { public: CMyWinApp();//③两个成员函数,一个是构造函数。什么也不写 virtual BOOL InitInstance();//④重写一个【父类】,的虚函数。 }; CMyWinApp::CMyWinApp() { } BOOL CMyWinApp::InitInstance() //声明与实现可以分开规范,声明都放头文件,实现单独。 { CMyFrameWnd *pFrame=new CMyFrameWnd; //⑤new一个,自己写的框架类的对象,CMyFrameWnd的对象。 pFrame->Create(NULL,"MFC base"); //⑥然后,用这个对象pFrame,调用【框架类】的成员函数creat。 m_pMainWnd=pFrame; //简单的【赋值语句】。 pFrame->ShowWindow(SW_SHOW); //调用showwindow。 pFrame->UpdateWindow(); //调用updatewindos。 return true; } //5、创建一个程序类对象(触发点): CMyWinApp theApp; //⑦定义一个,全局的,CMyWinApp的对象。程序各处都会调用它。 //----------------------------------------------------------------------------------------//
展开消息处理的宏
直接替换后,可运行,手动履行编译器职责
//直接替换后,可运行,手动履行编译器职责 //----------------------↓DECLARE_MESSAGE_MAP展开的内容↓------------------------------------------------// //①DECLARE_MESSAGE_MAP() 【声明宏】展开后。 protected: \ static const AFX_MSGMAP* PASCAL GetThisMessageMap(); \ virtual const AFX_MSGMAP* GetMessageMap() const; \ //----------------------↓BEGIN_MESSAGE_MAP展开的内容↓------------------------------------------------// //②BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd) 【带参宏】记得[参替换]。[theclass]的地方换本类类名,base换基类。 PTM_WARNING_DISABLE \ const AFX_MSGMAP* CMyFrameWnd::GetMessageMap() const \ { return GetThisMessageMap(); } \ const AFX_MSGMAP* PASCAL CMyFrameWnd::GetThisMessageMap() \ { \ typedef CMyFrameWnd ThisClass; \ //typedef起别名,可以屏蔽,并替换别名。简化。 typedef CFrameWnd TheBaseClass; \ __pragma(warning(push)) \ __pragma(warning(disable: 4640)) /* message maps can only be called by single threaded message pump */ \ static const AFX_MSGMAP_ENTRY _messageEntries[] = \ { //括号别乱删,完全严格替换。 //----------------------↓ON_MESSAGE展开的内容↓------------------------------------------------// //ON_MESSAGE(WM_CREATE,OnCrearte) 记得参替换,message替换消息id,memberFxn替换处理函数 { WM_CREATE, 0, 0, 0, AfxSig_lwl, \ (AFX_PMSG)(AFX_PMSGW) \ (static_cast< LRESULT (AFX_MSG_CALL CWnd::*)(WPARAM, LPARAM) > \ (OnCrearte)) }, //----------------------↓END_MESSAGE展开的内容↓------------------------------------------------// //③END_MESSAGE_MAP() {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \ }; \ __pragma(warning(pop)) \ static const AFX_MSGMAP messageMap = { &TheBaseClass::GetThisMessageMap, &_messageEntries[0] }; \ return &messageMap; \ } \ PTM_WARNING_RESTORE //加上一头,PTM_WARNING_DISABLE,这两个宏,可不加取地址符。不加宏OnCrearte要加&取地址符。 //类内,函数名不是函数地址。
消息映射机制--原理--内部走的路线
分析展开宏的,代码。
类内的声明宏,声明了一个【静态函数】和一个【虚函数】,两个函数。
类外的定义宏,实现这两个【静态函数】和【虚函数】。
故要成对出现。
GetThisMessageMap内部,【静态数组】【静态变量】这两个东西。
#include <afxwin.h> //【一】【二】【三】【四】的顺序 class CMyFrameWnd :public CFrameWnd //①定义一个自己的【框架类】,【公有派生】自[框架类CFrameWnd] { //DECLARE_MESSAGE_MAP() //【一】、类内声明宏 protected: \ static const AFX_MSGMAP* PASCAL GetThisMessageMap(); \ virtual const AFX_MSGMAP* GetMessageMap() const; \ public: LRESULT OnCreate(WPARAM wParam, LPARAM lParam);//【四】好习惯,类内声明函数,类外实现函数。 }; //---------------------------------↑相对上代码,未变部分↑----------------------------------------------------// //①BEGIN_MESSAGE_MAP(CMyFrameWnd,CFrameWnd) //【二】、类外实现,第一个参数【本类的类名】,第二个参数【父类的类名】。 const AFX_MSGMAP* CMyFrameWnd::GetMessageMap() const { return GetThisMessageMap(); } const AFX_MSGMAP* PASCAL CMyFrameWnd::GetThisMessageMap() { //typedef起别名,可以屏蔽,并替换别名。简化。 __pragma(warning(push)) __pragma(warning(disable: 4640)) /* message maps can only be called by single threaded message pump */ static const AFX_MSGMAP_ENTRY _messageEntries[] = { //②ON_MESSAGE(WM_CREATE, OnCreate) //【三】、ON_MESSAGE第一个参数,要自己处理的消息ID,参2:哪个函数处理这个消息。 { WM_CREATE, 0, 0, 0, AfxSig_lwl, (AFX_PMSG)(AFX_PMSGW) (static_cast< LRESULT (AFX_MSG_CALL CWnd::*)(WPARAM, LPARAM) > (&OnCreate)) }, //③END_MESSAGE_MAP() {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0} }; __pragma(warning(pop)) static const AFX_MSGMAP messageMap = { &CFrameWnd::GetThisMessageMap, &_messageEntries[0] }; return &messageMap; } //---------------------------------↓相对上代码,未变部分↓----------------------------------------------------// LRESULT CMyFrameWnd::OnCreate(WPARAM wParam, LPARAM lParam) //【五】消息处理函数。 { AfxMessageBox("WM_CREATE消息被处理"); return 0; } class CMyWinApp :public CWinApp //②在定义一个自己的【应用程序类】,公有派生自【CWinApp类】 { public: CMyWinApp();//③两个成员函数,一个是构造函数。什么也不写 virtual BOOL InitInstance();//④重写一个【父类】,的虚函数。 }; CMyWinApp::CMyWinApp() { } BOOL CMyWinApp::InitInstance() //声明与实现可以分开规范,声明都放头文件,实现单独。 { CMyFrameWnd *pFrame = new CMyFrameWnd; //⑤new一个,自己写的框架类的对象,CMyFrameWnd的对象。 pFrame->Create(NULL, "MFC base"); //⑥然后,用这个对象pFrame,调用【框架类】的成员函数creat。 m_pMainWnd = pFrame; //简单的【赋值语句】。 pFrame->ShowWindow(SW_SHOW); //调用showwindow。 pFrame->UpdateWindow(); //调用updatewindos。 return true; } //5、创建一个程序类对象(触发点): CMyWinApp theApp; //⑦定义一个,全局的,CMyWinApp的对象。程序各处都会调用它。 //----------------------------------------------------------------------------------------//
结构体
参2:只有消息来自【控件】才有通知码。
参3:菜单项的ID、加速键ID、控件ID统称【命令ID】。
参4:可以命令ID如100-最后ID200,之间范围,统一处理。
第一个和最后一个,重点关注。
结构体AFX_MSGMAP
赋值时,解释,易理解。下一个图中。
参1: &CFrameWnd::GetThisMessageMap,,CFrameWnd是父类,父类的成员GetThisMessageMap,本类也有一个(来自声明宏展开)。
静态成员,只属于类,不属于对象,父类有是父类的,本类的是本类的。父类也有(也是父类声明宏展开一个[静态函数]和[虚函数])。
父类静态变量,也是两个成员,第二个是父类的静态数组]的首地址。第一个是,指向爷爷类的。
参2:存的是&_messageEntries[0],就是[静态数组]的首地址。
关注[静态变量]和[静态数组]
局部变量,静态的线程级生命周期。
形成了【链表】。单向链表,有了链表势必要,遍历链表。链表头,是自己这个类的【静态变量】。
声明宏和实现宏得到:一个【虚函数】和一个【静态函数】。
静态函数内部:一个[数组](关注第一个:消息ID和最后一个:处理函数)和一个[静态变量](成员1:指向父类的静态变量,成员2:指向数组)。
宏展开各部分作用
作用,
如何遍历,上节的链表,找到【消息处理函数】,及对应消息ID
辅助文件
OnCreate()WM_CREATE消息的处理函数,打断点,调用堆栈,查看调用关系,定位到AfxWndProc()再[打断点]。曾定位过此函数,通过【重写WndProc()处理消息】时。
前半段,与[处理WndProc()消息]相似。
//以WM_CREATE消息为例,捎带想着点WM_PAINT,WM_COMMAND(到了CWnd::OnWndMsg函数路线不一样) //WM_create消息比较特殊,简单。WM_COMMAND路线比较麻烦。WM_NOTIFY也会走一个别路线。WM_PAINT与WM_CREATE路线一样。 AfxWndProc(...) { CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);//根据传入的[窗口句柄],返回与hWnd绑定在一起的框架类对象地址(pFrame===pWnd) ... AfxCallWndProc(pWnd...)//参数pWnd就是===pFrame { pWnd->WindowProc(...)//函数内部this为pFrame等同于===pWnd *************这里开始与原来不同了。 //之前,重写时调子类的,这里回到自己代码。 //现在并没重写WndProc,这次调【基类的】,【基类的】wndproc会【遍历链表】。 { OnWndMsg(message...)//函数内部this为pFrame===pWnd { //message就是从窗口处理函数,一层层传入的。 if (message == WM_COMMAND)//如果消息是WM_COMMAND,要么跳转,要么return。WM_command走的另一个路线。 ... const AFX_MSGMAP* pMessageMap;pMessageMap = GetMessageMap();//先定义个指针变量,紧接着就给赋值了。getmessagemap()是宏展开的虚函数。 //GetMessageMap隐含this指针,是pFrame,调自己类展开的虚函数。单步步入就回到,自己展开消息宏的,那个代码里。 //进而进入展开的静态函数getthismessagemap()里,返回【静态变量】messagemap的[头节点]。 //这个pMessageMap就拿到了,链表的[头节点] //获取【本类自己宏展开】的[静态变量]的地址(链表头结点) ... const AFX_MSGMAP_ENTRY* lpEntry;//lpEntry后面会赋值,此处是来源。 for (; pMessageMap->pfnGetBaseMap != NULL; pMessageMap = (*pMessageMap->pfnGetBaseMap)())//该for循环,起始条件是:“pMessageMap already init'ed” //注释了,没写。即pMessageMap被初始化过(已达成,在上),终止条件:pMessageMap【静态变量】的第一个元素,是父类的静态变量,为空即没有下一个节点了。 //变化条件是:每循环一次,pMessageMap会变成,父类的【静态变量】地址(pfnGetBaseMap是【静态变量】的第一个元素,保存父类的【静态变量】头地址)。 //此for就是在遍历链表 { ... lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries,message, 0, 0));//“找东西”“到哪找”“找什么东西” //pMessageMap->lpEntries(上面赋值过)【静态变量】的第二个成员是【静态数组】的[首地址]。数组中找, //拿WM_CREATE消息,去[本类宏展开]【的代码】里,找。返回的!!是【整个元素的地址】。不是处理函数oncreate()的地址。 //先在【本类】【静态变量】的数组里找,没找到,还在for循环里,就到【父类】【静态变量】元素的数组里找 //如果找到返回找到的【数组】元素的地址,如果没找到返回NULL if(lpEntry != NULL )//判断返回来的信息,不为空。 { goto LDispatch;//如果找到,进入if,goto跳出for循环。 } } ... LDispatch: //找到了,跳到此LDispatch lpEntry->pfn; //CMyFrameWnd::OnCreate,pfn这是数组的最后一个元素,是ONCREATE()函数的地址。 //后面,调用CMyFrameWnd::OnCreate函数完成消息的处理,后面细节太琐碎,就不跟了。 //最后回到,自己写的OnCreate()消息处理函数了。 } } } }
WM_CREATE消息映射流程概览