MFC的执行过程分析
MFC程序的执行细节剖析
MFC程序也是Windows程序,所以它应该也有一个WinMain。可是在程序中看不到它的踪影。事实上在程序进入点之前。另一个(并且仅有一个)全局对象(theApp)。这就是所谓的Application object,当操作系统将程序载入并激活时,这个全局对象获得配置,其构造函数会先运行。比WinMain更早。
一 CWinApp代替WinMain
CWinApp的派生对象被称为application object,能够想见,CWinApp本身就代表一个程序本体。所谓程序本体是与程序本身有关而不与窗体有关的数据或动作。CWinApp类定义例如以下:
class CWinApp :
public CWinThread
{
//startup args
HINSTANCE m_hInstance;
HINSTANCE m_hPrevInstance;
LPTSTR m_lpCmdLine;
int m_nCmdShow;
//Runing args
CWnd* m_pMainWnd;
CWnd* m_pActiveWnd;
LPCTSTR m_pszAppName;
LPCTSTR m_pszRegistryKey;
public:
LPCTSTR m_pszExeName; //executable name
LPCTSTR m_pszHelpFilePath; //default based on module path
LPCTSTR m_pszProfileName; //default based on app name
public:
virtual BOOL InitApplication();
//overrides for implementation
virtual BOOL InitInstance();
virtual
int ExitInstance();
virtual
int Run();
virtual BOOL OnIdle(LONG lCount);
};
传统意义上SDK程序的WinMain所完毕的工作如今由CWinApp的三个函数完毕:
virtual BOOL InitApplication();
virtual BOOL InitInstance();
virtual int Run();
那么WndProc函数到哪里去了呢?CFrameWnd主要用来掌握一个窗体,你差点儿能够说它是用来代替SDK程序中的窗体函数的地位。
MFC通过内建了一个所谓的Message Map机制,会把消息自己主动送到“与消息相应的特定函数”中去。
有关Message Map实现机制请看另外一篇博客。
二 程序执行过程分析
如图所看到的,左半边MFC程序代码。右半部是我们自己写的程序代码。
当程序開始运行时,theApp这个全局对象产生,于是构造函数运行起来。CWinApp的构造函数例如以下
CWinApp::CWinApp(LPCSTR lpszAppName)
{
m_pszAppName = lpszAppName;
//initialize CWinThread state
AFX_MODULE_THREAD_STATE* pThreadState = AfxGetModuleThreadState();
pThreadState->m_pCurrentWinThread =
this;
m_hThread = ::GetCurrentThread();
m_nThreadID = ::GetCurrentThreadId();
//initialize CWinApp state
AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
pModuleState->m_pCurrentWinApp = this;
//in non-running state until WinMain
m_hInstance = NULL;
m_pszHelpFilePath = NULL;
........
}
CWinApp之中的成员变量将由于theApp这个全局对象的诞生而获得配置与初值。theApp配置完毕后,WinMain登场。我们并未编写WinMain程序代码,这是MFC早就已经准备好并由链接器直接加到应用程序代码中:
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, LPTSTR lpCmdLine, int nCmdShow)
{
int nReturnCode = -1;
CWinApp* pApp = AfxGetApp();
AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
pApp->InitApplication();
pApp->InitInstance();
nReturnCode = pApp->Run;
AfxWinTerm();
return nReturnCode;
}
当中。AfxGetApp是一个全局函数,定义域AFXWIN1.INL中:
_AFXWIN_INLINE CWinApp* AFXAPI AfxGetApp()
{ return afxCurrentWinApp; }
#define afxCurrentWinApp AfxGetModuleState()->m_pCurrentWinApp
依据之前描写叙述的CWinApp::CWinApp()中的操作,能够知道AfxGetApp事实上就是取得CMyWinApp对象指针。所以,AfxWinMain中这种操作:
CWinApp* pApp = AfxGetApp();
pApp->InitApplication();
pApp->InitInstance();
nReturnCode = pApp->Run;
事实上就相当于调用:
CMyWinApp::InitApplication();
CMyWinApp::InitInstance();
CMyWinApp::Run();
因而导致调用:
CWinApp::InitApplication();
CMyWinApp::InitInstance();//改写了父类的该方法,调用子类的
CWinApp::Run();
AfxWinInit——AFX内部初始化操作
AfxWinInit是继CWinApp构造函数之后的第一个操作。下面是它的操作摘要
BOOL AFXAPI AfxWinInit(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
ASSERT(hPrevInstance == NULL);
//set resource handles
AFX_MODULE_STATE* pState = AfxGetModuleState();
pState->m_hCurrentInstanceHandle = hInstance;
pState->m_hCurrentResourceHandle = hInstance;
//fill in the initial state for the application
CWinApp* pApp = AfxGetApp();
if(pApp != NULL)
{
//windows spcific initialization
pApp->m_hInstance = hInstance;
pApp->m_hPrevInstance = hPrevInstance;
pApp->m_lpCmdLine = lpCmdLine;
pApp->m_nCmdShow = nCmdShow;
pApp->SetCurrentHandles();
}
//initialize thread specific data
if(!afxContexIsDLL)
{
AfxInitThread();
}
return TURE;
}
CWinApp::InitApplication.
BOOL CWinApp::InitApplication()
{
if(CDocManager::pStaticDocManager != NULL)
{
if(m_pDocManager == NULL)
{
m_pDocManager = CDocManager::pStaticDocManager;
}
CDocManager::pStaticDocManager = NULL;
}
if(m_pDocManager != NULL)
{
m_pDocManager->AddDocTemplate(NULL);
}
else
{
CDocManager::bStaticInit = FALSE;
}
return TRUE;
}
这些都是MFC为了内部管理而做的。
CMyWinApp::InitInstance
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
继InitApplication之后。调用InitInstance即调用CMyWinApp::InitInstance();
也就是调用我们重写后的InitInstance函数。
CFrameWnd::Create产生主窗体(并先注冊窗体类)
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
CMyWinApp::InitInstance一開始new了一个CMyFrameWnd对象,准备用作主框窗体的c++对象。
会调用CMyFrameWnd类的构造函数,从而调用Create函数它将产生一个窗体。在调用Create函数的过程中Create会调用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;
PreCreateWindow(cs);
AfxHookWindowCreate(this);
HWND hWnd = ::CreateWindow(.....);
........
}
到这里可能熟悉SDK编程的都会问那么窗体类呢,事实上窗体类在Create函数调用时窗体类參数传的NULL代表将以MFC内建的窗体类产生一个标准的外框窗体。
窗体显示与更新
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
CMyFrameWnd::CMyFrameWnd结束后。窗体已经诞生出来;程序流程又回到了CmyWinApp::InitInstance。于是调用ShowWindow函数令窗体显示出来,并调用UpdateWindow函数令hello程序发送出WM_PAINT消息。
我们非常关心这个WM_PAINT消息是怎样送到窗体函数的手中。并且,窗体函数又在哪里?
MFC程序是不是也像SDK程序一样。有一个GetMessage/DispatchMesage循环?
CWinApp::Run
程序执行到这,接下来将执行pApp->Run()即相当于调用CMyWinApp::Run();
Run函数是CWinApp的一个虚函数,并且我们没有改写它。所以上述操作相当于调用CWinApp::Run();通过研读Run函数实现代码它会调用PumpMessage()函数。在这个函数中就是真正处理消息循环函数。
三 整理
1.程序的诞生:
Application object 产生,内存于是获得配置,初值亦设立了。AfxWinMain
运行AfxWinInit,后者有调用AfxInitThread,把消息队列尽量加大到96。
AfxWinMain运行InitApplication。
这是CWinApp的虚函数,我们通常不改写它。
AfxWinMain运行InitInstance。
这是CWinApp的虚函数,我们必须改写它。
CMyWinApp::InitInstance new 了一个CMyFrameWnd对象。
CMyFrameWnd构造函数调用Create,产生主窗体。
回到InitInstance中继续运行ShowWindow。显示窗体。
运行UpdateWindow。于是发出WM_PAINT。
回到AfxWinMain。运行Run,进入消息循环。
2.程序開始执行:
程序获得WM_PAINT消息(藉由CWinApp::Run中的::GetMessage循环)。
WM_PAINT经由::DispatchMessage送到窗体函数CWnd::DefWindowProc
中。
CWnd::DefWindowProc将消息传递过消息映射表格(Message Map)。
传递过程中发现有相符项目,于是调用项目中相应的函数。此函数是应用程序利用BEGIN_MESSAGE_MAP和END_MESSAGE_MAP之间的宏设立起来的。
标准消息的处理程序亦有标准命名。比如WM_PAINT必定由OnPaint处理。
3.程序的死亡
使用者单击【File/Close】,于是发出WM_CLOS。
CMyFrameWnd并没有设置WM_CLOSE处理程序。于是交给默认的处理程序。
默认的处理函数对于WM_CLOSE的处理方式是调用::DestroyWindow,并因
而发出WM_DESTROY。
CWinApp::Run收到WM_QUIT后会结束其内部消息循环。然后调用
ExitInstance,这是CWinApp的一个虚拟函数。
最后回到AfxWinMain,运行AfxWinTerm,结束程序。
MFC的执行过程分析的更多相关文章
- ASP.NET MVC应用程序执行过程分析
ASP.NET MVC应用程序执行过程分析 2009-08-14 17:57 朱先忠 朱先忠的博客 字号:T | T ASP.NET MVC框架提供了支持Visual Studio的工程模板.本文 ...
- Ansible系列(七):执行过程分析、异步模式和速度优化
本文目录:1.1 ansible执行过程分析1.2 ansible并发和异步1.3 ansible的-t选项妙用1.4 优化ansible速度 1.4.1 设置ansible开启ssh长连接 1.4. ...
- MFC程序执行过程剖析
一 MFC程序执行过程剖析 1)我们知道在WIN32API程序当中,程序的入口为WinMain函数,在这个函数当中我们完成注册窗口类,创建窗口,进入消息循环,最后由操作系统根据发送到程序窗口的消息调用 ...
- MFC程序执行过程剖析(转)
一 MFC程序执行过程剖析 1)我们知道在WIN32API程序当中,程序的入口为WinMain函数,在这个函数当中我们完成注册窗口类,创建窗口,进入消息循环,最后由操作系统根据发送到程序窗口的消息调用 ...
- MFC程序执行顺序 .
1.创建Application object对象theApp 程序一开始生产一个(且只有一个)Application object对象theApp,也即一个CWinApp对象,这个全局对象一产生,便执 ...
- CDH集群spark-shell执行过程分析
目的 刚入门spark,安装的是CDH的版本,版本号spark-core_2.11-2.4.0-cdh6.2.1,部署了cdh客户端(非集群节点),本文主要以spark-shell为例子,对在cdh客 ...
- http执行过程分析
执行过程: 1.用户在浏览器(客户端)里输入或者点击一个网址链接: 2.浏览器通过网址域名查找ip地址.DNS查找方式是通过浏览器缓存(会记录DNS记录)→系统缓存→TCP/IP参数中设置的首选DNS ...
- twitter storm源码走读之6 -- Trident Topology执行过程分析
欢迎转载,转载请注明出处,徽沪一郎. TridentTopology是storm提供的高层使用接口,常见的一些SQL中的操作在tridenttopology提供的api中都有类似的影射.关于Tride ...
- u-boot、kernel和filesystem 执行过程分析
标题: Uboot -kerne-root 启动流程 内容: ※uboot启动流程 ※Kernel启动流程 ※Root启动流程 ※构建根文件系统 /************************** ...
随机推荐
- tigergao
互联网从业 6 年.前码农&DBA,现运维&电商创业者,也在做自媒体.终生学习者. 运营微信公众号:高哥咋么看 感兴趣的朋友们可以订阅.
- HTML提交表单
一.使用form提交表单 <form action="#" method="post"> {% csrf_token %} 班级<input ...
- BZOJ 1951 [SDOI2010]古代猪文 (组合数学+欧拉降幂+中国剩余定理)
题目大意:求$G^{\sum_{m|n} C_{n}^{m}}\;mod\;999911659\;$的值$(n,g<=10^{9})$ 并没有想到欧拉定理.. 999911659是一个质数,所以 ...
- java redistemplate
//添加一个 key ValueOperations<String, Object> value = redisTemplate.opsForValue(); value.set(&quo ...
- gRPC全局异常捕获
gRPC全局异常捕获 引 一般的.net项目比如ASP.NET.控制台程序.Windows服务.桌面程序等都会有framework自带的全局异常捕获机制.ASP.NET的ExceptionFilter ...
- Linux用户与用户组
Linux用户与用户组 Linux系统是一个多用户多任务的分时操作系统,任何一个要使用系统资源的用户,都必须首先向系统管理员申请一个账号,然后以这个账号的身份进入系统. 用户的账号一方面可以帮助系统管 ...
- ASP.NET-Razor语法03
ASP.NET MVC中使用Razor语法 @{} @{ string s ="super xiao lizi"; @s; // 里面的这个@代表着在页面上输出这个s // 我记 ...
- BZOJ 2683 简单题 cdq分治+树状数组
题意:链接 **方法:**cdq分治+树状数组 解析: 首先对于这道题,看了范围之后.二维的数据结构是显然不能过的.于是我们可能会考虑把一维排序之后还有一位上数据结构什么的,然而cdq分治却可以非常好 ...
- Android控件篇
Android中提供了丰富的UI空间.为了最大限度地发挥平台的性能.每个开发人员必须熟练掌握UI控件尤其是经常使用的UI控件.并能依据须要呈现的内容选择最恰当的控件. Android提供了XML配置和 ...
- bzoj1003: [ZJOI2006]物流运输(DP+spfa)
1003: [ZJOI2006]物流运输 题目:传送门 题解: 可以用spfa处理出第i天到第j都走这条路的花费,记录为cost f[i]表示前i天的最小花费:f[i]=min(f[i],f[j-1] ...