MFC程序生与死

来源:互联网 发布:light room mac 编辑:程序博客网 时间:2024/05/01 11:46

MFC程序的启动与死亡顺序:

      1、创建Application object对象theApp
          程序一开始生产一个(且只有一个)Application
      object对象theApp,也即一个CWinApp对象,这个全局对象一产生,便执行其构造函数,因为并没有定义CMyWinApp构造函数,所以即执行CWinApp类的构造函数。该函数定义于APPCORE.CPP第75行,你可以自己搜出来啃一啃,因此,CWinApp之中的成员变量将因为theApp这个全局对象的诞生而获得配置与初值。
      2、WinMain登场
          用SDK编程序时,程序的入口点是WinMain函数,而在MFC程序里我们并没有看到WinMain函数,哦!~
      原来她是被隐藏在MFC代码里面了。当theApp配置完成后,WinMain登场,慢!细看程序,并没连到WinMain函数的代码啊!这个我也不知道,MFC早已准备好并由链接器直接加到应用程序代码中了,原来她在APPMODUL.CPP里面,好,我们就认为当theApp配置完成后,程序就转到APPMODUL.CPP来了。那执行什么呢?看看下面从APPMODUL.CPP摘出来的代码:
          extern C int WINAPI
          _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR
      lpCmdLine, int nCmdShow)
          {
              // call shared/exported WinMain
              return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
          }
          _tWinMain函数的“_t”是为了支持Unicode而准备的一个宏。
         
      _tWinMain函数返回值是AfxWinMain函数的返回值,AfxWinMain函数定义于WINMAIN.CPP第21行,稍加整理,去芜存菁,就可以看到这个“程序进入点”主要做些什么事:
          int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
      LPTSTR lpCmdLine, int nCmdShow)
          {
              int nReturnCode = -1;
              CWinApp* pApp = AfxGetApp();
              AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
              pApp->InitApplication();
              pApp->InitInstance()
              nReturnCode = pApp->Run();
              AfxWinTerm();
              return nReturnCode;
          }

          AfxGetApp()函数是取得CMyWinApp对象指针,故上面函数第6至8行相当于调用:
          CMyWinApp::InitApplication();
          CMyWinApp::InitInstance()
          CMyWinApp::Run();
          因而导致调用:
          CWinApp::InitApplication();  //因为 CMyWinApp 并没有改写 InitApplication
          CMyWinApp::InitInstance()    //因为 CMyWinApp 改写了 InitInstance
          CWinApp::Run();              //因为 CMyWinApp 并没有改写 Run
          用过SDK写程序的朋友,现在可能会发出会心的微笑。
      3、AfxWinInit——AFX内部初始化操作      
      AfxWinInit是继CWinApp构造函数之后的第一个操作,主要做的是AFX内部初始化操作,该函数定义于APPINIT.CPP第24行。

      4、执行CWinApp::InitApplication
          AfxWinInit之后的操作是pApp->InitApplication,我们已知道pApp指向CMyWinApp对象,当调用:
              pApp->InitApplication();
          相当于调用:
              CMyWinApp::InitApplication();
         
      但是你要知道,CMyWinApp继承自CWinApp,而InitApplication又是CWinApp的一个虚拟函数,我们并没有改写它(大部分情况下不需改写它),所以上述操作相当于调用:
              CWinApp::InitApplication();
         
      此函数定义于APPCORE.CPP第125行,你自己搜出来看吧!我就不搬出来了,里面的操作都是MFC为了内部管理而做的(其实我也看不懂,知道有这回事就好了)。
      5、执行CWinApp::InitInstance
          继InitApplication函数之后,AfxWinMain调用pApp->InitInstance。当程序调用:
              pApp->InitInstance();
          相当于调用:
              CMyWinApp::InitInstance();
         
      但是你要知道,CMyWinApp继承自CWinApp,而InitInstance又是CWinApp的一个虚拟函数。由于我们改写了它,所以上述操作就是调用我们自己(CMyWinApp)的这个InitInstance函数。
      6、CFrameWnd::Create产生主窗口(并先注册窗口类)
         
      现在已经来到CWinApp::InitInstance了,该函数先new一个CMyFrameWnd对象,从而产生主窗口。在创建CMyFrameWnd对之前,要先执行构造函数CMyFrameWnd::CMyFrameWnd(),该函数用Create函数产生窗口:
              CMyFrameWnd::CMyFrameWnd()
              {
                  Create(NULL, Hello MFC, WS_OVERLAPPEDWINDOW, rectDefault,
      NULL, MainMenu);
              }
          
      其中Create是CFrameWnd的成员函数,它将产生一个窗口,用过SDK编程序的朋友都知道,要创建主窗口时要先注册一个窗口类,规定窗口的属性等,但,这里使用哪一个窗口类呢?Create函数第一个参数(其它参数请参考MSDN或《深出浅出MFC》详解)指定窗口类设为NULL又是什么意思啊?意思是要以MFC内建的空中类产生一个标准的外框窗口,但,我们的程序一般都没有注册任何窗口类呀!噢,Create函数在产生窗口之前会引发窗口类的注册操作。
         
      让我们先挖出Create函数都做了些什么操作,Create函数定义于WINFRM.CPP的第538行(在此我就不把代码Copy过来了,你自己打开出来看吧),函数在562行调用CreateEx函数,由于CreateEx是CWnd的成员函数,而CFrameWnd是从CWnd继而来,故将调用CWnd::CreateEx。此函数定义于WINCORE.CPP第665行,下面是部分代码:
         BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,
                              LPCTSTR lpszWindowName, DWORD dwStyle,
                              int x, int y, int nWidth, int nHeight,
                       HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam)
          {
              // allow modification of several common create parameters
              CREATESTRUCT cs;
              cs.dwExStyle = dwExStyle;
              cs.lpszClass = lpszClassName;
              cs.lpszName = lpszWindowName;
              cs.style = dwStyle;
              cs.x = x;
              cs.y = y;
              cs.cx = nWidth;
              cs.cy = nHeight;
              cs.hwndParent = hWndParent;
              cs.hMenu = nIDorHMenu;
              cs.hInstance = AfxGetInstanceHandle();
              cs.lpCreateParams = lpParam;
          
      用过SDK编程序的朋友,看到上面代码应该有一点感觉了吧,函数中调用的PreCreateWindows是虚拟函数,在CWnd和CFrameWnd之中都有定义。由于this指针所指对象的缘故,这里应该调用的是CFrameWnd::PreCreateWindow。该函数定义于WINFRM.CPP第521行,以下是部分代码:
          BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs)
          {
              if (cs.lpszClass == NULL)
              {
                  VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));
                  cs.lpszClass = _afxWndFrameOrView;  // COLOR_WINDOW background
       }
              ...
          }
          其中AfxDeferRegisterClass是一个定义于AFXIMPL.H中的宏。该宏如下:
              #define AfxDeferRegisterClass(fClass)
      AfxEndDeferRegisterClass(fClass)
          注:这里有宏和《深入浅出MFC》的不一样,以上代码是从Visual C++ 6.0摘取。
         
      AfxEndDeferRegisterClass定义于WINCORE.CPP第3619行,该函数很复杂,主要是注册五个窗口类(哇!终于看到窗口类了,怎么用5个呢?我还不清楚),不同类的PreCreateWindow成员函数都是在窗口产生之前一刻被调用,准备用来注册窗口类。如果我们指定的窗口类是NULL,那么就使用系统默认类。从CWnd及其各个派生类的PreCreateWindow成员函数可以看出,整个Framework针对不同功能的窗口使用了哪些窗口类。
      7、窗口显示与更新
         
      CMyFrameWnd::CMyFrameWnd结束后,窗口已经诞生出来;程序流程又回到CMyWinApp::InitInstance,于是调用ShowWindow函数令窗口显示出来,并调用UpdateWindow函数令程序送出WM_PAINT消息。在SDK程序中,消息是通过窗口函数来处理,而现在窗口函数在哪里、又如何送到窗口函数手中呢?那要从CWinApp::Run说起了。
      8、执行CWinApp::Run——程序生命的活水源头
         
      在执行完CMyWinApp::InitInstance函数后,程序的脚步到了AfxWinMain函数的pApp->Run了,现在我们已知道这将执行CWinApp::Run函数,该函数定义于APPCORE.CPP第391行,下面是程序代码:
          int CWinApp::Run()
          {
              if (m_pMainWnd == NULL && AfxOleGetUserCtrl())
              {
                  // Not launched /Embedding or /Automation, but has no main
      window!
                  TRACE0(Warning: m_pMainWnd is NULL in CWinApp::Run - quitting
      application./n);
                  AfxPostQuitMessage(0);
              }
              return CWinThread::Run();
          }


              if(PreCreateWindow(cs))
       {
        PostNcDestroy();
        return FALSE;
       }
       AfxHookWindowCreate(this);
       HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass,
         cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,
         cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);
              ...
      }

 

      函数调用CWinThread::Run函数,该函数定义于THRDCORE.CPP第456行,在这里我就不Copy出来了。函数在第480行调用了PumpMessage函数,该函数定义于THRDCORE.CPP第810行,整理后的部分代码如下:
          BOOL CWinThread::PumpMessage()
          {
              if (!::GetMessage(&m_msgCur, NULL, NULL, NULL))
              {
                  return FALSE;
       }
              // process this message
       if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur))
              {
                  ::TranslateMessage(&m_msgCur);
                  ::DispatchMessage(&m_msgCur);
              }
              return TRUE;
          }

          
      该函数主要操作是将消息由::DispatchMessage送到窗口函数(CWnd::DefWindowProc)中,但程序一般没有提供任何窗口函数,但在AfxEndDeferRegisterClass中,在注册五种窗口类之前已经指定窗口函数为:
              wndcls.lpfnWndProc = DefWindowProc;
         
      虽然窗口函数被指定为DefWindowProc成员函数(CWnd::DefWindowProc),但事实上消息并不是被唧往该处,而是一个名为AfxWndProc的全局函数去。
      9、把消息与处理函数连接在一起——Message Map机制
          到此,主窗口已经产生,等待的就是各种消息了,然后调用相应的处理函数,然而消息和处理函数怎样连接在一起呢?MFC采用了Message
      Map机制(消息映射机制),提供给应用程序使用的“很方便的接口”的两组宏,其原理我还不大清楚,在这里也无法讲解,主要用法是:先在类声明中结合DECLARE_MESSAGE_MAP()给出处理函数,如:
          class CMyFrameWnd : public CFrameWnd
          {
          public:
              CMyFrameWnd();
              afx_msg void OnPaint();   // for WM_PAINT
              afx_msg void OnAbout();   // for WM_COMMAND (IDM_ABOUT)
              DECLARE_MESSAGE_MAP()
          }
          
      再在相应的.CPP文件的任何位置(当然不能在函数之内)使用BEBIN_MESSAGE_MAP()和END_MESSAGE_MAP()宏把相应的消息加入去,如:
          BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)
              ON_COMMAND(IDM_ABOUT, OnAbout)
              ON_WM_PAINT()
          END_MESSAGE_MAP()
         

为什么经过这样的宏之后,消息就会自动流往指定的函数去呢?谜底在于Message Map的结构设计。

 转自http://blog.163.com/shizhen_2001/blog/static/27026132006101013619682/
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 被吸血蠓咬了怎么办 股份公司大股东占用资金怎么办 抱小孩抱的胳膊疼怎么办 一个月好几个性伴侣怎么办 惹了势力大的人怎么办 给佛像换新法器旧的怎么办 cad填充图案拾取不了点怎么办 cad填充图案太密怎么办 桃木葫芦挂件裂口了怎么办 皮肤被太阳晒伤变黑怎么办 皮肤又粗又黑该怎么办 盆栽红薯长疯了怎么办 四叶草叶子发黄怎么办 美篇不想让别人看到怎么办 黑魂3暗灵入侵怎么办 问道手游账号忘了怎么办 问道手游密码忘了怎么办 毒蚂蚁咬了红肿怎么办 电脑开机后有电流声怎么办 小学生阅读方面不会总结怎么办 天龙八部手游转职装备怎么办 投简历没回应该怎么办? 开车开的腰疼怎么办 开车腰疼怎么办吃什么 58简历投错了怎么办 开了一天车腰疼怎么办 开车腰疼怎么办怎么解决好 球球大作战找不到主界面怎么办 在QQ上找不到主界面怎么办 mac地址被过滤了怎么办 续贷声明含非法字符怎么办 辞职老板不给结工资怎么办 辞职后老板不发工资怎么办 联想手机不自带系统升级怎么办 苹果6玩崩坏3卡怎么办 机票名字打错了怎么办? 安装软件后显示名字乱码怎么办 户口上民族错了怎么办 改名字后行驶证怎么办 眼角撞到了肿了怎么办 想不想修真邪气怎么办