深入浅出Win32多线程设计之MFC的多线程(II)

使用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线程的需求!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值