MFC内部运行来龙去脉追踪

 

摘自:http://www.cnblogs.com/witxjp/archive/2010/04/21/1717564.html

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

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

  

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

   ......

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

 

 

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

F5运行,程序停在了

 

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

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

 

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

 

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

 

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

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

 

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

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

 

在派生类的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中实现如下:

 

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

 

而CWinThread::PumpMessage实现如下:

 

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

 

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

 

最后,本来要附上几张图片例子的,却不知如何在csdn上插入图片,杯具。也罢,放我163博客吧!

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值