首页HTML开始的地方

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消息映射流程概览

 

 

 

 

posted @ 2023-02-12 08:40  csnotes  阅读(80)  评论(0编辑  收藏  举报

这是页脚html