- 浏览: 64199 次
-
文章分类
最新评论
MFC 框架剖析
即便是基于MFC的应用程序,建立窗口类也是会遵循如下的过程:
设计窗口类->注册窗口类->生成窗口->显示窗口->更新窗口->消息循环->消息路由到窗口过程函数处理。下面就剖析一下在MFC中是如何完成上述过程的。
(1)每个应用程序都有且仅有一个应用类的全局变量theApp,全局变量先于WinMain函数进行处理。
(2)WinMain函数体在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);
}
其中#define _tWinMain WinMain
(3)AfxWinMain函数体在WINMAIN.CPP文件中,里面有如下两句话:
CWinThread* pThread = AfxGetThread();
CWinApp* pApp = AfxGetApp();
其实这里得到的这两个指针都是指向全局的对象theApp的;
接下来有函数调用pThread->InitInstance(),根据多态性,会调用CXXXApp类中的InitInstance()函数。该函数很重要,在对该函数的调用中就会完成:设计窗口类->注册窗口类->生成窗口->显示窗口->更新窗口。
接下来,该函数中会继续调用pThread->Run(),这就完成了:消息循环->消息路由到窗口过程函数处理。
(4)进入CXXXApp::InitInstance()函数体中,对于单文档应用程序,调用ProcessShellCommand(cmdInfo),通过调用该函数就会完成:设计窗口类->注册窗口类->生成窗口。
再接下来就会调用m_pMainWnd->ShowWindow(SW_SHOW);m_pMainWnd->UpdateWindow();这就完成了:显示窗口->更新窗口。
(5)在函数CWinApp::ProcessShellCommand(CCommandLineInfo& rCmdInfo)中会进入到如下的case分支:case CCommandLineInfo::FileNew:
if (!AfxGetApp()->OnCmdMsg(ID_FILE_NEW, 0, NULL, NULL))
(6)进入函数CCmdTarget::OnCmdMsg(UINT nID, int nCode, void* pExtra,
AFX_CMDHANDLERINFO* pHandlerInfo),调用_AfxDispatchCmdMsg(this, nID, nCode,
lpEntry->pfn, pExtra, lpEntry->nSig, pHandlerInfo);
(7)进入函数AFXAPI _AfxDispatchCmdMsg(CCmdTarget* pTarget, UINT nID, int nCode,
AFX_PMSG pfn, void* pExtra, UINT nSig, AFX_CMDHANDLERINFO* pHandlerInfo),调用
case AfxSig_vv:
// normal command or control notification
ASSERT(CN_COMMAND == 0); // CN_COMMAND same as BN_CLICKED
ASSERT(pExtra == NULL);
(pTarget->*mmf.pfn_COMMAND)();
(8)进入CWinApp::OnFileNew(),调用m_pDocManager->OnFileNew();这个函数很特殊,它本身是个消息响应函数,当我们点击ID为ID_FILE_NEW的菜单时,会产生一个命令消息,由于命令消息可以被CCmdTarget类及其派生类来捕获,而CWinApp是从CCmdTarget派生出来的,因此可以捕获这个消息。当应用程序创建完成并成功显示后,当我们点击文件菜单下的新建菜单项时,就会首先进入这个函数,然后再依次执行下去,最后就会执行到pDocument->OnNewDocument()中,往往我们会对这个函数不解,不知道它为什么会响应ID_FILE_NEW的命令消息,至此真相大白了。顺便说一句,为什么程序在刚启动的时候,我们并没有点击菜单项,为什么会自动的产生这个消息呢?这是因为在CXXXXApp::InitInstance()函数中有“CCommandLineInfo
cmdInfo;”这个类的构造函数是这样的:CCommandLineInfo::CCommandLineInfo()
{
m_bShowSplash = TRUE;
m_bRunEmbedded = FALSE;
m_bRunAutomated = FALSE;
m_nShellCommand = FileNew;
},因此就会在第(5)步骤的时候进入到“case CCommandLineInfo::FileNew:”这个分支中,就相当于产生了这样一个FileNew的消息。同理对于ID为ID_FILE_OPEN(在CWinApp::OnFileOpen()中响应)、ID_FILE_SAVE(在CDocument::OnFileSave()中响应)等等在MFC向导为我们生成的单文档类中找不到消息响应的入口时,其实都是在基类CWinApp或者CDocument类中进行了响应。对于CXXXXDoc::Serialize(CArchive&
ar)函数也是通过ID_FILE_SAVE和ID_FILE_OPEN产生命令消息后就行响应从而才调用该函数的。
(9)进入CDocManager::OnFileNew(),CDocManager类有一个成员变量是CPtrList m_templateList;该变量保存了一个文档模版链表指针,在CDocManager::OnFileNew()函数体中会调用CDocTemplate*
pTemplate = (CDocTemplate*)m_templateList.GetHead();得到链表中的头,也就是第一个文档模版,后面就会用得到的这个指针去调用pTemplate->OpenDocumentFile(NULL);紧接着就会有一个判断,用来确定该链表中是否只有一项,如果链表中保存了多个文档模版,则会弹出一个对话框,来让我们选择到底是使用哪一套文档模版来构建应用程序,相信大家也都见到过这种情况吧。对了,还有一点要说明的是:pTemplate是一个CDocTemplate的指针,但接下来程序为什么会进入到CSingleDocTemplate::OpenDocumentFile的函数体内呢,这是因为CDocTemplate类中的OpenDocumentFile函数被定义为纯虚函数,而CSingleDocTemplate类又是从CDocTemplate类派生出来的,并且实现了该函数,因此就会进入到子类的函数体中了。
(10)进入CDocument* CSingleDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName,
BOOL bMakeVisible),先调用CreateNewDocument()创建文档类,再调用pFrame = CreateNewFrame(pDocument, NULL);创建框架类和视图类,从这里也可以看出MFC体系结构中文档、框架、视图“三位一体”的模式,在这一个函数中同时创建三个类;再会调用pDocument->OnNewDocument();因此就会进入到子类的文档类中的pDocument->OnNewDocument()中了。
(11)进入CFrameWnd* CDocTemplate::CreateNewFrame(CDocument* pDoc, CFrameWnd* pOther),调用if (!pFrame->LoadFrame(m_nIDResource,
WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE, // default frame styles
NULL, &context))
(12)进入BOOL CFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle,
CWnd* pParentWnd, CCreateContext* pContext),调用VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));
(13)进入BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister),该函数内部就完成了:设计窗口类->注册窗口类。MFC通过给我们提供好一些已经订制好的窗口类,我们不需要自己再设计窗口类,只需要到那些订制好的窗口类“仓库”中寻找一种适合我们需要的窗口类就可以了,然后通过AfxRegisterClass函数注册窗口类。还需要说明的是,再后续的跟踪过程中,我们会发现还会进入到AfxEndDeferRegisterClass函数中进行设计和注册窗口类,这主要是因为单文档应用程序比较特殊,它提前通过这样的一种途径进行了窗口类的设计和注册步骤,其实是应该在BOOL
CMainFrame::PreCreateWindow(CREATESTRUCT& cs)函数的调用中完成窗口类的设计和注册的,这一点我们要清楚,也就是说设计和注册窗口类的正宗发源地应该是PreCreateWindow(CREATESTRUCT& cs)。此外,我们还会注意到在该函数体的前部分有一语句为“wndcls.lpfnWndProc = DefWindowProc;”因此所有窗口类的窗口过程函数都是DefWindowProc,这一点在后面的跟踪中可以看到,每次生成窗口之后都会调用几次DefWindowProc函数。也就是说MFC都是让我们采用默认的窗口过程函数,这并不是说我们因此就不能使用自己的窗口过程函数实现个性化的消息处理了,MFC采用了一种基于消息映射的机制完成了消息个性化处理。
(14)回到BOOL CFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle,
CWnd* pParentWnd, CCreateContext* pContext)中,调用LPCTSTR lpszClass = GetIconWndClass(dwDefaultStyle, nIDResource);
(15)进入LPCTSTR CFrameWnd::GetIconWndClass(DWORD dwDefaultStyle, UINT nIDResource),调用PreCreateWindow(cs);
(16)进入BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs),调用CFrameWnd::PreCreateWindow(cs)
(17)进入BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs),调用VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));又一次设计和注册窗口类
(18)回到BOOL CFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle,
CWnd* pParentWnd, CCreateContext* pContext)中,调用if (!Create(lpszClass, lpszTitle, dwDefaultStyle, rectDefault, pParentWnd, MAKEINTRESOURCE(nIDResource), 0L, pContext))
(19)进入BOOL CFrameWnd::Create(LPCTSTR lpszClassName,
LPCTSTR lpszWindowName,
DWORD dwStyle,
const RECT& rect,
CWnd* pParentWnd,
LPCTSTR lpszMenuName,
DWORD dwExStyle,
CCreateContext* pContext),调用if (!CreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle,
rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
pParentWnd->GetSafeHwnd(), hMenu, (LPVOID)pContext))
(20)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),调用if (!PreCreateWindow(cs))
,接下来调用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);好了,终于让我们找到生成窗口的地方了——函数::CreateWindowEx!
(21)进入int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct),调用if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
(22)进入int CFrameWnd::OnCreate(LPCREATESTRUCT lpcs),调用return OnCreateHelper(lpcs, pContext);
(23)进入int CFrameWnd::OnCreateHelper(LPCREATESTRUCT lpcs, CCreateContext* pContext),调用if (CWnd::OnCreate(lpcs) == -1)
(24)进入_AFXWIN_INLINE int CWnd::OnCreate(LPCREATESTRUCT),调用return (int)Default();
(25)进入LRESULT CWnd::Default(),调用return DefWindowProc(pThreadState->m_lastSentMsg.message, pThreadState->m_lastSentMsg.wParam, pThreadState->m_lastSentMsg.lParam);
(26)进入LRESULT CWnd::DefWindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam),调用return ::CallWindowProc(m_pfnSuper, m_hWnd, nMsg, wParam, lParam);
(27)回到int CFrameWnd::OnCreateHelper(LPCREATESTRUCT lpcs, CCreateContext* pContext),调用if (!OnCreateClient(lpcs, pContext))
(28)进入BOOL CFrameWnd::OnCreateClient(LPCREATESTRUCT, CCreateContext* pContext),调用if (CreateView(pContext, AFX_IDW_PANE_FIRST) == NULL)
(29)进入CWnd* CFrameWnd::CreateView(CCreateContext* pContext, UINT nID),调用if (!pView->Create(NULL, NULL, AFX_WS_DEFAULT_VIEW, CRect(0,0,0,0), this, nID,
pContext))
(30)进入BOOL CWnd::Create(LPCTSTR lpszClassName,
LPCTSTR lpszWindowName, DWORD dwStyle,
const RECT& rect,
CWnd* pParentWnd, UINT nID,
CCreateContext* pContext),调用return CreateEx(0, lpszClassName, lpszWindowName,
dwStyle | WS_CHILD,
rect.left, rect.top,
rect.right - rect.left, rect.bottom - rect.top,
pParentWnd->GetSafeHwnd(), (HMENU)nID, (LPVOID)pContext);
(31)进入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),重复生成框架类CMainFrame的过程来生成CXXXView,因为它也是一个窗口类,因此也需要进行那一系列过程才能最终显示更新出来。
调用的顺序是这个样子的:PreCreateWindow(cs)->BOOL CXXXView::PreCreateWindow(CREATESTRUCT& cs)->CView::PreCreateWindow(cs)->VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));->::CreateWindowEx(...)->CWnd::DefWindowProc->::CallWindowProc(...)->...->CXXXView::OnCreate->CView::OnCreate->CWnd::OnCreate->...
写到这里,基本上就清楚了,中间的省略号表示的部分大多数都是在与窗口过程函数有关的,因为在生成窗口的时候需要响应一些消息,因此需要调用一些窗口过程函数,每次在调用::CreateWindowEx(...)函数后都会调用一些窗口过程函数,然后再去调用该窗口类对应的OnCreate函数,其实在调用OnCreate函数之前调用CreateWindowEx只是生成了一个窗口,至于这个窗口里面要放置些什么东西,以及该如何装饰该窗口,则就需要由OnCreate来完成了,往往我们都会在OnCreate函数的后面(这样做是为了不影响窗口本身应该布置的格局)添加一些代码,创建我们自己的东西,比如我们通常会在CMainFrame类的OnCreate函数后面放置一些Create代码,来创建我们自己的可停靠的工具栏或者按钮之类的东西,当然我们也可以在CXXXView类的OnCreate函数的后面添加一些代码,来创建我们需要的东西,比如按钮之类的东西。在完成了从设计、注册到生成窗口的过程之后,往往还需要显示更新,有些时候,我们不必要每次都显示的调用CWnd的ShowWindow和UpdateWindow两个函数,我们可以在创建的时候,给窗口风格中添加WS_VISIBLE即可,因此有些时候会跟踪不到ShowWindow和UpdateWindow两个函数这两个函数,因为窗口在创建的时候就可见了。
总的来说,先初始化应用类,然后注册生成框架类,然后再注册生成视图类,然后注册生成视图类OnCreate函数后面用户添加的、用Create来准备创建的窗口,然后再注册生成框架类的OnCreate函数后面需要生成的m_wndToolBar、m_wndStatusBar以及我们自己添加的要创建的窗口类,最后在回到应用类的初始化的函数体中,调用框架类的显示和更新函数,然后再进入由框架类定义的窗口的消息循环中。
消息循环的过程是这个样子的:
(1)调用int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPTSTR lpCmdLine, int nCmdShow)函数中的pThread->Run()
(2)进入int CWinApp::Run(),调用return CWinThread::Run();
(3)进入int CWinThread::Run(),调用if (!PumpMessage())
(4)进入BOOL CWinThread::PumpMessage(),调用if (!::GetMessage(&m_msgCur, NULL, NULL, NULL))
(5)回到BOOL CWinThread::PumpMessage(),调用::TranslateMessage(&m_msgCur);::DispatchMessage(&m_msgCur);
(6)回到int CWinThread::Run(),调用while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));
(7)再重复(4)-(6)的步骤
下面给出int CWinThread::Run()中消息循环的部分代码:
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));
这段代码其实本质上与我们基于Win32 SDK手写的代码:
//消息循环
MSG msg;
while(GetMessage(&msg,NULL,0,0))
{
//简单的说,函数TranslateMessage就是把WM_KEYDOWN和WM_KEYUP翻译成WM_CHAR消息,没有该函数就不能产生WM_CHAR消息。
TranslateMessage(&msg);
::DispatchMessage(&msg);
}
是一致的。
1,寻找WinMain人口:
在安装目录下找到MFC文件夹下的SRC文件夹,SRC下是MFC源代码。
路径:MFC|SRC|APPMODUL.CPP:
_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPTSTR lpCmdLine, int nCmdShow)
{
// call shared/exported WinMain
return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
}
注意:(#define _tWinMain WinMain)
2,对于全局对象或全局变量来说,在程序运行即WINMAIN函数加载的时候,已经为全局对象或全局变量分配了内存和赋初值。
所以:CTEApp theApp;->CTEApp ::CTEApp(){}->_tWinMain(){}
说明:每一个MFC程序,有且只有一个从WinApp类派生的类(应用程序类),也只有一个从应用程序类所事例化的对象,表示应用程序本身。在WIN32程序当中,表示应用程序是通过WINMAIN入口函数来表示的(通过一个应用程序的一个事例号这一个标识来表示的)。在基于MFC应用程序中,是通过产生一个应用程序对象,用它来唯一的表示了应用程序。
3,通过构造应用程序对象过程中调用基类CWinApp的构造函数,在CWinApp的构造函数中对程序包括运行时一些初始化工作完成了。
CWinApp构造函数:MFC|SRC|APPCORE.CPP
CWinApp::CWinApp(LPCTSTR lpszAppName){...}//带参数,而CTEApp构造函数没有显式向父类传参,难道CWinApp()有默认参数?见下:
(在CWinApp类定义中,CWinApp(LPCTSTR lpszAppName = NULL);
)
注意:CWinApp()函数中:
pThreadState->m_pCurrentWinThread = this;
pModuleState->m_pCurrentWinApp = this
(this指向的是派生类CTEApp对象,即theApp)
调试:CWinApp::CWinApp();->CTEApp theApp;(->CTEApp ::CTEApp())->CWinApp::CWinApp()->CTEApp
::CTEApp()->_tWinMain(){}
4,_tWinMain函数中通过调用AfxWinMain()函数来完成它要完成的功能。(Afx*前缀代表这是应用程序框架函数,是一些全局函数,应用程序框架是一套辅助生成应用程序的框架模型,把一些类做一些有机的集成,我们可根据这些类函数来设计自己的应用程序)。
AfxWinMain()函数路径:MFC|SRC|WINMAIN.CPP:
在AfxWinMain()函数中:
CWinApp* pApp = AfxGetApp();
说明:pApp存储的是指向WinApp派生类对象(theApp)的指针。
//_AFXWIN_INLINE CWinApp* AFXAPI AfxGetApp()
//{ return afxCurrentWinApp; }
调用pThread->InitInstance()
说明:pThread也指向theApp,由于基类中virtual BOOL InitApplication()定义为虚函数,所以调用pThread->InitInstance()时候,调用的是派生类CTEApp的InitInstance()函数。
nReturnCode = pThread->Run();
说明:pThread->Run()完成了消息循环。
5,注册窗口类:AfxEndDeferRegisterClass();
AfxEndDeferRegisterClass()函数所在文件:MFC|SRC|APPCORE.CPP
BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister){...}
说明:设计窗口类:在MFC中事先设计好了几种缺省的窗口类,根据不同的应用程序的选择,调用AfxEndDeferRegisterClass()函数注册所选择的窗口类。
调试:CWinApp::CWinApp();->CTEApp theApp;(->CTEApp ::CTEApp())->CWinApp::CWinApp()->CTEApp
::CTEApp()->_tWinMain(){}//进入程序
->AfxWinMain();->pApp->InitApplication();->pThread->InitInstance()//父类InitInstance虚函数;->CTEApp::InitInstance()//子类实现函数;->AfxEndDeferRegisterClass(LONG
fToRegister)//注册所选择的窗口类(出于文档管理,注册提前,正常的应在PreCreateWindow中进行注册)//之后进入创建窗口阶段(以下再不做调试)
6,PreCreateWindow()://主要是注册窗口类
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if( !CFrameWnd::PreCreateWindow(cs) )
return FALSE;
return TRUE;
}
说明:
CFrameWnd::PreCreateWindow()函数所在文件:MFC|SRC|WINFRM.CPP
BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs)
{
if (cs.lpszClass == NULL)
{
VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));
//判断AFX_WNDFRAMEORVIEW_REG型号窗口类是否注册,如果没有注册则注册
cs.lpszClass = _afxWndFrameOrView; // COLOR_WINDOW background
//把注册后的窗口类名赋给cs.lpszClass
}
if ((cs.style & FWS_ADDTOTITLE) && afxData.bWin4)
cs.style |= FWS_PREFIXTITLE;
if (afxData.bWin4)
cs.dwExStyle |= WS_EX_CLIENTEDGE;
return TRUE;
}
其中:
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);//PreCreateWindow()是个虚函数,如果子类有则调用子类的。
#define VERIFY(f) ASSERT(f)
#define AfxDeferRegisterClass(fClass) AfxEndDeferRegisterClass(fClass)
define AFX_WNDFRAMEORVIEW_REG 0x00008
const TCHAR _afxWndFrameOrView[] = AFX_WNDFRAMEORVIEW;//WINCORE.CPP文件中,定义为全局数组。
//#define AFX_WNDFRAMEORVIEW AFX_WNDCLASS("FrameOrView")
7,创建窗口:
Create()函数路径:MFC|SRC|WINFRM.CPP:
CFrameWnd::Create(...){
...
CreateEx(...);//从父类继承来的,调用CWnd::CreateEx().
...
}
CWnd::CreateEx()函数路径:MFC|SRC|WINCORE.CPP
BOOL CWnd::CreateEx(...){
...
if (!PreCreateWindow(cs))//虚函数,如果子类有调用子类的。
{
PostNcDestroy();
return FALSE;
}
...
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);
...
}
说明:CreateWindowEx()函数与CREATESTRUCT结构体参数的对应关系,使我们在创建窗口之前通过可PreCreateWindow(cs)修改cs结构体成员来修改所要的窗口外观。PreCreateWindow(cs))//是虚函数,如果子类有调用子类的。
HWND CreateWindowEx(
DWORD dwExStyle,
LPCTSTR lpClassName,
LPCTSTR lpWindowName,
DWORD dwStyle,
int x,
int y,
int nWidth,
int nHeight,
HWND hWndParent,
HMENU hMenu,
HINSTANCE hInstance,
LPVOID lpParam
);
typedef struct tagCREATESTRUCT { // cs
LPVOID lpCreateParams;
HINSTANCE hInstance;
HMENU hMenu;
HWND hwndParent;
int cy;
int cx;
int y;
int x;
LONG style;
LPCTSTR lpszName;
LPCTSTR lpszClass;
DWORD dwExStyle;
} CREATESTRUCT;
8,显示和更新窗口:
CTEApp类,TEApp.cpp中
m_pMainWnd->ShowWindow(SW_SHOW);//显示窗口,m_pMainWnd指向框架窗口
m_pMainWnd->UpdateWindow();//更新窗口
说明:
class CTEApp : public CWinApp{...}
class CWinApp : public CWinThread{...}
class CWinThread : public CCmdTarget
{
...
public:
CWnd* m_pMainWnd;
...
...
}
9,消息循环:
int AFXAPI AfxWinMain()
{...
// Perform specific initializations
if (!pThread->InitInstance()){...}
//完成窗口初始化工作,完成窗口的注册,完成窗口的创建,显示和更新。
nReturnCode = pThread->Run();
//继承基类Run()方法,调用CWinThread::Run()来完成消息循环
...
}
////////////////////////////////////////////////////////////////
CWinThread::Run()方法路径:MFC|SRC|THRDCORE.CPP
int CWinThread::Run()
{...
// phase2: pump messages while available
do//消息循环
{
// pump message, but quit on WM_QUIT
if (!PumpMessage())//取消息并处理
return ExitInstance();
...
} while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));
...
}
说明:
BOOL PeekMessage(,,,,)函数说明
The PeekMessage function checks a thread message queue for a message and places the message (if any) in the specified structure.
If a message is available, the return value is nonzero.
If no messages are available, the return value is zero.
/////////////////////////////////////////////////////////////
BOOL CWinThread::PumpMessage()
{
...
if (!::GetMessage(&m_msgCur, NULL, NULL, NULL))//取消息
{...}
...
// process this message
if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur))
{
::TranslateMessage(&m_msgCur);//进行消息(如键盘消息)转换
::DispatchMessage(&m_msgCur);//分派消息到窗口的回调函数处理(实际上分派的消息经过消息映射,交由消息响应函数进行处理。)
}
return TRUE;
}
9,文档与视结构:
可以认为View类窗口是CMainFram类窗口的子窗口。
DOCument类是文档类。
DOC-VIEW结构将数据本身与它的显示分离开。
文档类:数据的存储,加载
视类:数据的显示,修改
10,文档类,视类,框架类的有机结合:
在CTEApp类CTEApp::InitInstance()函数中通过文档模板将文档类,视类,框架类的有机组织一起。
...
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CTEDoc),
RUNTIME_CLASS(CMainFrame), // main SDI frame window
RUNTIME_CLASS(CTEView));
AddDocTemplate(pDocTemplate);//增加到模板
相关推荐
标题和描述均指向了“MFC框架程序剖析”的主题,这暗示了文档的主要内容是关于MFC(Microsoft Foundation Classes)框架的深入分析和探讨。MFC是微软为简化Windows编程而设计的一组类库,它封装了Win32 API,使得...
MFC 程序框架剖析 MFC 程序框架剖析是基于 MFC 的应用程序的核心内容之一。MFC(Microsoft Foundation Classes)是 Microsoft 提供的应用程序框架,它提供了一个基于对象的编程模型,帮助开发者快速构建 Windows ...
WinMain在MFC程序中是如何从源程序中被隐藏的,theApp全局变量是如何被分配的,MFC框架中的几个类的作用与相互关系,MFC框架窗口是如何产生和销毁的,对窗口类的PreCreateWidow和OnCreate两个函数的着重分析,...
在"分析MFC框架的模拟代码——AnylyseMFC.rar"这个压缩包中,我们可以期待找到一些关于MFC框架的源代码实例,帮助我们理解并学习MFC的核心概念。 1. **MFC的基本架构** MFC基于面向对象编程,其核心包括类库、文档...
### MFC框架中的设计模式分析 #### 模板方法(Template Method) 模板方法模式是一种用于代码复用的设计模式。在这一模式中,基类定义了一个算法的骨架,并将某些步骤延迟到子类实现。这种模式允许子类在不改变...
适合初学者学习MFC的框架,梗概的讲述了MFC框架,和常用的类及其成员函数。
MFC框架为开发者提供了丰富的类,涵盖了GUI(图形用户界面)、数据库访问、网络通信等多个方面。下面将详细介绍MFC框架的基本原理和关键知识点。 **MFC框架概述** MFC的核心思想是封装Windows API,通过面向对象的...
下面将详细介绍背景减除法、OpenCV库、VS2010及MFC框架,以及如何将这些元素结合在一起构建一个实时背景模型。 **背景减除法** 背景减除是通过创建和更新一个静态背景模型,然后与每一帧进行比较,找出与背景模型...
在学习如何使用MFC(Microsoft ...而通过跟随大牛作者的分析和代码调试,开发者可以更加清晰地理解整个过程的每一个细节,并对MFC框架有更深刻的认识。这对于开发高效、稳定的Windows桌面应用程序是非常有益的。
学习"VC++ MFC框架的剖析",不仅可以深入了解MFC的工作原理,还能通过实践案例掌握如何在实际项目中应用MFC,提升Windows应用程序的开发能力。无论是初学者还是有经验的开发者,都可以从这份资料中获益匪浅。
【OSG基本MFc框架】是一个面向初学者的OpenSceneGraph(OSG)与Microsoft Foundation Classes(MFC)结合的编程框架。OpenSceneGraph是一个开源的3D图形库,用于高性能的可视化应用,如虚拟现实、科学模拟和游戏开发...
在本课程“MFC-lesson3-AppWizard的原理与MFC程序框架的剖析”中,我们将深入探讨MFC的核心组件,特别是AppWizard工具的使用,以及MFC程序的基本框架结构。 AppWizard是Visual C++集成开发环境中的一个实用工具,它...
MFC程序框架的剖析是一个深入理解MFC核心概念和技术的关键过程,这对于Windows应用开发者来说至关重要。 MFC的核心是CWinApp类,它是每个MFC应用程序的基础。这个类代表了应用程序本身,并管理着诸如初始化、消息...
总的来说,这份MFC应用框架的实例源码提供了学习MFC的基础平台,通过阅读和分析源代码,初学者可以深入了解MFC如何处理Windows消息、如何组织类结构以及如何实现多线程等关键概念,从而更好地掌握MFC开发技术。
第三讲MFC框架程序剖析.ppt
在深入探讨如何将Win32程序封装成MFC框架之前,我们先理解这两个概念的基本含义。Win32程序是基于Microsoft Windows API(应用程序接口)编写的,它直接使用Windows操作系统提供的函数来创建用户界面和执行任务。而...
关于MFC框架的分析,对于理解面向对象程序很有用,建议参考,新学者多了解,帮助很大。
通过上述分析可以看出,MFC框架通过C++的封装性、继承性和多态性,以及一系列关键类和函数,为Windows应用程序开发提供了一套完整的解决方案。开发者只需要关注应用程序的核心逻辑,而无需关心底层的复杂细节。