社区
非技术类
帖子详情
CWinThread里的Run函数是什么时候被谁调用的,是自动的呢还是要手动自己去调用。
hahatoy
2002-01-17 06:31:17
...全文
112
3
打赏
收藏
CWinThread里的Run函数是什么时候被谁调用的,是自动的呢还是要手动自己去调用。
复制链接
扫一扫
分享
转发到动态
举报
写回复
配置赞助广告
用AI写文章
3 条
回复
切换为时间正序
请发表友善的回复…
发表回复
打赏红包
masterz
2002-01-17
打赏
举报
回复
当然可以重载
hahatoy
2002-01-17
打赏
举报
回复
那可不可以重载呢
应该没问题吧
zzaixi
2002-01-17
打赏
举报
回复
是C++自动调用的
在InitInstance()之后!
我们没必要去改写它
MFC的程序框架剖析
什么是句柄? 句柄,是整个
Win
dows编程的基础。一个句柄是指使用的一个唯一的整数值,即一个4字节(64位程序中为8字节)长的数值,来标识应用程序中的不同对象和同类对象中的不同的实例,诸如,一个窗口,按钮,图标,滚动条,输出设备,控件或者文件等。应用程序能够通过句柄访问相应的对象的信息,但是句柄不是一个指针,程序不能利用句柄来直接阅读文件中的信息。如果句柄不用在I/O文件中,它是毫无用处的。 句柄是
Win
dows用来标志应用程序中建立的或是使用的唯一整数,
Win
dows使用了大量的句柄来标志很多对象。 一、MFC AppWizard 1、MFC(Microsoft Foundation Class,微软基础类库)是微软为了简化程序员的开发工作所开发的一套C++ 类的集合,是一套面向对象的
函数
库,以类的方式提供给用户使用 2、MFC AppWizard是一个辅助我们生成源代码的向导工具,它可以帮助我们
自动
生成基于MFC框架的源代码 二、基于MFC的程序框架剖析 1、MFC程序的ClassView标签页(图) 2、继承关系 (1)CMainFrame继承于CFrameWnd (2)CTestApp继承于C
Win
App (3)CTestDoc继承于CDocument (4)CTestView继承于CView 注:CFrameWnd和CView都继承于CWnd 3、CWnd类是MFC中一个非常重要的类,它封装了与窗口相关的操作 4、MFC类的简化组织结构图(图) 5、MFC程序也有一个
Win
Main
函数
,程序是在编译时,由链接器将它链接到程序中 6、MFC程序具有一个CTestApp类的全局对象theApp,在MFC程序运行时,程序执行的顺序为:theApp全局对象定义 处->CTestApp构造
函数
->
Win
Main
函数
7、对于普通的VC++控制台程序,无论全局变量还是全局对象,程序运行时,在加载main
函数
之前,就已经为它们 分配了内存空间。对于一个全局对象来说,此时就会
调用
该对象的构造
函数
,构造该对象,并进行初始化操作 8、实例句柄与全局对象 (1)对于
Win
32 SDK程序,应用程序的实例是由实例句柄(
Win
Main
函数
的hInstance参数)来标识的 (2)对于MFC程序,应用程序的实例是由全局对象(每一个MFC程序有且仅有一个从应用程序类C
Win
App派生的类, 如CTestApp,它实例化theApp全局对象)来标识的 9、基类构造
函数
中this指针的指向问题 在构造子类对象时,会
自动
调用
父类的构造
函数
,此时在父类的构造
函数
中的this指针所指向的是子类对象地址 10、Afx
Win
Main
函数
MFC程序的
Win
Main
函数
是通过
调用
Afx
Win
Main
函数
来完成它的功能的 注:Afx前缀的
函数
代表应用程序框架(Application Framework)
函数
,它们可以在程序的任何地方被
调用
11、CTestApp::InitInstance
函数
在Afx
Win
Main
函数
中,通过
调用
InitInstance
函数
来完成MFC内部管理方面的工作 12、AfxEndDeferRegisterClass
函数
MFC提供了一些默认的标准窗口类,我们只需要选择所需的窗口类就行。然后,
调用
AfxEndDeferRegisterClass
函数
来注册窗口类 13、CMainFrame::PreCreate
Win
dow
函数
MFC程序具有两个窗口(框架窗口和视类窗口),在框架窗口产生之前会
调用
PreCreate
Win
dow
函数
14、CWnd::CreateEx
函数
在MFC程序中,窗口的创建是由CreateEx
函数
实现的 15、CWnd::Create
Win
dowEx
函数
主要作用是当修改了CreateEx
函数
的CREATESTRUCT参数时,Create
Win
dowEx
函数
会根据参数发生的相应变化来创 建一个符合我们要求的窗口 注:MFC中后缀名为Ex的
函数
都是扩展
函数
16、CMainFrame::Show
Win
dow
函数
和CMainFrame::Update
Win
dow
函数
用于显示应用程序框架窗口和更新这个窗口 17、C
Win
Thread
::
Run
函数
和C
Win
Thread
::PumpMessage
函数
用于完成消息循环 18、Def
Win
dowProc
函数
默认的窗口过程,但MFC程序对消息的处理实际上是通过消息映射机制来完成的 19、MFC程序的运行过程 (1)首先利用全局应用程序对象theApp启动应用程序 (2)
调用
全局应用程序对象的构造
函数
,从而就会
调用
其基类C
Win
App的构造
函数
,以完成应用程序的一些初始化 (3)进入
Win
Main
函数
(4)进入消息循环 20、MFC程序的主要过程 theApp-> CTestApp::CTestApp构造
函数
-> C
Win
App::C
Win
App构造
函数
-> _t
Win
Main(
Win
Main
函数
的宏)-> Afx
Win
Main
函数
-> CTestApp::InitInstance
函数
-> AfxEndDeferRegisterClass
函数
-> CMainFrame::PreCreate
Win
dow
函数
-> CFrameWnd::PreCreate
Win
dow
函数
-> AfxDeferRegisterClass(AfxEndDeferRegisterClass
函数
的宏)-> CFrameWnd::Create
函数
-> CWnd::CreateEx
函数
-> CMainFrame::PreCreate
Win
dow
函数
-> CWnd::CreateEx
函数
-> CMainFrame::Show
Win
dow
函数
-> CMainFrame::Update
Win
dow
函数
-> C
Win
Thread
::
Run
函数
-> C
Win
Thread
::PumpMessage
函数
21、框架窗口(整个应用程序外框所包括的部分)是视类窗口(框架窗口中空白的地方)的一个父窗口 22、MFC提供了一个文档/视类的结构,文档是指CDocument类,视类是指CView类。前者用于数据的存储和加载, 后者用于数据的显示和修改 23、框架对象、文档对象和视类对象是通过一个单文档模板指针来有机地组织在一起,并利用AddDocTemplate
函数
把这个单文档模板添加到文档模板中,从而把这三个类组织成为一个整体 24、MFC程序的CAboutDlg类继承于CDialog类,用于为用户提供一些与程序相关的帮助信息 三、窗口类、窗口类对象与窗口 1、以“::”开始的
函数
是一个全局
函数
,表示
调用
的是Platform SDK的
函数
2、如果我们关闭了一个窗口,这个窗口就销毁了,那么该窗口对应的C++窗口类对象销毁了吗? (1)当一个窗口销毁时,它会
调用
CWnd::Destroy
Win
dow
函数
,该
函数
销毁窗口后,将CWnd::m_hWnd设为NULL (2)窗口的生命周期和C++窗口类对象的声明周期不是一致的。当一个窗口销毁时,与C++窗口类对象没有关系,它 们之间的纽带仅仅在于这个C++窗口类内部的成员变量m_hWnd,该变量保存了与这个C++窗口类对象相关的哪个窗口 的句柄 (3)但是,当C++窗口类对象销毁时,与之相关的窗口也将销毁,因为它们之间的纽带m_hWnd已经断了 3、示例---在窗口中显示按钮 (1)CButton按钮类继承于CWnd (2)对于一个CButton对象,在定义之后就可以使用了;但是,如果要显示这个按钮的话,还需
调用
CButton::Create
函数
,把按钮窗口与CButton对象关联起来 (3)MFC程序的窗口创建时都会产生WM_CREATE消息,该消息通过OnCreate
函数
来捕获。对于框架窗口来说,MFC直 接把OnCreate
函数
提供到了CMainFrame中;而在视类窗口中没有提供该
函数
,如需使用,要用户自行添加 (4)通常对MFC程序的操作,都是在CTestView视类窗口中进行的 (5)在窗口创建之后,要显示该窗口可以通过
调用
Show
Win
dow
函数
或指定窗口风格为WS_VISIBLE来实现 (6)实现过程 A:在CTestView类中,添加CButton类型的私有成员m_btn B:在CTestView类中,添加WM_CREATE消息的OnCreate处理
函数
C:在CTestView类中,通过GetParent
函数
可以获得CMainFrame框架窗口对象的指针 D:实现一(在视类窗口中通过Show
Win
dow
函数
显示按钮) int CTestView::OnCreate(LPCREATESTRUCT lpCreateStruct) { ... m_btn.Create("按钮",WS_CHILD|BS_DEFPUSHBUTTON,CRect(0,0,100,100),this,123); m_btn.Show
Win
dow(SW_SHOWNORMAL); return 0: } E:实现二(在视类窗口中通过WS_VISIBLE风格显示窗口) int CTestView::OnCreate(LPCREATESTRUCT lpCreateStruct) { ... m_btn.Create("按钮",WS_CHILD|WS_VISIBLE|BS_DEFPUSHBUTTON,CRect(0,0,100,100),this,123); return 0: } F:实现三(在框架窗口中显示按钮) int CTestView::OnCreate(LPCREATESTRUCT lpCreateStruct) { ... m_btn.Create("按钮",WS_CHILD|WS_VISIBLE|BS_DEFPUSHBUTTON,CRect(0,0,100,100),GetParent(),123); return 0: } 即便是基于MFC的应用程序,建立窗口类也是会遵循如下的过程: 设计窗口类->注册窗口类->生成窗口->显示窗口->更新窗口->消息循环->消息路由到窗口过程
函数
处理。下面就剖析一下在MFC中是如何完成上述过程的。 (1)每个应用程序都有且仅有一个应用类的全局变量theApp,全局变量先于
Win
Main
函数
进行处理。 (2)
Win
Main
函数
体在APPMODUL.CPP文件中,定义如下: extern "C" int
WIN
API _t
Win
Main(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { // call shared/exported
Win
Main return Afx
Win
Main(hInstance, hPrevInstance, lpCmdLine, nCmdShow); } 其中#define _t
Win
Main
Win
Main (3)Afx
Win
Main
函数
体在
WIN
MAIN.CPP文件中,
里
面有如下两句话: C
Win
Thread
* p
Thread
= AfxGet
Thread
(); C
Win
App* pApp = AfxGetApp(); 其实这
里
得到的这两个指针都是指向全局的对象theApp的; 接下来有
函数
调用
p
Thread
->InitInstance(),根据多态性,会
调用
CXXXApp类中的InitInstance()
函数
。该
函数
很重要,在对该
函数
的
调用
中就会完成:设计窗口类->注册窗口类->生成窗口->显示窗口->更新窗口。 接下来,该
函数
中会继续
调用
p
Thread
->
Run
(),这就完成了:消息循环->消息路由到窗口过程
函数
处理。 (4)进入CXXXApp::InitInstance()
函数
体中,对于单文档应用程序,
调用
ProcessShellCommand(cmdInfo),通过
调用
该
函数
就会完成:设计窗口类->注册窗口类->生成窗口。 再接下来就会
调用
m_pMainWnd->Show
Win
dow(SW_SHOW);m_pMainWnd->Update
Win
dow();这就完成了:显示窗口->更新窗口。 (5)在
函数
C
Win
App::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)进入C
Win
App::OnFileNew(),
调用
m_pDocManager->OnFileNew();这个
函数
很特殊,它本身是个消息响应
函数
,当我们点击ID为ID_FILE_NEW的菜单时,会产生一个命令消息,由于命令消息可以被CCmdTarget类及其派生类来捕获,而C
Win
App是从CCmdTarget派生出来的,因此可以捕获这个消息。当应用程序创建完成并成功显示后,当我们点击文件菜单下的新建菜单项时,就会首先进入这个
函数
,然后再依次执行下去,最后就会执行到pDocument->OnNewDocument()中,往往我们会对这个
函数
不解,不知道它为什么会响应ID_FILE_NEW的命令消息,至此真相大白了。顺便说一句,为什么程序在刚启动的时候,我们并没有点击菜单项,为什么会
自动
的产生这个消息呢?这是因为在CXXXXApp::InitInstance()
函数
中有“CCommandLineInfo cmdInfo;”这个类的构造
函数
是这样的:CCommandLineInfo::CCommandLineInfo() { m_bShowSplash = TRUE; m_b
Run
Embedded = FALSE; m_b
Run
Automated = FALSE; m_nShellCommand = FileNew; },因此就会在第(5)步骤的时候进入到“case CCommandLineInfo::FileNew:”这个分支中,就相当于产生了这样一个FileNew的消息。同理对于ID为ID_FILE_OPEN(在C
Win
App::OnFileOpen()中响应)、ID_FILE_SAVE(在CDocument::OnFileSave()中响应)等等在MFC向导为我们生成的单文档类中找不到消息响应的入口时,其实都是在基类C
Win
App或者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_OVERLAPPED
WIN
DOW | 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::PreCreate
Win
dow(CREATESTRUCT& cs)
函数
的
调用
中完成窗口类的设计和注册的,这一点我们要清楚,也就是说设计和注册窗口类的正宗发源地应该是PreCreate
Win
dow(CREATESTRUCT& cs)。此外,我们还会注意到在该
函数
体的前部分有一语句为“wndcls.lpfnWndProc = Def
Win
dowProc;”因此所有窗口类的窗口过程
函数
都是Def
Win
dowProc,这一点在后面的跟踪中可以看到,每次生成窗口之后都会
调用
几次Def
Win
dowProc
函数
。也就是说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),
调用
PreCreate
Win
dow(cs); (16)进入BOOL CMainFrame::PreCreate
Win
dow(CREATESTRUCT& cs),
调用
CFrameWnd::PreCreate
Win
dow(cs) (17)进入BOOL CFrameWnd::PreCreate
Win
dow(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 lpsz
Win
dowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, LPCTSTR lpszMenuName, DWORD dwExStyle, CCreateContext* pContext),
调用
if (!CreateEx(dwExStyle, lpszClassName, lpsz
Win
dowName, 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 lpsz
Win
dowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam),
调用
if (!PreCreate
Win
dow(cs)) ,接下来
调用
HWND hWnd = ::Create
Win
dowEx(cs.dwExStyle, cs.lpszClass, cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy, cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);好了,终于让我们找到生成窗口的地方了——
函数
::Create
Win
dowEx! (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)进入_AFX
WIN
_INLINE int CWnd::OnCreate(LPCREATESTRUCT),
调用
return (int)Default(); (25)进入LRESULT CWnd::Default(),
调用
return Def
Win
dowProc(p
Thread
State->m_lastSentMsg.message, p
Thread
State->m_lastSentMsg.wParam, p
Thread
State->m_lastSentMsg.lParam); (26)进入LRESULT CWnd::Def
Win
dowProc(UINT nMsg, WPARAM wParam, LPARAM lParam),
调用
return ::Call
Win
dowProc(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 lpsz
Win
dowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext),
调用
return CreateEx(0, lpszClassName, lpsz
Win
dowName, 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 lpsz
Win
dowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam),重复生成框架类CMainFrame的过程来生成CXXXView,因为它也是一个窗口类,因此也需要进行那一系列过程才能最终显示更新出来。
调用
的顺序是这个样子的:PreCreate
Win
dow(cs)->BOOL CXXXView::PreCreate
Win
dow(CREATESTRUCT& cs)->CView::PreCreate
Win
dow(cs)->VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));->::Create
Win
dowEx(...)->CWnd::Def
Win
dowProc->::Call
Win
dowProc(...)->...->CXXXView::OnCreate->CView::OnCreate->CWnd::OnCreate->... 写到这
里
,基本上就清楚了,中间的省略号表示的部分大多数都是在与窗口过程
函数
有关的,因为在生成窗口的时候需要响应一些消息,因此需要
调用
一些窗口过程
函数
,每次在
调用
::Create
Win
dowEx(...)
函数
后都会
调用
一些窗口过程
函数
,然后再去
调用
该窗口类对应的OnCreate
函数
,其实在
调用
OnCreate
函数
之前
调用
Create
Win
dowEx只是生成了一个窗口,至于这个窗口
里
面要放置些什么东西,以及该如何装饰该窗口,则就需要由OnCreate来完成了,往往我们都会在OnCreate
函数
的后面(这样做是为了不影响窗口本身应该布置的格局)添加一些代码,创建我们自己的东西,比如我们通常会在CMainFrame类的OnCreate
函数
后面放置一些Create代码,来创建我们自己的可停靠的工具栏或者按钮之类的东西,当然我们也可以在CXXXView类的OnCreate
函数
的后面添加一些代码,来创建我们需要的东西,比如按钮之类的东西。在完成了从设计、注册到生成窗口的过程之后,往往还需要显示更新,有些时候,我们不必要每次都显示的
调用
CWnd的Show
Win
dow和Update
Win
dow两个
函数
,我们可以在创建的时候,给窗口风格中添加WS_VISIBLE即可,因此有些时候会跟踪不到Show
Win
dow和Update
Win
dow两个
函数
这两个
函数
,因为窗口在创建的时候就可见了。 总的来说,先初始化应用类,然后注册生成框架类,然后再注册生成视图类,然后注册生成视图类OnCreate
函数
后面用户添加的、用Create来准备创建的窗口,然后再注册生成框架类的OnCreate
函数
后面需要生成的m_wndToolBar、m_wndStatusBar以及我们自己添加的要创建的窗口类,最后在回到应用类的初始化的
函数
体中,
调用
框架类的显示和更新
函数
,然后再进入由框架类定义的窗口的消息循环中。 消息循环的过程是这个样子的: (1)
调用
int AFXAPI Afx
Win
Main(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
函数
中的p
Thread
->
Run
() (2)进入int C
Win
App::
Run
(),
调用
return C
Win
Thread
::
Run
(); (3)进入int C
Win
Thread
::
Run
(),
调用
if (!PumpMessage()) (4)进入BOOL C
Win
Thread
::PumpMessage(),
调用
if (!::GetMessage(&m_msgCur, NULL, NULL, NULL)) (5)回到BOOL C
Win
Thread
::PumpMessage(),
调用
::TranslateMessage(&m_msgCur);::DispatchMessage(&m_msgCur); (6)回到int C
Win
Thread
::
Run
(),
调用
while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE)); (7)再重复(4)-(6)的步骤 下面给出int C
Win
Thread
::
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)); 这段代码其实本质上与我们基于
Win
32 SDK手写的代码: //消息循环 MSG msg; while(GetMessage(&msg,NULL,0,0)) { //简单的说,
函数
TranslateMessage就是把WM_KEYDOWN和WM_KEYUP翻译成WM_CHAR消息,没有该
函数
就不能产生WM_CHAR消息。 TranslateMessage(&msg); ::DispatchMessage(&msg); } 是一致的。 1,寻找
Win
Main人口: 在安装目录下找到MFC文件夹下的SRC文件夹,SRC下是MFC源代码。 路径:MFC|SRC|APPMODUL.CPP: _t
Win
Main(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { // call shared/exported
Win
Main return Afx
Win
Main(hInstance, hPrevInstance, lpCmdLine, nCmdShow); } 注意:(#define _t
Win
Main
Win
Main) 2,对于全局对象或全局变量来说,在程序运行即
WIN
MAIN
函数
加载的时候,已经为全局对象或全局变量分配了内存和赋初值。 所以:CTEApp theApp;->CTEApp ::CTEApp(){}->_t
Win
Main(){} 说明:每一个MFC程序,有且只有一个从
Win
App类派生的类(应用程序类),也只有一个从应用程序类所事例化的对象,表示应用程序本身。在
WIN
32程序当中,表示应用程序是通过
WIN
MAIN入口
函数
来表示的(通过一个应用程序的一个事例号这一个标识来表示的)。在基于MFC应用程序中,是通过产生一个应用程序对象,用它来唯一的表示了应用程序。 3,通过构造应用程序对象过程中
调用
基类C
Win
App的构造
函数
,在C
Win
App的构造
函数
中对程序包括运行时一些初始化工作完成了。 C
Win
App构造
函数
:MFC|SRC|APPCORE.CPP C
Win
App::C
Win
App(LPCTSTR lpszAppName){...}//带参数,而CTEApp构造
函数
没有显式向父类传参,难道C
Win
App()有默认参数?见下: (在C
Win
App类定义中, C
Win
App(LPCTSTR lpszAppName = NULL); ) 注意:C
Win
App()
函数
中: p
Thread
State->m_pCurrent
Win
Thread
= this; pModuleState->m_pCurrent
Win
App = this (this指向的是派生类CTEApp对象,即theApp) 调试:C
Win
App::C
Win
App();->CTEApp theApp;(->CTEApp ::CTEApp())->C
Win
App::C
Win
App()->CTEApp ::CTEApp()->_t
Win
Main(){} 4,_t
Win
Main
函数
中通过
调用
Afx
Win
Main()
函数
来完成它要完成的功能。(Afx*前缀代表这是应用程序框架
函数
,是一些全局
函数
,应用程序框架是一套辅助生成应用程序的框架模型,把一些类做一些有机的集成,我们可根据这些类
函数
来设计自己的应用程序)。 Afx
Win
Main()
函数
路径:MFC|SRC|
WIN
MAIN.CPP: 在Afx
Win
Main()
函数
中: C
Win
App* pApp = AfxGetApp(); 说明:pApp存储的是指向
Win
App派生类对象(theApp)的指针。 //_AFX
WIN
_INLINE C
Win
App* AFXAPI AfxGetApp() // { return afxCurrent
Win
App; }
调用
p
Thread
->InitInstance() 说明:p
Thread
也指向theApp,由于基类中virtual BOOL InitApplication()定义为虚
函数
,所以
调用
p
Thread
->InitInstance()时候,
调用
的是派生类CTEApp的InitInstance()
函数
。 nReturnCode = p
Thread
->
Run
(); 说明:p
Thread
->
Run
()完成了消息循环。 5,注册窗口类:AfxEndDeferRegisterClass(); AfxEndDeferRegisterClass()
函数
所在文件:MFC|SRC|APPCORE.CPP BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister){...} 说明:设计窗口类:在MFC中事先设计好了几种缺省的窗口类,根据不同的应用程序的选择,
调用
AfxEndDeferRegisterClass()
函数
注册所选择的窗口类。 调试:C
Win
App::C
Win
App();->CTEApp theApp;(->CTEApp ::CTEApp())->C
Win
App::C
Win
App()->CTEApp ::CTEApp()->_t
Win
Main(){}//进入程序 ->Afx
Win
Main();->pApp->InitApplication();->p
Thread
->InitInstance()//父类InitInstance虚
函数
;->CTEApp::InitInstance()//子类实现
函数
;->AfxEndDeferRegisterClass(LONG fToRegister)//注册所选择的窗口类(出于文档管理,注册提前,正常的应在PreCreate
Win
dow中进行注册)//之后进入创建窗口阶段(以下再不做调试) 6,PreCreate
Win
dow()://主要是注册窗口类 BOOL CMainFrame::PreCreate
Win
dow(CREATESTRUCT& cs) { if( !CFrameWnd::PreCreate
Win
dow(cs) ) return FALSE; return TRUE; } 说明: CFrameWnd::PreCreate
Win
dow()
函数
所在文件:MFC|SRC|
WIN
FRM.CPP BOOL CFrameWnd::PreCreate
Win
dow(CREATESTRUCT& cs) { if (cs.lpszClass == NULL) { VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG)); //判断AFX_WNDFRAMEORVIEW_REG型号窗口类是否注册,如果没有注册则注册 cs.lpszClass = _afxWndFrameOrView; // COLOR_
WIN
DOW background //把注册后的窗口类名赋给cs.lpszClass } if ((cs.style & FWS_ADDTOTITLE) && afxData.b
Win
4) cs.style |= FWS_PREFIXTITLE; if (afxData.b
Win
4) cs.dwExStyle |= WS_EX_CLIENTEDGE; return TRUE; } 其中: virtual BOOL PreCreate
Win
dow(CREATESTRUCT& cs);//PreCreate
Win
dow()是个虚
函数
,如果子类有则
调用
子类的。 #define VERIFY(f) ASSERT(f) #define AfxDeferRegisterClass(fClass) AfxEndDeferRegisterClass(fClass) define AFX_WNDFRAMEORVIEW_REG 0x00008 const TCHAR _afxWndFrameOrView[] = AFX_WNDFRAMEORVIEW;//
WIN
CORE.CPP文件中,定义为全局数组。 //#define AFX_WNDFRAMEORVIEW AFX_WNDCLASS("FrameOrView") 7,创建窗口: Create()
函数
路径:MFC|SRC|
WIN
FRM.CPP: CFrameWnd::Create(...){ ... CreateEx(...);//从父类继承来的,
调用
CWnd::CreateEx(). ... } CWnd::CreateEx()
函数
路径:MFC|SRC|
WIN
CORE.CPP BOOL CWnd::CreateEx(...){ ... if (!PreCreate
Win
dow(cs))//虚
函数
,如果子类有
调用
子类的。 { PostNcDestroy(); return FALSE; } ... HWND hWnd = ::Create
Win
dowEx(cs.dwExStyle, cs.lpszClass, cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy, cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams); ... } 说明:Create
Win
dowEx()
函数
与CREATESTRUCT结构体参数的对应关系,使我们在创建窗口之前通过可PreCreate
Win
dow(cs)修改cs结构体成员来修改所要的窗口外观。PreCreate
Win
dow(cs))//是虚
函数
,如果子类有
调用
子类的。 HWND Create
Win
dowEx( DWORD dwExStyle, LPCTSTR lpClassName, LPCTSTR lp
Win
dowName, 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->Show
Win
dow(SW_SHOW);//显示窗口,m_pMainWnd指向框架窗口 m_pMainWnd->Update
Win
dow();//更新窗口 说明: class CTEApp : public C
Win
App{...} class C
Win
App : public C
Win
Thread
{...} class C
Win
Thread
: public CCmdTarget { ... public: CWnd* m_pMainWnd; ... ... } 9,消息循环: int AFXAPI Afx
Win
Main() { ... // Perform specific initializations if (!p
Thread
->InitInstance()){...} //完成窗口初始化工作,完成窗口的注册,完成窗口的创建,显示和更新。 nReturnCode = p
Thread
->
Run
(); //继承基类
Run
()方法,
调用
C
Win
Thread
::
Run
()来完成消息循环 ... } //////////////////////////////////////////////////////////////// C
Win
Thread
::
Run
()方法路径:MFC|SRC|THRDCORE.CPP int C
Win
Thread
::
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 C
Win
Thread
::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,
RUN
TIME_CLASS(CTEDoc),
RUN
TIME_CLASS(CMainFrame), // main SDI frame
win
dow
RUN
TIME_CLASS(CTEView)); AddDocTemplate(pDocTemplate);//增加到模板
mfc常用类及其成员
函数
C
Run
timeClass结构 在C
Run
timeClass结构中定义了类名、对象所占存储空间的大小、类的版本号等成员变量及动态创建对象、派生关系判断等成员
函数
。每一个从CObject类派生的类都有一个C
Run
timeClass结构同它关联,以便完成在运行时得到对象的信息或基类的信息。 要使用C
Run
timeClass结构,必须结合使用
RUN
TIME_CLASS()宏和其他有关运行时类型识别的MFC宏。 CCmdTarget类 (1)消息发送 MFC应用程序为每个CCmdTarget派生类创建一个称为消息映射表的静态数据结构,可将消息映射到对象所对应的消息处理
函数
上。 (2)设置光标 BeginWaitCursor() 将光标改为沙漏形状; EndWaitCursor() 将光标改回到之前的形状; RestoreWaitCursor()用于将光标还原为等待状态。 (3)支持
自动
化 CCmdTarget类支持程序通过COM接口进行交互操作,
自动
翻译COM接口的方法。 C
Win
Thread
类 由CCmdTarget派生,主要工作是创建和处理消息循环。 C
Win
App类 从C
Win
Thread
类派生,成员
函数
InitApplication()、InitInstance()、
Run
()。 在InitInstance()
函数
中,创建了一个单文档模板类或多文档模板类(CDocTemplate)的对象,并且在文档模板的构造
函数
中,系统定义的宏
RUN
TIME_CLASS创建了文档类对象,框架窗口类对象和视图类对象. 在MFC应用程序中有且仅有一个C
Win
App派生类的对象,代表程序运行的主线程,代表应用程序本身。 CWnd类 由CCmdTarget类直接派生,是MFC中最基本的GUI对象。公共变量m_hWnd用于存放供API
函数
调用
的窗口句柄。
MFC常用的类及其成员
函数
简介
FC常用类及其成员
函数
C
Run
timeClass结构 在C
Run
timeClass结构中定义了类名、对象所占存储空间的大小、类的版本号等成员变量及动态创建对象、派生关系判断等成员
函数
。每一个从CObject类派生的类都有一个C
Run
timeClass结构同它关联,以便完成在运行时得到对象的信息或基类的信息。 要使用C
Run
timeClass结构,必须结合使用
RUN
TIME_CLASS()宏和其他有关运行时类型识别的MFC宏。 CCmdTarget类 (1)消息发送 MFC应用程序为每个CCmdTarget派生类创建一个称为消息映射表的静态数据结构,可将消息映射到对象所对应的消息处理
函数
上。 (2)设置光标 BeginWaitCursor() 将光标改为沙漏形状; EndWaitCursor() 将光标改回到之前的形状; RestoreWaitCursor()用于将光标还原为等待状态。 (3)支持
自动
化 CCmdTarget类支持程序通过COM接口进行交互操作,
自动
翻译COM接口的方法。 C
Win
Thread
类 由CCmdTarget派生,主要工作是创建和处理消息循环。 C
Win
App类 从C
Win
Thread
类派生,成员
函数
InitApplication()、InitInstance()、
Run
()。 在InitInstance()
函数
中,创建了一个单文档模板类或多文档模板类(CDocTemplate)的对象,并且在文档模板的构造
函数
中,系统定义的宏
RUN
TIME_CLASS创建了文档类对象,框架窗口类对象和视图类对象. 在MFC应用程序中有且仅有一个C
Win
App派生类的对象,代表程序运行的主线程,代表应用程序本身。 CWnd类 由CCmdTarget类直接派生,是MFC中最基本的GUI对象。公共变量m_hWnd用于存放供API
函数
调用
的窗口句柄。 CframeWnd类 从CWnd类派生而来,主要用来掌管一个窗口。其对象是一个框架窗口,包括边界、标题栏、菜单、最大化按钮、最小化按钮和一个激活的视图。常用成员
函数
: GetActiveDocument():得到当前文档的指针。 GetActiveView(): 得到当前视图的指针。 SetActiveView(): 激活一个视图。 GetTitle(): 得到框架窗口的标题。 SetTitle(): 设置框架窗口的标题。 SetMessageText(): 设置状态栏文本。 CDocument类 从CCmdTarget派生,作为用户文档的基类,代表了用户存储或打开一个文件。主要功能是把对数据的处理从对用户的界面处理中分离出来,同时提供一个与视图类交互的接口。常用的成员
函数
有: OnNewDocument(): 建立新文档。 OnOpenDocument(): 打开一个文档。 OnCloseDocument(): 关闭文档。 OnSaveDocument(): 保存文档。 UpdateAllView(): 通知所有视图文档被修改。 SaveModified(): 设置文档修改标志。 CView类 从CWnd类派生而来,是MFC视图类和用户视图类的基类。CWnd::Invalidate()或CWnd::InvalidateRect()可以刷新视图。常用
函数
有: GetDocument(): 视图类对象访问文档类对象中的数据的. OnDraw(): 这个
函数
有一个指向CDC类的指针参数, 通过它可能直接
调用
CDC类上显示数据和图形. 在应用程序窗口出现在及大小发生变化时, 系统将
自动
调用
OnDraw
函数
OnInitialUpdate(): 作一些初始化工作. 程序员的主要工作 (1) 重写
Win
App派生类的虚
函数
InitInstance.在这个
函数
中,按自己的需要创建和显示窗口. (2) 在CDocument的派生类中,声明程序所需的数据和对这些数据进行必要操作的接口
函数
. (3) 在CViwe类的派生类中编写处理消息的代码.如果在消息处理中需要文档的数据,应该
调用
该类的成员
函数
GetDocument来获取文档对象,然后通过文档对象的接口
函数
对文档中的数据进行操作. (4) 在CViwe类的派生类中的OnDraw
函数
中编写窗口重绘时的代码. Gilbert觉得以上是很大的类, 下面介绍一些小类: CRect类 矩形类,拥有四个成员变量:top, left, bottom, right。分别表是左上角和右下角的坐标。可以通过以下的方法构造: CRect( int l, int t, int r, int b ); 指明四个坐标 CRect( const RECT& srcRect ); 由RECT结构构造 CRect( LPCRECT lpSrcRect ); 由RECT结构构造 CRect( POINT point, SIZE size ); 有左上角坐标和尺寸构造 CRect( POINT topLeft, POINT bottomRight ); 有两点坐标构造 它的几个成员
函数
: int Width( ) const; 得到宽度 int Height( ) const; 得到高度 CSize Size( ) const; 得到尺寸 CPoint& TopLeft( ); 得到左上角坐标 CPoint& BottomRight( ); 得到右下角坐标 CPoint CenterPoint( ) const; 得当中心坐标 此外矩形可以和点(CPoint)相加进行位移,和另一个矩形相加得到“并”操作后的矩形。 CPoint类 点的坐标,有两个成员变量:x, y。可以和另一个点相加。 CString类 表示可变长度的字符串。使用CString可不指明内存大小,CString会根据需要自行分配。几个成员
函数
: GetLength 得到字符串长度 operator + 相当于strcat Compare 比较 CompareNoCase 不区分大小写比较 MakeUpper 改为小写 MakeLower 改为大写
C
Win
Thread
::
Run
() 简单理解
MFC的控制中心――C
Win
Thread
::
Run
() 说C
Win
Thread
::
Run
()是MFC的控制中心,一点也没有夸大。在MFC中,所有来自于消息队列的消息的分派都是在C
Win
Thread
::
Run
()
函数
中完成的,同 Afx
Win
Main()一样,这个
函数
也是对程序员是不可见的,其道理同Afx
Win
Main()的一样。 首先要提的一点是,
VC++ 消息循环
Run
函数
PumpMessage
函数
AfxInternalPumpMessage
函数
至此,注册窗口类、创建窗口、显示和更新窗口的工作都已完成,就该进入消息循环了。C
Win
Thread
类的
Run
函数
就是完成消息循环这一任务的,该
函数
是在Afx
Win
Main
函数
中
调用
的,
调用
形式如下(位于 Afx
Win
Main
函数
实现代码处)所示。 thrdcore.cpp: nResult = p
Thread
->
Run
(); Afx
Win
Main
函数
(
win
main.cpp): 连接:VC++MFC程序中的
Win
Main
函数
(四) Afx
Win
Main
函数
AfxGet
Thread
函数
.
非技术类
1,650
社区成员
58,980
社区内容
发帖
与我相关
我的任务
非技术类
VC/MFC 非技术类
复制链接
扫一扫
分享
社区描述
VC/MFC 非技术类
社区管理员
加入社区
获取链接或二维码
近7日
近30日
至今
加载中
查看更多榜单
社区公告
暂无公告
试试用AI创作助手写篇文章吧
+ 用AI写文章