[转]《深入浅出MFC》– MFC程序的生死因果
1.首先MFC程序需要哪些函数库?
Windows C Runtime函数库
LIBC.LIB C Runtime函数库的静态链接版本
MSVCRT.LIB C Runtime函数库的动态链接版本
MSVCRTD.LIB ‘D’表示使用于Debug方式
DLL Import函数库:GDI32.LIB、USER32.LIB、KERNEL32.LIB…
MFC函数库:MFC42.LIB、MFC42D.LIB、MFCS42.LIB、MFCS42D.LIB、MFCN42D.LIB、MFCD42D.LIB、MFCO42.LIB…
2.MFC程序需要哪些头文件?
①STDAFX.H:这个文件用来作为Precompiled header file,其内只是载入其他的MFC头文件。
②AFXWIN.H:每一个Windows MFC程序都必须载入它,因为它以及它所载入的文件声明了所有的MFC类。此文件内涵AFX.H,后者又载入AFXVER_.H,后者又载入AFXV_W32.H,后者又载入WINDOWS.H。
③AFXEXT.H 使用工具栏、状态栏的程序必须载入这个文件。
④AFXDLGS.H 凡使用通用对话框(Common dialog)的MFC程序需要载入此文件。它内部包含COMMDLG.H
⑤FXCMN.H 凡使用windows 9x新增通用行控件(common control)的MFC程序需要载入此文件。
⑥AFXCOLL.H 凡使用MFC提供的容器都需要载入此文件。
⑦AFXDLLX.H 凡使用MFC extension DLLs需要载入此文件。
⑧AFXRES.H MFC程序的RC文件必须载入此文件。此文件中对于标准资源的ID都有默认值。它们定义于此文件中。
3.afx_msg和CALLBACK的意义:
#define afx_msg //故意安排的一个位置,也许以后版本会用到
#define CALLBACK __stdcall //一种函数的调用习惯
4.MFC把有着固定行为的WinMain内部操作封装在CWinApp中,把有着相当固定行为的WndProc内部操作封装在CFrameWnd中。也就是CWinApp代表程序主体,CFrameWnd代表一个主框窗口(Frame Window)。
几乎可以说CWinApp用来取代WinMain在SDK程序中地位。这并不是说MFC程序没有WinMain,而是传统上SDK程序的WinMain所完成的工作现在由CWinApp的三个函数完成:
virtual bool InitApplication();
virtual bool InitInstance();
virtual int Run();
从MFC 4.x开始,m_pMainWnd已经被移往CWinThread中了。
5.MFC内建了一个所谓的Message Map机制,会把消息自动送到“与消息对应的特定函数”中去,消息与处理函数之间的对应关系由程序猿指定。
BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)
ON_WM_PAINT()
ON_COMMAND(IDM_ABOUT, OnAbout)
END_MESSAGE_MAP()
6.全局对象—Application Object
上图的theApp就是Hello程序的application object,每一个MFC应用程序都有一个,而且也只有这么一个。当你执行Hello时,这个全局对象产生,于是构造函数执行起来。我们并没有定义CMyWinApp构造函数,至于其父类CWinApp的构造函数内容如下:
CWinApp:: CWinApp(LPCTSTR lpszAppName) { m_pszAppName = lpszAppName; AFX_MODULE_THREAD_STATE * pThreadState = AfxGetMoudleThreadState(); pThreadState->m_pCurrentWinThread = this; m_hThread = ::GetCurrentThread(); m_nThreadID = ::GetCurrentThreadId(); AFX_MOUDLE_STATE * pModueState = AfxGetModuleState(); pModuleState->m_pCurrentWinApp = this; m_hInstance = NULL; m_pszHelpFilePath = NULL; m_pszProfileName = NULL; m_pszRegistryKey = NULL; m_pszExeName = NULL; m_lpCmdLine = NULL; m_pCmdInfo = NULL; … }
从源代码中可以看出cwinapp的构造函数主要收集了一些关于应用程序主线程的信息及初始化一些相关应用程序的信息。值得注意的是cwinapp类的一些主要的数据成员如:m_hinstance,m_lpcmdline,m_pcmdinfo及m_atomapp等都初始化为null,这些成员在后面将被重新赋值。
7.隐晦不明的WinMain
theApp配置完成后,WinMain登场。我们并未撰写WinMain程序代码,这是MFC早已准备好并由链接器直接加到应用程序代码中的,其程序代码如下。_tWinMain函数的 “-t”是为了支持Unicode而准备的一个宏。
extern "C" int WINAPI
_tWinMain(HINSTANCE hinstance, HINSTANCE hPrevInstance,LPTSTR lpCmdLine,int nCmdShow)
{
Return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
} int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow )
{
ASSERT( hPrevInstance == NULL ); int nReturnCode = -; CwinApp *pApp = AfxGetApp(); if(!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow)) goto InitFailure; ASSERT_VALID(pApp); if(!pApp->InitApplication()) goto InitFailure; ASSERT_VALID(pApp); if(!pApp->InitInstance())
{
if(pApp->m_pMainWnd != NULL)
{
TRACE0("Warning:Destroying non-NULL m_pMainWnd\n");
pApp->m_pMainWnd->DestroyWindow();
} nReturnCode = pApp->ExitInstance();
goto InitFailure;
} ASSERT_VALID(pApp);
nReturnCode = pApp->Run();
ASSERT_VALID(pApp);
InitFailure: AfxWinTerm();
Return nReturnCode;
}
其中AfxGetApp是一个全局函数,定义域AFXWIN1.INL中:
_AFXWIN_INLINE CWinApp * AFXAPI AfxGetApp()
{ return afxCurrentWinApp ;}
而afxCurrentWinApp 又定义于AFXWIN.H中:
#define afxCurrentWinApp
AfxGetModuleState()->m_pCurrentWinApp
AfxGetApp其实就是取得CMyWinApp对象指针。
所以上面相当于
CMyWinApp::InitApplication();
-> CWinApp::InitApplication();
CMyWinApp::InitInstance();
CMyWinApp::Run();
-> CWinApp::Run();
8.AfxWinInit:AFX内部初始化操作
前面说过:”CWinApp类的一些主要的数据成员在后面将被重新赋值。”,AfxWinInit函数就是这些数据成员被赋值的地方,它重新初始化这些在整个程中都扮演重要角色的成员,并且调用AfxInitThread()为主线程作了一些初始化工作,这些都为以后mfc框架的正常运作铺平了道路。
9.CWinApp::InitApplication
上面的pApp->InitApplication()即为CMyWinApp::InitApplication也就是CWinApp::InitApplication。
10.CMyWinApp::InitInstance
接着调用pApp->InitInstance.pApp指向CMyWinApp对象,由于我们改写了它,所以即为调用CMyWinApp->InitInstance.
11.CFrameWnd::Create产生主窗口
MFC构造了CMyFrameWnd,在CMyFrameWnd的构造函数内调用了Create函数,它产生一个窗口,它需要八个参数,其中六个都有了默认值。只有前两个需要指定。
第一个参数lpszClassName,用以指定WNDCLASS类,使用NULL表示使用MFC内建的窗口类产生一个标准的窗口,但是此时我们并没与发现窗口注册的操作。因为在Create函数内会调用注册窗口类的函数。这稍候会做介绍。
第二个参数:lpszWindowName,指定窗口标题。
第三个参数指定窗口风格。
第四个参数指定窗口的位置与大小。默认值rectDefault是CFrameWnd的一个static成员变量。告诉windows以默认方式指定窗口位置和大小。 同时也可以手动指定,如CRect(40,20,240,460);
第五个参数用以指定父窗口。对于一个顶层窗口来说它应该为NULL,表示它没有父窗口。但是其实它是有的,它的父窗口是desktop窗口。
第六个参数指定菜单,它是在RC文件中定义的。
第八个参数pContext指向CCreateContext结构指针,MFC利用它在文档视图结构初始化外框窗口。 不具备文档视图结构的程序此值为NULL.
前面提到,CFrameWnd::Create在产生窗口之前,会引发窗口类的注册操作。
Create是CFrameWnd的成员函数,在Create函数中又调用了CreateEx,因为派生类CFrameWnd没有改变CWind的虚函数CreateEx,所以其实调用的是CWnd::CreateEx.
然后我们看CreateEx源码会发现调用了PreCreateWindow();这是个虚函数,在CWnd和CFrameWnd都有定义,由于this指针的缘故,这里调用的是CFrameWnd::PreCreateWindow。
继续跟入可以发现AfxDeferRegisterClass()的调用。这是一个定义于AFXIMAPL.H的宏。
#define AfxDeferRegisterCalss(fClass)\
((afxRegisteredClasses&fClass)?true:AfxEndDeferRegisterClass(fClass);
这个宏表示如果afxRegisterClasses的值显示系统已经注册了fClass,窗口类就啥也不做。否则就调用AfxEndDeferRegisterClass注册该窗口类。
afxRegisterClasses是一个旗标变量,用来记录已经注册了哪些窗口类。
#define afxRegisterClasses
AfxGetModuleState->m_fRegisteredClasses();
我们看AfxEndDeferRegisterClass的操作的话,它调用两个函数完成实际的窗口类注册操作,一个是RegisterWithIcon,一个是AfxRegisterClass,最终都是RegisterClass。
注意,不同的类PreCreateWindow成员函数都是在窗口产生之前一刻被调用,准备用来注册窗口类。如果我们制定的窗口类是NULL,那么就使用系统默认类。从CWnd及其各个派生类的PreCreateWindow成员函数可以看出,整个Framework针对不同功能的窗口使用不同的窗口类。
12.窗口显示与更新
CMyFrameWnd::CMyFrameWnd结束后,窗口已经诞生出来,程序流程又回到CMyFrameWnd::InitInstance,于是调用ShowWindow函数令窗口显示出来,并调用UpdateWindow函数令Hello程序送出WM_PAINT消息。
13.CWinApp::Run — 程序生命的活水源头
pApp指向CMyWinApp对象,也就是本例的theApp,所以,当程序调用:pApp->Run();相当于调用:CMyWinApp::Run();虚函数,没有改写,所以调用CWinApp::Run().
int CWinApp::Run() { if(m_pMainWnd==NULL&&AfxOleGetUserCtrl()) { AfxPostQUitMessage(); } return CWinThread::Run(); }
int cwinthread::run()
{
assert_valid(this);
_afx_thread_state* pstate = afxgetthreadstate(); // for tracking the idle time state
bool bidle = true;
long lidlecount = ; // 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 = ;
} } while (::peekmessage(&(pstate->m_msgcur), null, null, null, pm_noremove));
}
} Bool CWinThread::PumpMessage()
{
If(!::GetMessage(&m_msgCur,NULL,NULL,NULL)
{
Return false;
}
If(m_msg.message!=WM_KICKIDLE&&
!PreTranslateMessage(&m_msgCur)
{
::TranslateMessage(&m_msgCur);
::DispatchMessage(&m_msgCur);
}
}
窗口函数实际上也是由MFC提供的,DefWindowProc函数,但事实上消息并不是被送往该处,而是一个名为AfxWndProc的全局函数。
14.把消息与处理函数连接在一起:Message Map机制
MFC把消息主要分为三大类,Message Map机制中对于消息与函数间的对应关系也明定以下三种:
①标准Windos消息(WM_xxx)的对应规则
②命令消息(WM_COMMAND)的一般性对应规则是:
ON_COMMAND(IDM_ABOUT,OnAbout);
ON_COMMAND(ID_FILEOPEN,OnFileOpen);
③Notification消息(由空间产生,例如BN_xxx)的对应机制的宏分为好几种。
15.来龙去脉总整理
①程序的诞生:
1.Application object 产生, 内存于是获得配置,初值亦设立了。
2.AfxWinMain执行AfxWinInit,后者又调有AfxInitThread,把消息队列尽量加大到96
3.AfxWinMain执行InitApplication。这是CWinApp的虚拟函数,我们通常不改写它
4.AfxWinMain执行InitInstance。这是CWinApp的虚拟函数,我们必须改写它
5.CMyWinApp::InitInstance “new”了一个CMyFrameWnd对象
6.CmyFrameWnd构造函数调用Create,产生主窗口。我们在Create参数中指定的窗口类是NULL,于是MFC根据窗口种类,自行为我们注册一个名为“AfxFrameOrView42d”的窗口类。
7.回到InitInstance中继续执行ShowWindow,显示窗口
8.执行UpdateWindow,于是发出WM_PAINT
9.回到AfxWinMain,执行Run,进入消息循环。
②程序开始运行:
1.程序获得WM-PAINT消息(由CWinApp::Run中的::GetMessage循环)
2.WM-PAINT经由::DispatchMessage送到窗口函数CWnd::DefWindowProc中。
3.CWnd::DefWindowProc将消息传递到消息映射表格
4.传递过程中发现有相符项目,于是调用项目中对应的函数。此函数是利用BEGIN_MESSAGE_MAP和END_MESSAGE_MAP之间的宏设立起来的。
5.标准消息的处理程序亦有标准命名,例如WM_PAINT必由OnPaint处理
③程序的死亡:
1.使用者单击File/Close,于是发出WM-CLOSE
2.CMyFrameWnd并没有设置WM-CLOSE处理程序,于是交给默认的处理程序
3.默认函数对于WM-CLOSE的处理方式是调用::DestroyWindow,并因而发出WM_DESTROY
4.默认的WM-DESTROY处理方式是调用::PostQuitMessage,因此发出WM_QUIT
5.CWinApp::Run收到WM_QUIT后会结束内部之消息循环,然后调用ExitInstance,这是CWinApp的一个虚拟函数;
6.如果CMyWinApp改写了ExitInstance,那么CWinApp::Run所调用的就是CMyWinApp::ExitInstance,否则就是CWinApp:::ExitInstance
7.最后回到AfxWinMain,执行AfxWinTerm,结束程序.
16.如果你的MFC程序也想处理idle time,只要改写CWinApp派生类的OnIdle函数即可。
原文链接:http://www.blogfshare.com/mfc-five.html
[转]《深入浅出MFC》– MFC程序的生死因果的更多相关文章
- 深入浅出MFC——MFC程序的生死因果(三)
1. 本章主要目的:从MFC程序代码中检验出一个Windows程序原本该有的程序进入点(WinMain).窗口类注册(RegisterClass).窗口产生(CreateWindow).消息循环(Me ...
- 【笔记】《深入浅出MFC》第6章 MFC程序的生死因果
一.头文件说明 STDAFX.H 这个文件用来作为Precompile header file,其内只是载入其他的MFC头文件.应用程序通常会准备自己的头STDAFX.H. AFXWIN.H 每一个W ...
- 深入浅出MFC——MFC骨干程序(四)
1. 熟记MFC类层次结构: 2. AppWizard可以为我们制作出MFC程序骨干: 3. Document/View支撑你的应用程序:Document/View的价值在于,这些MFC类已经把一个应 ...
- [转]深入理解MFC中程序框架
最近抽空复习了一下MFC的内容,觉得一篇博文写的不错. 原文内容太多直接给出链接吧:深入理解MFC中程序框架 链接2:深入浅出话VC++(2)——MFC的本质 链接3:MFC单文档/视图结构穷追猛打
- DCMTK354之VC++ 2008 MFC应用程序配置完整过程
花了一个礼拜,终于在VC++2008 MFC 应用程序中完成了首个基于DCMTK354的首个程序ECHOSCUWIN32,现将过程记录下来,便于日后查阅,同时也提供给那些有幸看到此博文而对他们又有帮助 ...
- MFC应用程序框架(转)
对于程序员来说,如果要提高编程效率,一个好用的,功能强大的并且可以得心应手使用的编程工具往往会给我们程序员带来莫大的方便.其实对于现在的编程工具来说,使用哪一种工具都不是问题的关键,重要的是你能够使用 ...
- MFC应用程序框架入门(转)
1 MFC概述 顾名思意,MFC应用程序框架是以MFC作为框架基础的,以此程序框架模式搭建起来的应用程序在程序结构组织上是完全不同于以前的Win32 SDK编程方式的.自20世纪90年代初问世以来,M ...
- VS2010/MFC编程入门之四(MFC应用程序框架分析)
VS2010/MFC编程入门之四(MFC应用程序框架分析)-软件开发-鸡啄米 http://www.jizhuomi.com/software/145.html 上一讲鸡啄米讲的是VS2010应用 ...
- MFC应用程序向导生成的文件
比方说我们用Visio Studio创建了一个MFC应用程序,名称为Mysdi.在创建这个项目的时候,默认的会生成许多类和文件,就关于这些文件的内容和要点展开以下论述. 框架窗口类头文件 向导为项目M ...
随机推荐
- 向一个GitHub repository添加协作者
第一步: 在协作者的机器(就是你的电脑啦)上创建一个ssh key (使用命令ssh-keygen) 第二步: 创建一个github账户 第三步: 把public-key添加到你的github用户账户 ...
- 设置Log文件每天生成一个(wamp)
打开 Wamp的 httpd.conf文件 把下面两句话拷贝进去即可: 1.设置错误log的, " 2.设置访问log的 " common 说明:bin/rota ...
- log4j整理
<meta http-equiv="refresh" content="1"/> # log4j日志组件 #- SLF4J,一个**通用日志接口** ...
- git本地创建新分支并推送到远程仓库
1,在当前项目目录,从已有的分支创建新的分支(如从master分支),创建一个dev分支 git checkout -b dev 2,创建完可以查看一下,分支已经切换到dev git branch * ...
- Spring定时器——时间设置规则
平时经常有一些写Spring定时器的需求,但是每次要设置定时器的时间的时候,老是既不住,到处找别人的博客又各种不爽,所以就自己记录一份吧,顺便整理一下. org.springframework.sch ...
- PAT 1037 Magic Coupon
#include <cstdio> #include <cstdlib> #include <vector> #include <algorithm> ...
- 如何取消IntelliJ IDEA打开默认项目配置
一.前言 在前端中,个人比较喜欢使用IntelliJ IDEA开发工具!IntelliJ IDEA是非常强大的开发集成工具,打开IntelliJ IDEA软件默认会打开最近一次的 ...
- ADO.NET Tips
1. SqlCommand.ExecuteScalar Method Executes the query, and returns the first column of the first row ...
- 超简便的ListView中Adapter的写法
对于 ListView 的使用,他有两个重点的部分,一个是下拉刷新和加载更多,这个今天我们不讲,另外一个是 BaseAdapter 的使用,这个是今天的主角,BaseAdapter 中又有 ViewH ...
- FragmentActivity的简单使用
如图是效果图 当 点击下面 不同 的按钮 进入 不同的界面 其中 要一个 主布局当做容器 , 和3个不同的 布局来对应下面的3个按钮界面 主程序的 代码和布局如下 import android.su ...