怎么样剖析MFC程序框架?

怎么样剖析MFC程序框架?

MFC程序框架剖析

作者:ECNU)孟庆涛 Email:mqt-2003@163.com

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;

}

由上面的程序可以看到几个很重要的函数

1AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))

goto InitFailure;

(2) pApp->InitApplication())

(3) pThread->InitInstance()

(4) pThread->Run()

其中12 也是完成程序的一些初始化工作,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);

}

有几点要说明

1m_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), 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;

}

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;

}