MFC内部运行来龙去脉追踪

1.全局对象theApp先于WinMain函数构造,而theApp是一个派生类的对象,故先调用基类CWinApp的构造函数,再调用派生类对象的构造函数。

  CWinApp的构造函数定义于APPCORE.CPP文件中

 

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
}

CWinApp构造函数主要完成this指针的赋值,将全局对象theApp的this指针赋值给全局唯一实例。该构造函数有一参数,我们在默认调用的时候却没有指定参数,其实这是因为有默认值,

追踪AFXWIN.h头文件查看CWinApp类的定义可知。

class CWinApp : public CWinThread
{
 DECLARE_DYNAMIC(CWinApp)
public:

// Constructor
 CWinApp(LPCTSTR lpszAppName = NULL);     // app name defaults to EXE name

   ......

接着调用我们自己派生类的构造函数,我们可以做一些数据成员的初始化操作。

CHelloMFCApp::CHelloMFCApp()
{
 // TODO: add construction code here,
 // Place all significant initialization in InitInstance
}

 

2.全局对象之后即是WinMain函数的调用,在MFC中对WinMain进行了封装。所以在文件中查找WinMain是找不到的。可以在源文件的class CAboutDlg : public CDialog这一行加上断点,

F5运行,程序停在了

extern "C" int WINAPI
_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
 LPTSTR lpCmdLine, int nCmdShow)
{
 // call shared/exported WinMain
 return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
}

代码处,而同时可以看到该代码位于APPMODUL.cpp中。说明程序运行时加载了该函数,有点像我们的WinMain函数。go to definition可以看到#define _tWinMain   WinMain,_tWinMain正是我们的WinMain。

而为什么是_tWinMain而不是WinMain执行,这主要是MFC做了手脚,在MFC开始运行时就运行_tWinMain函数。其中又调用了AfxWinMain函数。在MFC源代码中查找一下AfxWinMain,在WINMAIN.cpp中可以找到AfxWinMain的实现:

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

稍加整理,可看到AfxWinMain主要做些什么事:

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

在APPINIT.cpp中查看AfxWinInit的实现如下:

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

 // handle critical errors and avoid Windows message boxes
 SetErrorMode(SetErrorMode(0) |
  SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);

 // set resource handles
 AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
 pModuleState->m_hCurrentInstanceHandle = hInstance;
 pModuleState->m_hCurrentResourceHandle = hInstance;

 // fill in the initial state for the application
 CWinApp* pApp = AfxGetApp();
 if (pApp != NULL)
 {
  // Windows specific initialization (not done if no CWinApp)
  pApp->m_hInstance = hInstance;
  pApp->m_hPrevInstance = hPrevInstance;
  pApp->m_lpCmdLine = lpCmdLine;
  pApp->m_nCmdShow = nCmdShow;
  pApp->SetCurrentHandles();
 }

 // initialize thread specific data (for main thread)
 if (!afxContextIsDLL)
  AfxInitThread();

 return TRUE;
}

 

void AFXAPI AfxInitThread()
{
 if (!afxContextIsDLL)
 {
  // set message filter proc
  _AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
  ASSERT(pThreadState->m_hHookOldMsgFilter == NULL);
  pThreadState->m_hHookOldMsgFilter = ::SetWindowsHookEx(WH_MSGFILTER,
   _AfxMsgFilterHook, NULL, ::GetCurrentThreadId());

#ifndef _AFX_NO_CTL3D_SUPPORT
  // intialize CTL3D for this thread
  _AFX_CTL3D_STATE* pCtl3dState = _afxCtl3dState;
  if (pCtl3dState->m_pfnAutoSubclass != NULL)
   (*pCtl3dState->m_pfnAutoSubclass)(AfxGetInstanceHandle());

  // allocate thread local _AFX_CTL3D_THREAD just for automatic termination
  _AFX_CTL3D_THREAD* pTemp = _afxCtl3dThread;
  pTemp;  // avoid unused warning
#endif
 }
}

AfxInitThread函数利用钩子函数将消息映射机制引入MFC的Message Map中,然后再回到默认的DefWindowProc。而AfxWinInit也主要做些初始化工作,完成一些初始细节配置。

之后是pApp->Application()调用了,由于派生类没有改写,故调用基类CWinApp::Application(),回到APPCORE.CPP文件:

BOOL CWinApp::InitApplication()
{
 if (CDocManager::pStaticDocManager != NULL)
 {
  if (m_pDocManager == NULL)
   m_pDocManager = CDocManager::pStaticDocManager;
  CDocManager::pStaticDocManager = NULL;
 }

 if (m_pDocManager != NULL)
  m_pDocManager->AddDocTemplate(NULL);
 else
  CDocManager::bStaticInit = FALSE;

 return TRUE;
}

可以看到主要做些内部管理。

然后是pApp->InitInstance()调用,派生类进行了改写,所以调用的是派生类的InitInstance(),如下:

BOOL CHelloMFCApp::InitInstance()
{
 AfxEnableControlContainer();

 // Standard initialization
 // If you are not using these features and wish to reduce the size
 //  of your final executable, you should remove from the following
 //  the specific initialization routines you do not need.

#ifdef _AFXDLL
 Enable3dControls();   // Call this when using MFC in a shared DLL
#else
 Enable3dControlsStatic(); // Call this when linking to MFC statically
#endif

 // Change the registry key under which our settings are stored.
 // TODO: You should modify this string to be something appropriate
 // such as the name of your company or organization.
 SetRegistryKey(_T("Local AppWizard-Generated Applications"));

 LoadStdProfileSettings();  // Load standard INI file options (including MRU)

 // Register the application's document templates.  Document templates
 //  serve as the connection between documents, frame windows and views.

 CMultiDocTemplate* pDocTemplate;
 pDocTemplate = new CMultiDocTemplate(
  IDR_HELLOMTYPE,
  RUNTIME_CLASS(CHelloMFCDoc),
  RUNTIME_CLASS(CChildFrame), // custom MDI child frame
  RUNTIME_CLASS(CHelloMFCView));
 AddDocTemplate(pDocTemplate);

 // create main MDI Frame window
 CMainFrame* pMainFrame = new CMainFrame;
 if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
  return FALSE;
 m_pMainWnd = pMainFrame;

 // Parse command line for standard shell commands, DDE, file open
 CCommandLineInfo cmdInfo;
 ParseCommandLine(cmdInfo);

 // Dispatch commands specified on the command line
 if (!ProcessShellCommand(cmdInfo))
  return FALSE;

 // The main window has been initialized, so show and update it.
 pMainFrame->ShowWindow(m_nCmdShow);
 pMainFrame->UpdateWindow();

 return TRUE;
}

在派生类的InitInstance函数中产生了一个CMainFrame窗口对象,故先调用基类CMDIFrameWnd的构造函数,CMDIFrameWnd的基类是CFrameWnd,故调用CFrameWnd的构造函数,在WinFrm.cpp文件中

CFrameWnd::CFrameWnd()
{
 ASSERT(m_hWnd == NULL);

 m_nWindow = -1;                 // unknown window ID
 m_bAutoMenuEnable = TRUE;       // auto enable on by default
 m_lpfnCloseProc = NULL;
 m_hMenuDefault = NULL;
 m_hAccelTable = NULL;
 m_nIDHelp = 0;
 m_nIDTracking = 0;
 m_nIDLastMessage = 0;
 m_pViewActive = NULL;

 m_cModalStack = 0;              // initialize modality support
 m_phWndDisable = NULL;
 m_pNotifyHook = NULL;
 m_hMenuAlt = NULL;
 m_nIdleFlags = 0;               // no idle work at start
 m_rectBorder.SetRectEmpty();

 m_bHelpMode = HELP_INACTIVE;    // not in Shift+F1 help mode
 m_dwPromptContext = 0;

 m_pNextFrameWnd = NULL;         // not in list yet

 m_bInRecalcLayout = FALSE;
 m_pFloatingFrameClass = NULL;
 m_nShowDelay = -1;              // no delay pending

 AddFrameWnd();
}

一直追踪到基类CWnd,CCmdTarget,CObject都没有发现产生窗口(Create)的操作,当然在产生窗口之前首先要注册窗口。其实Create的调用是在pMainFrame->LoadFrame(IDR_MAINFRAME)

中完成的。即CFrameWnd::LoadFrame()

BOOL CFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle,
 CWnd* pParentWnd, CCreateContext* pContext)
{
 // only do this once
 ASSERT_VALID_IDR(nIDResource);
 ASSERT(m_nIDHelp == 0 || m_nIDHelp == nIDResource);

 m_nIDHelp = nIDResource;    // ID for help context (+HID_BASE_RESOURCE)

 CString strFullString;
 if (strFullString.LoadString(nIDResource))
  AfxExtractSubString(m_strTitle, strFullString, 0);    // first sub-string

 VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));

 // attempt to create the window
 LPCTSTR lpszClass = GetIconWndClass(dwDefaultStyle, nIDResource);
 LPCTSTR lpszTitle = m_strTitle;
 if (!Create(lpszClass, lpszTitle, dwDefaultStyle, rectDefault,
   pParentWnd, MAKEINTRESOURCE(nIDResource), 0L, pContext))

 {
  return FALSE;   // will self destruct on failure normally
 }

 // save the default menu handle
 ASSERT(m_hWnd != NULL);
 m_hMenuDefault = ::GetMenu(m_hWnd);

 // load accelerator resource
 LoadAccelTable(MAKEINTRESOURCE(nIDResource));

 if (pContext == NULL)   // send initial update
  SendMessageToDescendants(WM_INITIALUPDATE, 0, 0, TRUE, TRUE);

 return TRUE;
}

可以看到在LoadFrame中主要做了2件事,第一,注册窗口。第二,创建窗口。先看AfxDeferRegisterClass(),它是一个全局函数,在文件AFXIMPL.H中可以看到

#define AfxDeferRegisterClass(fClass) AfxEndDeferRegisterClass(fClass)

在WINCORE.CPP中可以追踪到AfxEndDeferRegisterClass(fClass)如下:

BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister)
{
 // mask off all classes that are already registered
 AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
 fToRegister &= ~pModuleState->m_fRegisteredClasses;
 if (fToRegister == 0)
  return TRUE;

 LONG fRegisteredClasses = 0;

 // common initialization
 WNDCLASS wndcls;
 memset(&wndcls, 0, sizeof(WNDCLASS));   // start with NULL defaults
 wndcls.lpfnWndProc = DefWindowProc;
 wndcls.hInstance = AfxGetInstanceHandle();
 wndcls.hCursor = afxData.hcurArrow;

 INITCOMMONCONTROLSEX init;
 init.dwSize = sizeof(init);

 // work to register classes as specified by fToRegister, populate fRegisteredClasses as we go
 if (fToRegister & AFX_WND_REG)
 {
  // Child windows - no brush, no icon, safest default class styles
  wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
  wndcls.lpszClassName = _afxWnd;
  if (AfxRegisterClass(&wndcls))
   fRegisteredClasses |= AFX_WND_REG;
 }
 if (fToRegister & AFX_WNDOLECONTROL_REG)
 {
  // OLE Control windows - use parent DC for speed
  wndcls.style |= CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
  wndcls.lpszClassName = _afxWndOleControl;
  if (AfxRegisterClass(&wndcls))
   fRegisteredClasses |= AFX_WNDOLECONTROL_REG;
 }
 if (fToRegister & AFX_WNDCONTROLBAR_REG)
 {
  // Control bar windows
  wndcls.style = 0;   // control bars don't handle double click
  wndcls.lpszClassName = _afxWndControlBar;
  wndcls.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
  if (AfxRegisterClass(&wndcls))
   fRegisteredClasses |= AFX_WNDCONTROLBAR_REG;
 }
 if (fToRegister & AFX_WNDMDIFRAME_REG)
 {
  // MDI Frame window (also used for splitter window)
  wndcls.style = CS_DBLCLKS;
  wndcls.hbrBackground = NULL;
  if (_AfxRegisterWithIcon(&wndcls, _afxWndMDIFrame, AFX_IDI_STD_MDIFRAME))
   fRegisteredClasses |= AFX_WNDMDIFRAME_REG;
 }
 if (fToRegister & AFX_WNDFRAMEORVIEW_REG)
 {
  // SDI Frame or MDI Child windows or views - normal colors
  wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
  wndcls.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
  if (_AfxRegisterWithIcon(&wndcls, _afxWndFrameOrView, AFX_IDI_STD_FRAME))
   fRegisteredClasses |= AFX_WNDFRAMEORVIEW_REG;
 }
 if (fToRegister & AFX_WNDCOMMCTLS_REG)
 {
  // this flag is compatible with the old InitCommonControls() API
  init.dwICC = ICC_WIN95_CLASSES;
  fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WIN95CTLS_MASK);
  fToRegister &= ~AFX_WIN95CTLS_MASK;
 }
 if (fToRegister & AFX_WNDCOMMCTL_UPDOWN_REG)
 {
  init.dwICC = ICC_UPDOWN_CLASS;
  fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_UPDOWN_REG);
 }
 if (fToRegister & AFX_WNDCOMMCTL_TREEVIEW_REG)
 {
  init.dwICC = ICC_TREEVIEW_CLASSES;
  fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_TREEVIEW_REG);
 }
 if (fToRegister & AFX_WNDCOMMCTL_TAB_REG)
 {
  init.dwICC = ICC_TAB_CLASSES;
  fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_TAB_REG);
 }
 if (fToRegister & AFX_WNDCOMMCTL_PROGRESS_REG)
 {
  init.dwICC = ICC_PROGRESS_CLASS;
  fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_PROGRESS_REG);
 }
 if (fToRegister & AFX_WNDCOMMCTL_LISTVIEW_REG)
 {
  init.dwICC = ICC_LISTVIEW_CLASSES;
  fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_LISTVIEW_REG);
 }
 if (fToRegister & AFX_WNDCOMMCTL_HOTKEY_REG)
 {
  init.dwICC = ICC_HOTKEY_CLASS;
  fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_HOTKEY_REG);
 }
 if (fToRegister & AFX_WNDCOMMCTL_BAR_REG)
 {
  init.dwICC = ICC_BAR_CLASSES;
  fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_BAR_REG);
 }
 if (fToRegister & AFX_WNDCOMMCTL_ANIMATE_REG)
 {
  init.dwICC = ICC_ANIMATE_CLASS;
  fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_ANIMATE_REG);
 }
 if (fToRegister & AFX_WNDCOMMCTL_INTERNET_REG)
 {
  init.dwICC = ICC_INTERNET_CLASSES;
  fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_INTERNET_REG);
 }
 if (fToRegister & AFX_WNDCOMMCTL_COOL_REG)
 {
  init.dwICC = ICC_COOL_CLASSES;
  fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_COOL_REG);
 }
 if (fToRegister & AFX_WNDCOMMCTL_USEREX_REG)
 {
  init.dwICC = ICC_USEREX_CLASSES;
  fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_USEREX_REG);
 }
 if (fToRegister & AFX_WNDCOMMCTL_DATE_REG)
 {
  init.dwICC = ICC_DATE_CLASSES;
  fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_DATE_REG);
 }

 // save new state of registered controls
 pModuleState->m_fRegisteredClasses |= fRegisteredClasses;

 // special case for all common controls registered, turn on AFX_WNDCOMMCTLS_REG
 if ((pModuleState->m_fRegisteredClasses & AFX_WIN95CTLS_MASK) == AFX_WIN95CTLS_MASK)
 {
  pModuleState->m_fRegisteredClasses |= AFX_WNDCOMMCTLS_REG;
  fRegisteredClasses |= AFX_WNDCOMMCTLS_REG;
 }

 // must have registered at least as mamy classes as requested
 return (fToRegister & fRegisteredClasses) == fToRegister;
}

至此可以看到MFC默认的注册了很多窗口类,而其中调用了AfxRegisterClass函数。而AfxRegisterClass函数实现如下:

BOOL AFXAPI AfxRegisterClass(WNDCLASS* lpWndClass)
{
 WNDCLASS wndcls;
 if (GetClassInfo(lpWndClass->hInstance, lpWndClass->lpszClassName,
  &wndcls))
 {
  // class already registered
  return TRUE;
 }

 if (!::RegisterClass(lpWndClass))
 {
  TRACE1("Can't register window class named %s\n",
   lpWndClass->lpszClassName);
  return FALSE;
 }

 if (afxContextIsDLL)
 {
  AfxLockGlobals(CRIT_REGCLASSLIST);
  TRY
  {
   // class registered successfully, add to registered list
   AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
   LPTSTR lpszUnregisterList = pModuleState->m_szUnregisterList;
   // the buffer is of fixed size -- ensure that it does not overflow
   ASSERT(lstrlen(lpszUnregisterList) + 1 +
    lstrlen(lpWndClass->lpszClassName) + 1 <
    _countof(pModuleState->m_szUnregisterList));
   // append classname + newline to m_szUnregisterList
   lstrcat(lpszUnregisterList, lpWndClass->lpszClassName);
   TCHAR szTemp[2];
   szTemp[0] = '\n';
   szTemp[1] = '\0';
   lstrcat(lpszUnregisterList, szTemp);
  }
  CATCH_ALL(e)
  {
   AfxUnlockGlobals(CRIT_REGCLASSLIST);
   THROW_LAST();
   // Note: DELETE_EXCEPTION not required.
  }
  END_CATCH_ALL
  AfxUnlockGlobals(CRIT_REGCLASSLIST);
 }

 return TRUE;
}

可以看到最终调用Windows API函数实现窗口类的注册。

再次回到CFrameWnd::LoadFrame()函数,接下来就是产生窗口的操作了。LoadFrame中的Create调用依据多态性,应调用派生类的Create函数,由于派生类没有改写,此处调用CFrameWnd的Create函数。如下(在WINFRM.CPP中):

BOOL CFrameWnd::Create(LPCTSTR lpszClassName,
 LPCTSTR lpszWindowName,
 DWORD dwStyle,
 const RECT& rect,
 CWnd* pParentWnd,
 LPCTSTR lpszMenuName,
 DWORD dwExStyle,
 CCreateContext* pContext)
{
 HMENU hMenu = NULL;
 if (lpszMenuName != NULL)
 {
  // load in a menu that will get destroyed when window gets destroyed
  HINSTANCE hInst = AfxFindResourceHandle(lpszMenuName, RT_MENU);
  if ((hMenu = ::LoadMenu(hInst, lpszMenuName)) == NULL)
  {
   TRACE0("Warning: failed to load menu for CFrameWnd.\n");
   PostNcDestroy();            // perhaps delete the C++ object
   return FALSE;
  }
 }

 m_strTitle = lpszWindowName;    // save title for later

 if (!CreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle,
  rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
  pParentWnd->GetSafeHwnd(), hMenu, (LPVOID)pContext))

 {
  TRACE0("Warning: failed to create CFrameWnd.\n");
  if (hMenu != NULL)
   DestroyMenu(hMenu);
  return FALSE;
 }

 return TRUE;
}

后者又调用了CreateEx函数,CWnd有CreateEx函数,而CFrameWnd函数并没有改写它,故调用CWnd类的CreateEx函数,在WINCORE.cpp中实现如下:

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;

 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);//Windows API完成窗口类的一些额外风格设置

#ifdef _DEBUG
 if (hWnd == NULL)
 {
  TRACE1("Warning: Window creation failed: GetLastError returns 0x%8.8X\n",
   GetLastError());
 }
#endif

 if (!AfxUnhookWindowCreate())
  PostNcDestroy();        // cleanup if CreateWindowEx fails too soon

 if (hWnd == NULL)
  return FALSE;
 ASSERT(hWnd == m_hWnd); // should have been set in send msg hook
 return TRUE;
}

该函数先是调用PreCreateWindow函数,依据多态性,先调用派生类,接着是CMDIFrameWnd,然后是CFrameWnd的PreCreateWindow函数

在WINFRM.CPP中如下:

BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs)
{
 if (cs.lpszClass == NULL)
 {
  VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));
  cs.lpszClass = _afxWndFrameOrView;  // COLOR_WINDOW background
 }

 if ((cs.style & FWS_ADDTOTITLE) && afxData.bWin4)
  cs.style |= FWS_PREFIXTITLE;

 if (afxData.bWin4)
  cs.dwExStyle |= WS_EX_CLIENTEDGE;

 return TRUE;
}

沿着AfxDeferRegisterClass追踪下去,和上面的相同,都为MFC注册窗口类。

追踪完毕,回到开始的InitInstance函数中,接下来就是ShowWindow、UpdateWindow了。UpdateWindow会发出WM_PAINT消息。

再回到AfxWinMain()函数就是调用pApp->Run()函数了。因为派生类没有改写所以调用CWinApp::Run()函数,在APPCORE.CPP中实现如下:

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

而CWinThread::Run实现如下,在文件THRDCORE.CPP中:

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
}

而CWinThread::PumpMessage实现如下:

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

 if (!::GetMessage(&m_msgCur, NULL, NULL, NULL))
 {
#ifdef _DEBUG
  if (afxTraceFlags & traceAppMsg)
   TRACE0("CWinThread::PumpMessage - Received WM_QUIT.\n");
  m_nDisablePumpCount++; // application must die
   // Note: prevents calling message loop things in 'ExitInstance'
   // will never be decremented
#endif
  return FALSE;
 }

#ifdef _DEBUG
 if (m_nDisablePumpCount != 0)
 {
  TRACE0("Error: CWinThread::PumpMessage called when not permitted.\n");
  ASSERT(FALSE);
 }
#endif

#ifdef _DEBUG
 if (afxTraceFlags & traceAppMsg)
  _AfxTraceMsg(_T("PumpMessage"), &m_msgCur);
#endif

 // process this message

 if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur))
 {
  ::TranslateMessage(&m_msgCur);
  ::DispatchMessage(&m_msgCur);
 }
 return TRUE;
}

可以看到都是调用底层Windows API实现。程序退出时回到AfxWinInit最后调用AfxWinTerm终止程序。

 

而消息映射的实现是利用钩子函数在AfxWndProc中实现的,最后没有处理的消息都用DefWindowProc函数处理

MFC内部运行来龙去脉追踪_第1张图片

MFC内部运行来龙去脉追踪_第2张图片

MFC内部运行来龙去脉追踪_第3张图片

MFC内部运行来龙去脉追踪_第4张图片

  the end

你可能感兴趣的:(mfc)