zoukankan      html  css  js  c++  java
  • 深入浅出win32多线程之mfc(2)

    4. MFC线程、消息队列与MFC程序的"生死因果"

      分析MFC程序的主线程启动及消息队列处理的过程将有助于我们进一步理解UI线程与消息队列的关系,为此我们需要简单地叙述一下MFC程序的"生死因果"(侯捷:《深入浅出MFC》)。

      使用VC++ 6.0的向导完成一个最简单的单文档架构MFC应用程序MFCThread:

      (1) 输入MFC EXE工程名MFCThread;

      (2) 选择单文档架构,不支持Document/View结构;

      (3) ActiveX、3D container等其他选项都选择无。

      我们来分析这个工程。下面是产生的核心源代码: MFCThread.h 文件

    class CMFCThreadApp : public CWinApp
    {
     public:
      CMFCThreadApp();

      // Overrides
      // ClassWizard generated virtual function overrides
      //{{AFX_VIRTUAL(CMFCThreadApp)
       public:
        virtual BOOL InitInstance();
      //}}AFX_VIRTUAL

      // Implementation

     public:
      //{{AFX_MSG(CMFCThreadApp)
       afx_msg void OnAppAbout();
       // NOTE - the ClassWizard will add and remove member functions here.
       // DO NOT EDIT what you see in these blocks of generated code !
      //}}AFX_MSG
     DECLARE_MESSAGE_MAP()
    };

    MFCThread.cpp文件

    CMFCThreadApp theApp;

    /////////////////////////////////////////////////////////////////////////////
    // CMFCThreadApp initialization

    BOOL CMFCThreadApp::InitInstance()
    {
     …
     CMainFrame* pFrame = new CMainFrame;
     m_pMainWnd = pFrame;

     // create and load the frame with its resources
     pFrame->LoadFrame(IDR_MAINFRAME,WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE, NULL,NULL);
     // The one and only window has been initialized, so show and update it.
     pFrame->ShowWindow(SW_SHOW);
     pFrame->UpdateWindow();

     return TRUE;
    }

    MainFrm.h文件

    #include "ChildView.h"

    class CMainFrame : public CFrameWnd
    {
     public:
      CMainFrame();
     protected:
      DECLARE_DYNAMIC(CMainFrame)

      // Attributes
     public:

      // Operations
     public:
      // Overrides
      // ClassWizard generated virtual function overrides
      //{{AFX_VIRTUAL(CMainFrame)
       virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
       virtual BOOL OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo);
      //}}AFX_VIRTUAL

      // Implementation
     public:
      virtual ~CMainFrame();
      #ifdef _DEBUG
       virtual void AssertValid() const;
       virtual void Dump(CDumpContext& dc) const;
      #endif
      CChildView m_wndView;

      // Generated message map functions
     protected:
     //{{AFX_MSG(CMainFrame)
      afx_msg void OnSetFocus(CWnd *pOldWnd);
      // NOTE - the ClassWizard will add and remove member functions here.
      // DO NOT EDIT what you see in these blocks of generated code!
     //}}AFX_MSG
     DECLARE_MESSAGE_MAP()
    };

    MainFrm.cpp文件

    IMPLEMENT_DYNAMIC(CMainFrame, CFrameWnd)

    BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
     //{{AFX_MSG_MAP(CMainFrame)
      // NOTE - the ClassWizard will add and remove mapping macros here.
      // DO NOT EDIT what you see in these blocks of generated code !
      ON_WM_SETFOCUS()
     //}}AFX_MSG_MAP
    END_MESSAGE_MAP()

    /////////////////////////////////////////////////////////////////////////////
    // CMainFrame construction/destruction

    CMainFrame::CMainFrame()
    {
     // TODO: add member initialization code here
    }

    CMainFrame::~CMainFrame()
    {}

    BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
    {
     if( !CFrameWnd::PreCreateWindow(cs) )
      return FALSE;
      // TODO: Modify the Window class or styles here by modifying
      // the CREATESTRUCT cs

     cs.dwExStyle &= ~WS_EX_CLIENTEDGE;
     cs.lpszClass = AfxRegisterWndClass(0);
     return TRUE;
    }

    ChildView.h文件

    // CChildView window

    class CChildView : public CWnd
    {
     // Construction
     public:
      CChildView();

      // Attributes
     public:
      // Operations
     public:
      // Overrides
      // ClassWizard generated virtual function overrides
      //{{AFX_VIRTUAL(CChildView)
       protected:
        virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
      //}}AFX_VIRTUAL

      // Implementation
     public:
      virtual ~CChildView();

      // Generated message map functions
     protected:
      //{{AFX_MSG(CChildView)
       afx_msg void OnPaint();
      //}}AFX_MSG
     DECLARE_MESSAGE_MAP()
    };

    ChildView.cpp文件
    // CChildView

    CChildView::CChildView()
    {}

    CChildView::~CChildView()
    {}

    BEGIN_MESSAGE_MAP(CChildView,CWnd )
    //{{AFX_MSG_MAP(CChildView)
    ON_WM_PAINT()
    //}}AFX_MSG_MAP
    END_MESSAGE_MAP()

    /////////////////////////////////////////////////////////////////////////////
    // CChildView message handlers

    BOOL CChildView::PreCreateWindow(CREATESTRUCT& cs)
    {
     if (!CWnd::PreCreateWindow(cs))
      return FALSE;

     cs.dwExStyle |= WS_EX_CLIENTEDGE;
     cs.style &= ~WS_BORDER;
     cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS,::LoadCursor(NULL, IDC_ARROW),
    HBRUSH(COLOR_WINDOW+1),NULL);

     return TRUE;
    }

    void CChildView::OnPaint()
    {
     CPaintDC dc(this); // device context for painting

     // TODO: Add your message handler code here
     // Do not call CWnd::OnPaint() for painting messages
    }

    文件MFCThread.h和MFCThread.cpp定义和实现的类CMFCThreadApp继承自CWinApp类,而CWinApp类又继承自CWinThread类(CWinThread类又继承自CCmdTarget类),所以CMFCThread本质上是一个MFC线程类,下图给出了相关的类层次结构:
    我们提取CWinApp类原型的一部分:

    class CWinApp : public CWinThread
    {
     DECLARE_DYNAMIC(CWinApp)
     public:
      // Constructor
      CWinApp(LPCTSTR lpszAppName = NULL);// default app name
      // Attributes
      // Startup args (do not change)
      HINSTANCE m_hInstance;
      HINSTANCE m_hPrevInstance;
      LPTSTR m_lpCmdLine;
      int m_nCmdShow;
      // Running args (can be changed in InitInstance)
      LPCTSTR m_pszAppName; // human readable name
      LPCTSTR m_pszExeName; // executable name (no spaces)
      LPCTSTR m_pszHelpFilePath; // default based on module path
      LPCTSTR m_pszProfileName; // default based on app name

      // Overridables
      virtual BOOL InitApplication();
      virtual BOOL InitInstance();
      virtual int ExitInstance(); // return app exit code
      virtual int Run();
      virtual BOOL OnIdle(LONG lCount); // return TRUE if more idle processing
      virtual LRESULT ProcessWndProcException(CException* e,const MSG* pMsg);

     public:
      virtual ~CWinApp();
     protected:
      DECLARE_MESSAGE_MAP()
    };

    SDK程序的WinMain 所完成的工作现在由CWinApp 的三个函数完成:

    virtual BOOL InitApplication();
    virtual BOOL InitInstance();
    virtual int Run();


      "CMFCThreadApp theApp;"语句定义的全局变量theApp是整个程式的application object,每一个MFC 应用程序都有一个。当我们执行MFCThread程序的时候,这个全局变量被构造。theApp 配置完成后,WinMain开始执行。但是程序中并没有WinMain的代码,它在哪里呢?原来MFC早已准备好并由Linker直接加到应用程序代码中的,其原型为(存在于VC++6.0安装目录下提供的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);
    }


      其中调用的AfxWinMain如下(存在于VC++6.0安装目录下提供的WINMAIN.CPP文件中):

    int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    LPTSTR lpCmdLine, int nCmdShow)
    {
     ASSERT(hPrevInstance == NULL);

     int nReturnCode = -1;
     CWinThread* pThread = AfxGetThread();
     CWinApp* pApp = AfxGetApp();

     // AFX internal initialization
     if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))
      goto InitFailure;

     // App global initializations (rare)
     if (pApp != NULL && !pApp->InitApplication())
      goto InitFailure;

     // Perform specific initializations
     if (!pThread->InitInstance())
     {
      if (pThread->m_pMainWnd != NULL)
      {
       TRACE0("Warning: Destroying non-NULL m_pMainWnd\n");
       pThread->m_pMainWnd->DestroyWindow();
      }
      nReturnCode = pThread->ExitInstance();
      goto InitFailure;
     }
     nReturnCode = pThread->Run();

     InitFailure:
     #ifdef _DEBUG
      // Check for missing AfxLockTempMap calls
      if (AfxGetModuleThreadState()->m_nTempMapLock != 0)
      {
       TRACE1("Warning: Temp map lock count non-zero (%ld).\n",
    AfxGetModuleThreadState()->m_nTempMapLock);
      }
      AfxLockTempMaps();
      AfxUnlockTempMaps(-1);
     #endif

     AfxWinTerm();
     return nReturnCode;
    }


      我们提取主干,实际上,这个函数做的事情主要是:

    CWinThread* pThread = AfxGetThread();
    CWinApp* pApp = AfxGetApp();
    AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow)
    pApp->InitApplication()
    pThread->InitInstance()
    pThread->Run();


      其中,InitApplication 是注册窗口类别的场所;InitInstance是产生窗口并显示窗口的场所;Run是提取并分派消息的场所。这样,MFC就同WIN32 SDK程序对应起来了。CWinThread::Run是程序生命的"活水源头"(侯捷:《深入浅出MFC》,函数存在于VC++ 6.0安装目录下提供的THRDCORE.CPP文件中):

    // main running routine until thread exits
    int CWinThread::Run()
    {
     ASSERT_VALID(this);

     // for tracking the idle time state
     BOOL bIdle = TRUE;
     LONG lIdleCount = 0;

     // acquire and dispatch messages until a WM_QUIT message is received.
     for (;;)
     {
      // phase1: check to see if we can do idle work
      while (bIdle && !::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE))
      {
       // call OnIdle while in bIdle state
       if (!OnIdle(lIdleCount++))
        bIdle = FALSE; // assume "no idle" state
      }

      // phase2: pump messages while available
      do
      {
       // pump message, but quit on WM_QUIT
       if (!PumpMessage())
        return ExitInstance();

       // reset "no idle" state after pumping "normal" message
       if (IsIdleMessage(&m_msgCur))
       {
        bIdle = TRUE;
        lIdleCount = 0;
       }

      } while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));
     }
     ASSERT(FALSE); // not reachable
    }


      其中的PumpMessage函数又对应于:

    /////////////////////////////////////////////////////////////////////////////
    // CWinThread implementation helpers

    BOOL CWinThread::PumpMessage()
    {
     ASSERT_VALID(this);

     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;
    }


      因此,忽略IDLE状态,整个RUN的执行提取主干就是:

    do {
     ::GetMessage(&msg,...);
     PreTranslateMessage{&msg);
     ::TranslateMessage(&msg);
     ::DispatchMessage(&msg);
     ...
    } while (::PeekMessage(...));


      由此,我们建立了MFC消息获取和派生机制与WIN32 SDK程序之间的对应关系。下面继续分析MFC消息的"绕行"过程。

      在MFC中,只要是CWnd 衍生类别,就可以拦下任何Windows消息。与窗口无关的MFC类别(例如CDocument 和CWinApp)如果也想处理消息,必须衍生自CCmdTarget,并且只可能收到WM_COMMAND消息。所有能进行MESSAGE_MAP的类都继承自CCmdTarget,如:


      MFC中MESSAGE_MAP的定义依赖于以下三个宏:

    DECLARE_MESSAGE_MAP()

    BEGIN_MESSAGE_MAP(
     theClass, //Specifies the name of the class whose message map this is
     baseClass //Specifies the name of the base class of theClass
    )

    END_MESSAGE_MAP()


      我们程序中涉及到的有:MFCThread.h、MainFrm.h、ChildView.h文件

    DECLARE_MESSAGE_MAP()
    MFCThread.cpp文件
    BEGIN_MESSAGE_MAP(CMFCThreadApp, CWinApp)
    //{{AFX_MSG_MAP(CMFCThreadApp)
    ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
    // NOTE - the ClassWizard will add and remove mapping macros here.
    // DO NOT EDIT what you see in these blocks of generated code!
    //}}AFX_MSG_MAP
    END_MESSAGE_MAP()
    MainFrm.cpp文件
    BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
    //{{AFX_MSG_MAP(CMainFrame)
    // NOTE - the ClassWizard will add and remove mapping macros here.
    // DO NOT EDIT what you see in these blocks of generated code !
    ON_WM_SETFOCUS()
    //}}AFX_MSG_MAP
    END_MESSAGE_MAP()
    ChildView.cpp文件
    BEGIN_MESSAGE_MAP(CChildView,CWnd )
    //{{AFX_MSG_MAP(CChildView)
    ON_WM_PAINT()
    //}}AFX_MSG_MAP
    END_MESSAGE_MAP()


      由这些宏,MFC建立了一个消息映射表(消息流动网),按照消息流动网匹配对应的消息处理函数,完成整个消息的"绕行"。

      看到这里相信你有这样的疑问:程序定义了CWinApp类的theApp全局变量,可是从来没有调用AfxBeginThread或theApp.CreateThread启动线程呀,theApp对应的线程是怎么启动的?

      答:MFC在这里用了很高明的一招。实际上,程序开始运行,第一个线程是由操作系统(OS)启动的,在CWinApp的构造函数里,MFC将theApp"对应"向了这个线程,具体的实现是这样的:

    CWinApp::CWinApp(LPCTSTR lpszAppName)
    {
     if (lpszAppName != NULL)
      m_pszAppName = _tcsdup(lpszAppName);
     else
      m_pszAppName = NULL;

     // initialize CWinThread state
     AFX_MODULE_STATE *pModuleState = _AFX_CMDTARGET_GETSTATE();
     AFX_MODULE_THREAD_STATE *pThreadState = pModuleState->m_thread;
     ASSERT(AfxGetThread() == NULL);
     pThreadState->m_pCurrentWinThread = this;
     ASSERT(AfxGetThread() == this);
     m_hThread = ::GetCurrentThread();
     m_nThreadID = ::GetCurrentThreadId();

     // initialize CWinApp state
     ASSERT(afxCurrentWinApp == NULL); // only one CWinApp object please
     pModuleState->m_pCurrentWinApp = this;
     ASSERT(AfxGetApp() == this);

     // in non-running state until WinMain
     m_hInstance = NULL;
     m_pszHelpFilePath = NULL;
     m_pszProfileName = NULL;
     m_pszRegistryKey = NULL;
     m_pszExeName = NULL;
     m_pRecentFileList = NULL;
     m_pDocManager = NULL;
     m_atomApp = m_atomSystemTopic = NULL; //微软懒鬼?或者他认为
     //这样连等含义更明确?
     m_lpCmdLine = NULL;
     m_pCmdInfo = NULL;

     // initialize wait cursor state
     m_nWaitCursorCount = 0;
     m_hcurWaitCursorRestore = NULL;

     // initialize current printer state
     m_hDevMode = NULL;
     m_hDevNames = NULL;
     m_nNumPreviewPages = 0; // not specified (defaults to 1)

     // initialize DAO state
     m_lpfnDaoTerm = NULL; // will be set if AfxDaoInit called

     // other initialization
     m_bHelpMode = FALSE;
     m_nSafetyPoolSize = 512; // default size
    }


      很显然,theApp成员变量都被赋予OS启动的这个当前线程相关的值,如代码:

    m_hThread = ::GetCurrentThread();//theApp的线程句柄等于当前线程句柄
    m_nThreadID = ::GetCurrentThreadId();//theApp的线程ID等于当前线程ID


      所以CWinApp类几乎只是为MFC程序的第一个线程量身定制的,它不需要也不能被AfxBeginThread或theApp.CreateThread"再次"启动。这就是CWinApp类和theApp全局变量的内涵!如果你要再增加一个UI线程,不要继承类CWinApp,而应继承类CWinThread。而参考第1节,由于我们一般以主线程(在MFC程序里实际上就是OS启动的第一个线程)处理所有窗口的消息,所以我们几乎没有再启动UI线程的需求!

  • 相关阅读:
    linux在线安装jdk,tomcat,Nginx
    SpringAOP的实现方式
    Spring整合Mybatis
    基本sql语句
    解决No module named 'sklearn.cross_validation'
    虚拟机中安装redhat8操作系统
    【Linux命令】tcpdump抓包工具
    【Nodejs】Linux系统搭建Nodejs
    【SSL证书配置】tomcat实现SSL证书访问
    【SSL证书配置】腾讯云申请ssl证书,nginx+tomcat配置ssl证书
  • 原文地址:https://www.cnblogs.com/lzjsky/p/1934835.html
Copyright © 2011-2022 走看看