怎么样剖析MFC程序框架?
怎么样剖析MFC程序框架?
	MFC程序框架剖析
	作者:(ECNU)孟庆涛 Email:
	1:程序的“导火索”---theApp
	CjjjApp theApp;
	在声明对象的同时,调用其构造函数。按C++的语法,首先要调用其基类Cwinapp的构造函数. 这个文件主要用于应用程序的一些初始化操作。
	class CWinApp : public CWinThread
	{
	DECLARE_DYNAMIC(CWinApp)
	public:
	// Constructor
	CWinApp(LPCTSTR lpszAppName = NULL); 
	…………
	}
	
	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_hLangResourceDLL = 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_eHelpType = afxWinHelp;
	m_nSafetyPoolSize = 512; // default size
	}
	2:theApp之后的隐藏代码,由他控制整个程序的流程。
	_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	LPTSTR lpCmdLine, int nCmdShow)
	{
	// call shared/exported WinMain
	return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
	}
	
	其中有宏定义:#define _tWinMain wWinMain
	
	int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	LPTSTR lpCmdLine, int nCmdShow)
	{
	ASSERT(hPrevInstance == NULL);
	int nReturnCode = -1;
	CWinThread* pThread = AfxGetThread();;// CWinApp是从CWinThread派生的,
	CWinApp* pApp = AfxGetApp(); //实质上就是pThread==pApp
	// 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()) //注意多态性 virtual BOOL InitInstance();
	//又因为pThread==pApp,所以调用pApp-> InitInstance()
	{
	if (pThread->m_pMainWnd != NULL)
	{
	TRACE(traceAppMsg, 0, "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)
	{
	TRACE(traceAppMsg, 0, "Warning: Temp map lock count non-zero (%ld)./n",
	AfxGetModuleThreadState()->m_nTempMapLock);
	}
	AfxLockTempMaps();
	AfxUnlockTempMaps(-1);
	#endif
	AfxWinTerm();
	return nReturnCode;
	}
	由上面的程序可以看到几个很重要的函数 
	(1)AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))
	goto InitFailure;
	(2) pApp->InitApplication())
	(3) pThread->InitInstance()
	(4) pThread->Run()
	其中1,2 也是完成程序的一些初始化工作,4 主要是为了处理消息,3呢,很关键,我们运行时看到的窗口就是从这里产生。下面一一介绍
	3:程序自动产生的InitInstance()函数
	以下是自动生成的InitInstance()源程序:
	BOOL CjjjApp::InitInstance()
	{
	// 如果一个运行在 Windows XP 上的应用程序清单指定要
	// 使用 ComCtl32.dll 版本 6 或更高版本来启用可视化方式,
	//则需要 InitCommonControls()。否则,将无法创建窗口。
	InitCommonControls();
	CWinApp::InitInstance();
	// 初始化 OLE 库
	if (!AfxOleInit())
	{
	AfxMessageBox(IDP_OLE_INIT_FAILED);
	return FALSE;
	}
	AfxEnableControlContainer();
	// 标准初始化
	// 如果未使用这些功能并希望减小
	// 最终可执行文件的大小,则应移除下列
	// 不需要的特定初始化例程
	// 更改用于存储设置的注册表项
	// TODO: 应适当修改该字符串,
	// 例如修改为公司或组织名
	SetRegistryKey(_T("应用程序向导生成的本地应用程序"));
	LoadStdProfileSettings(4); // 加载标准 INI 文件选项(包括 MRU)
	// 注册应用程序的文档模板。文档模板
	// 将用作文档、框架窗口和视图之间的连接
	CMultiDocTemplate* pDocTemplate;
	pDocTemplate = new CMultiDocTemplate(IDR_jjjTYPE,
	RUNTIME_CLASS(CjjjDoc),
	RUNTIME_CLASS(CChildFrame), // 自定义 MDI 子框架
	RUNTIME_CLASS(CjjjView));
	if (!pDocTemplate)
	return FALSE;
	AddDocTemplate(pDocTemplate);
	// 创建主 MDI 框架窗口
	CMainFrame* pMainFrame = new CMainFrame;
	if (!pMainFrame || !pMainFrame->LoadFrame(IDR_MAINFRAME))
	return FALSE;
	m_pMainWnd = pMainFrame;
	// 仅当具有后缀时才调用 DragAcceptFiles
	// 在 MDI 应用程序中,这应在设置 m_pMainWnd 之后立即发生
	// 分析标准外壳命令、DDE、打开文件操作的命令行
	CCommandLineInfo cmdInfo;
	ParseCommandLine(cmdInfo);
	// 调度在命令行中指定的命令。如果
	// 用 /RegServer、/Register、/Unregserver 或 /Unregister 启动应用程序,则返回 FALSE。
	if (!ProcessShellCommand(cmdInfo)) //引发窗口注册
	return FALSE;
	// 主窗口已初始化,因此显示它并对其进行更新
	pMainFrame->ShowWindow(m_nCmdShow);
	pMainFrame->UpdateWindow();
	return TRUE;
	}
	
	其中,注册窗口用到了一下函数,比较长,如下:
	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);
	}
	if (fToRegister & AFX_WNDCOMMCTL_LINK_REG)
	{
	init.dwICC = ICC_LINK_CLASS;
	fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WNDCOMMCTL_LINK_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;
	}
	
	他是在PreCreateWindow中调用的注册,
	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)
	cs.style |= FWS_PREFIXTITLE;
	cs.dwExStyle |= WS_EX_CLIENTEDGE;
	return TRUE;
	}
	BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
	{
	if( !CFrameWnd::PreCreateWindow(cs) )
	return FALSE;
	// TODO: Modify the Window class or styles here by modifying
	// the CREATESTRUCT cs
	return TRUE;
	}
	BOOL CView::PreCreateWindow(CREATESTRUCT & cs)
	{
	ASSERT(cs.style & WS_CHILD);
	if (cs.lpszClass == NULL)
	{
	VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));
	cs.lpszClass = _afxWndFrameOrView; // COLOR_WINDOW background
	}
	if (cs.style & WS_BORDER)
	{
	cs.dwExStyle |= WS_EX_CLIENTEDGE;
	cs.style &= ~WS_BORDER;
	}
	return TRUE;
	}
	BOOL CTestView::PreCreateWindow(CREATESTRUCT& cs)
	{
	// TODO: Modify the Window class or styles here by modifying
	// the CREATESTRUCT cs
	return CView::PreCreateWindow(cs);
	}
	有几点要说明
	(1)m_pMainWnd,头文件里并没有这个变量呀? 噢,这个在基类里定义的public 类型的变量,所以,他是通过继承得到的。
	(2) 你注意过吗,当我们一点运行,会默认出来一个视图窗口,这是谁调用的呢?哦,就是下面这几行,它调用了opendocument() 函数。
	CCommandLineInfo cmdInfo;
	ParseCommandLine(cmdInfo);
	if (!ProcessShellCommand(cmdInfo))
	return FALSE;
	(3)这个函数有个关键的地方
	CMultiDocTemplate* pDocTemplate;
	pDocTemplate = new CMultiDocTemplate(IDR_jjjTYPE,
	RUNTIME_CLASS(CjjjDoc),
	RUNTIME_CLASS(CChildFrame), // 自定义 MDI 子框架
	RUNTIME_CLASS(CjjjView));
	if (!pDocTemplate)
	return FALSE;
	AddDocTemplate(pDocTemplate);
	通过它,把视图,窗口和文档结合起来。
	4:主窗口CMainFrame的创建
	(1)通过CMainFrame* pMainFrame = new CMainFrame;
	我们得到的是不断调用的基类
	CMainFrame::CMainFrame()
	{
	// TODO: 在此添加成员初始化代码
	}
	CMDIFrameWnd::CMDIFrameWnd()
	{
	m_hWndMDIClient = NULL;
	}
	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();
	}
	(2)pMainFrame->LoadFrame(IDR_MAINFRAME)所引发的一系列
	
	BOOL CMDIFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle,
	CWnd* pParentWnd, CCreateContext* pContext)
	{
	if (!CFrameWnd::LoadFrame(nIDResource, dwDefaultStyle,
	pParentWnd, pContext))
	return FALSE;
	
	// save menu to use when no active MDI child window is present
	ASSERT(m_hWnd != NULL);
	m_hMenuDefault = ::GetMenu(m_hWnd);
	if (m_hMenuDefault == NULL)
	TRACE(traceAppMsg, 0, "Warning: CMDIFrameWnd without a default menu./n");
	return TRUE;
	}
	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);
	CString strTitle = m_strTitle;
	if (!Create(lpszClass, strTitle, dwDefaultStyle, rectDefault,
	pParentWnd, MAKEINTRESOURCE(nIDResource), 
	{
	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;
	}
	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)
	{
	TRACE(traceAppMsg, 0, "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))
	{
	TRACE(traceAppMsg, 0, "Warning: failed to create CFrameWnd./n");
	if (hMenu != NULL)
	DestroyMenu(hMenu);
	return FALSE;
	}
	
	return TRUE;
	}
	
	因为CFrameWnd没有重新写CreateEX,所以是调用的基类的CreateEx的函数:
	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);
	
	#ifdef _DEBUG
	if (hWnd == NULL)
	{
	TRACE(traceAppMsg, 0, "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;
	}
	
	可以看到,::CreateWindowEx是一个全局的函数,在其中触发WM_CREATE消息,进而调用我们自己定义的CMainFrame::OnCreate()
	int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
	{
	if (CMDIFrameWnd::OnCreate(lpCreateStruct) == -1)
	return -1;
	
	if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP
	| CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
	!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
	{
	TRACE0("未能创建工具栏/n");
	return -1; // 未能创建
	}
	if (!m_wndStatusBar.Create(this) || 
	!m_wndStatusBar.SetIndicators(indicators, 
	sizeof(indicators)/sizeof(UINT)))
	{
	TRACE0("未能创建状态栏/n");
	return -1; // 未能创建
	}
	//TODO: 如果不需要工具栏可停靠,则删除这三行
	m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
	EnableDocking(CBRS_ALIGN_ANY);
	DockControlBar(&m_wndToolBar);
	
	return 0;
	}
	5:运行后为什么能产生CMainFarme,文档 ,视图以及视图外包围的farme呢?
	你注意过吗,当我们一点运行,会默认出来一个视图窗口,这是谁调用的呢?哦,就是下面这几行,它调用void CWinApp::OnFileNew()函数.
	CCommandLineInfo cmdInfo;
	ParseCommandLine(cmdInfo);
	if (!ProcessShellCommand(cmdInfo))
	return FALSE;
	你可以自己跟踪以下。这里有个小技巧,你可以在BOOL CjjjDoc::OnNewDocument()处设立断点,然后“跳出”,最后你会达到起始点!ProcessShellCommand(cmdInfo)
	void CWinApp::OnFileNew()
	{
	if (m_pDocManager != NULL)
	m_pDocManager->OnFileNew(); //接着调用下面的函数
	}
	
	void CDocManager::OnFileNew()
	{
	if (m_templateList.IsEmpty())
	{
	TRACE(traceAppMsg, 0, "Error: no document templates registered with CWinApp./n");
	AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC);
	return;
	}
	CDocTemplate* pTemplate = (CDocTemplate*)m_templateList.GetHead();
	if (m_templateList.GetCount() > 1)
	{
	// more than one document template to choose from
	// bring up dialog prompting user
	CNewTypeDlg dlg(&m_templateList);
	INT_PTR nID = dlg.DoModal();
	if (nID == IDOK)
	pTemplate = dlg.m_pSelectedTemplate;
	else
	return; // none - cancel operation
	}
	ASSERT(pTemplate != NULL);
	ASSERT_KINDOF(CDocTemplate, pTemplate);
	pTemplate->OpenDocumentFile(NULL); //接着调用下面的函数 
	// if returns NULL, the user has already been alerted
	}
	
	CDocument* CMultiDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName,
	BOOL bMakeVisible)
	{
	CDocument* pDocument = CreateNewDocument(); //产生模板控制之一:文档
	if (pDocument == NULL)
	{
	TRACE(traceAppMsg, 0, "CDocTemplate::CreateNewDocument returned NULL./n");
	AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC);
	return NULL;
	}
	ASSERT_VALID(pDocument);
	BOOL bAutoDelete = pDocument->m_bAutoDelete;
	pDocument->m_bAutoDelete = FALSE; // don't destroy if something goes wrong
	CFrameWnd* pFrame = CreateNewFrame(pDocument, NULL); //产生新的:框架 
	pDocument->m_bAutoDelete = bAutoDelete;
	if (pFrame == NULL)
	{
	AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC);
	delete pDocument; // explicit delete on error
	return NULL;
	}
	ASSERT_VALID(pFrame);
	
	if (lpszPathName == NULL)
	{
	// create a new document - with default document name
	SetDefaultTitle(pDocument);
	
	// avoid creating temporary compound file when starting up invisible
	if (!bMakeVisible)
	pDocument->m_bEmbedded = TRUE;
	
	if (!pDocument->OnNewDocument())
	{
	// user has be alerted to what failed in OnNewDocument
	TRACE(traceAppMsg, 0, "CDocument::OnNewDocument returned FALSE./n");
	pFrame->DestroyWindow();
	return NULL;
	}
	// it worked, now bump untitled count
	m_nUntitledCount++;
	}
	else
	{
	// open an existing document
	CWaitCursor wait;
	if (!pDocument->OnOpenDocument(lpszPathName))
	{
	// user has be alerted to what failed in OnOpenDocument
	TRACE(traceAppMsg, 0, "CDocument::OnOpenDocument returned FALSE./n");
	pFrame->DestroyWindow();
	return NULL;
	}
	pDocument->SetPathName(lpszPathName);
	}
	
	InitialUpdateFrame(pFrame, pDocument, bMakeVisible);
	return pDocument;
	}
	
	我需要对上述函数进行进一步说明
	(1)CreateNewDocument()产生文档
	CDocument* CDocTemplate::CreateNewDocument()
	{
	// default implementation constructs one from CRuntimeClass
	if (m_pDocClass == NULL)
	{
	TRACE(traceAppMsg, 0, "Error: you must override CDocTemplate::CreateNewDocument./n");
	ASSERT(FALSE);
	return NULL;
	}
	CDocument* pDocument = (CDocument*)m_pDocClass->CreateObject();
	if (pDocument == NULL)
	{
	TRACE(traceAppMsg, 0, "Warning: Dynamic create of document type %hs failed./n",
	m_pDocClass->m_lpszClassName);
	return NULL;
	}
	ASSERT_KINDOF(CDocument, pDocument);
	AddDocument(pDocument);
	return pDocument;
	}
	(2)CreateNewFrame(pDocument, NULL),产生框架和视图
	CFrameWnd* CDocTemplate::CreateNewFrame(CDocument* pDoc, CFrameWnd* pOther)
	{
	if (pDoc != NULL)
	ASSERT_VALID(pDoc);
	// create a frame wired to the specified document
	ASSERT(m_nIDResource != 0); // must have a resource ID to load from
	CCreateContext context;
	context.m_pCurrentFrame = pOther;
	context.m_pCurrentDoc = pDoc;
	context.m_pNewViewClass = m_pViewClass;
	context.m_pNewDocTemplate = this;
	if (m_pFrameClass == NULL)
	{
	TRACE(traceAppMsg, 0, "Error: you must override CDocTemplate::CreateNewFrame./n");
	ASSERT(FALSE);
	return NULL;
	}
	CFrameWnd* pFrame = (CFrameWnd*)m_pFrameClass->CreateObject();
	if (pFrame == NULL)
	{
	TRACE(traceAppMsg, 0, "Warning: Dynamic create of frame %hs failed./n",
	m_pFrameClass->m_lpszClassName);
	return NULL;
	}
	ASSERT_KINDOF(CFrameWnd, pFrame);
	if (context.m_pNewViewClass == NULL)
	TRACE(traceAppMsg, 0, "Warning: creating frame with no default view./n");
	// create new from resource
	if (!pFrame->LoadFrame(m_nIDResource,
	WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE, NULL, &context))
	{
	TRACE(traceAppMsg, 0, "Warning: CDocTemplate couldn't create a frame./n");
	// frame will be deleted in PostNcDestroy cleanup
	return NULL;
	}
	// it worked !
	return pFrame;
	}
	在此函数中,不仅Frame被动态创建出来,其对应的窗口也以LoadFrame产生出来。其中参数context帮助产生了视图。别管怎么产生了,我跟踪不到,反正就是这个时候把框架和视图一起产生了出来。此时激发了WN_CREATE消息,从而引发CFrameWnd::OnCreate函数。我分别把相应的函数写在下面
	CFrameWnd::OnCreate------CFrameWnd::OnCreateHelper------CFrameWnd:OnCreateClient-----CFrameWnd::CreateView*/
	
	int CFrameWnd::OnCreate(LPCREATESTRUCT lpcs)
	{
	CCreateContext* pContext = (CCreateContext*)lpcs->lpCreateParams;
	return OnCreateHelper(lpcs, pContext);
	}
	
	int CFrameWnd::OnCreateHelper(LPCREATESTRUCT lpcs, CCreateContext* pContext)
	{
	if (CWnd::OnCreate(lpcs) == -1)
	return -1;
	// create special children first
	if (!OnCreateClient(lpcs, pContext))
	{
	TRACE(traceAppMsg, 0, "Failed to create client pane/view for frame./n");
	return -1;
	}
	// post message for initial message string
	PostMessage(WM_SETMESSAGESTRING, AFX_IDS_IDLEMESSAGE);
	// make sure the child windows have been properly sized
	RecalcLayout();
	return 0; // create ok
	}
	BOOL CFrameWnd::OnCreateClient(LPCREATESTRUCT, CCreateContext* pContext)
	{
	// default create client will create a view if asked for it
	if (pContext != NULL && pContext->m_pNewViewClass != NULL)
	{
	if (CreateView(pContext, AFX_IDW_PANE_FIRST) == NULL)
	return FALSE;
	}
	return TRUE;
	}
	CWnd* CFrameWnd::CreateView(CCreateContext* pContext, UINT nID)
	{
	ASSERT(m_hWnd != NULL);
	ASSERT(::IsWindow(m_hWnd));
	ASSERT(pContext != NULL);
	ASSERT(pContext->m_pNewViewClass != NULL);
	// Note: can be a CWnd with PostNcDestroy self cleanup
	CWnd* pView = (CWnd*)pContext->m_pNewViewClass->CreateObject();
	if (pView == NULL)
	{
	TRACE(traceAppMsg, 0, "Warning: Dynamic create of view type %hs failed./n",
	pContext->m_pNewViewClass->m_lpszClassName);
	return NULL;
	}
	ASSERT_KINDOF(CWnd, pView);
	// views are always created with a border!
	if (!pView->Create(NULL, NULL, AFX_WS_DEFAULT_VIEW,
	CRect(0,0,0,0), this, nID, pContext))
	{
	TRACE(traceAppMsg, 0, "Warning: could not create view for frame./n");
	return NULL; // can't continue without a view
	}
	if (pView->GetExStyle() & WS_EX_CLIENTEDGE)
	{
	// remove the 3d style from the frame, since the view is
	// providing it.
	// make sure to recalc the non-client area
	ModifyStyleEx(WS_EX_CLIENTEDGE, 0, SWP_FRAMECHANGED);
	}
	return pView;
	}
	6:窗口的现实和刷新(激发paint事件)
	pMainFrame->ShowWindow(m_nCmdShow);
	pMainFrame->UpdateWindow();
	7:pThread->Run()
	/*thrdcore.cpp文件*/
	// main running routine until thread exits
	int CWinThread::Run()
	{
	ASSERT_VALID(this);
	_AFX_THREAD_STATE* pState = AfxGetThreadState();
	
	// 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(&(pState->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))
	if (IsIdleMessage(&(pState->m_msgCur)))
	{
	bIdle = TRUE;
	lIdleCount = 0;
	}
	
	} while (::PeekMessage(&(pState->m_msgCur), NULL, NULL, NULL, PM_NOREMOVE));
	}
	}
	BOOL CWinThread::PumpMessage()
	{
	return AfxInternalPumpMessage();
	}
	BOOL AFXAPI AfxInternalPumpMessage()
	{
	_AFX_THREAD_STATE *pState = AfxGetThreadState();
	
	if (!::GetMessage(&(pState->m_msgCur), NULL, NULL, NULL))
	{
	#ifdef _DEBUG
	TRACE(traceAppMsg, 1, "CWinThread::PumpMessage - Received WM_QUIT./n");
	pState->m_nDisablePumpCount++; // application must die
	#endif
	// Note: prevents calling message loop things in 'ExitInstance'
	// will never be decremented
	return FALSE;
	}
	
	#ifdef _DEBUG
	if (pState->m_nDisablePumpCount != 0)
	{
	TRACE(traceAppMsg, 0, "Error: CWinThread::PumpMessage called when not permitted./n");
	ASSERT(FALSE);
	}
	#endif
	
	#ifdef _DEBUG
	_AfxTraceMsg(_T("PumpMessage"), &(pState->m_msgCur));
	#endif
	
	// process this message
	
	if (pState->m_msgCur.message != WM_KICKIDLE && !AfxPreTranslateMessage(&(pState->m_msgCur)))
	{
	::TranslateMessage(&(pState->m_msgCur));
	::DispatchMessage(&(pState->m_msgCur));
	}
	return TRUE;
	}