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的执行过程分析的更多相关文章

  1. ASP.NET MVC应用程序执行过程分析

    ASP.NET MVC应用程序执行过程分析 2009-08-14 17:57 朱先忠 朱先忠的博客 字号:T | T   ASP.NET MVC框架提供了支持Visual Studio的工程模板.本文 ...

  2. Ansible系列(七):执行过程分析、异步模式和速度优化

    本文目录:1.1 ansible执行过程分析1.2 ansible并发和异步1.3 ansible的-t选项妙用1.4 优化ansible速度 1.4.1 设置ansible开启ssh长连接 1.4. ...

  3. MFC程序执行过程剖析

    一 MFC程序执行过程剖析 1)我们知道在WIN32API程序当中,程序的入口为WinMain函数,在这个函数当中我们完成注册窗口类,创建窗口,进入消息循环,最后由操作系统根据发送到程序窗口的消息调用 ...

  4. MFC程序执行过程剖析(转)

    一 MFC程序执行过程剖析 1)我们知道在WIN32API程序当中,程序的入口为WinMain函数,在这个函数当中我们完成注册窗口类,创建窗口,进入消息循环,最后由操作系统根据发送到程序窗口的消息调用 ...

  5. MFC程序执行顺序 .

    1.创建Application object对象theApp 程序一开始生产一个(且只有一个)Application object对象theApp,也即一个CWinApp对象,这个全局对象一产生,便执 ...

  6. CDH集群spark-shell执行过程分析

    目的 刚入门spark,安装的是CDH的版本,版本号spark-core_2.11-2.4.0-cdh6.2.1,部署了cdh客户端(非集群节点),本文主要以spark-shell为例子,对在cdh客 ...

  7. http执行过程分析

    执行过程: 1.用户在浏览器(客户端)里输入或者点击一个网址链接: 2.浏览器通过网址域名查找ip地址.DNS查找方式是通过浏览器缓存(会记录DNS记录)→系统缓存→TCP/IP参数中设置的首选DNS ...

  8. twitter storm源码走读之6 -- Trident Topology执行过程分析

    欢迎转载,转载请注明出处,徽沪一郎. TridentTopology是storm提供的高层使用接口,常见的一些SQL中的操作在tridenttopology提供的api中都有类似的影射.关于Tride ...

  9. u-boot、kernel和filesystem 执行过程分析

    标题: Uboot -kerne-root 启动流程 内容: ※uboot启动流程 ※Kernel启动流程 ※Root启动流程 ※构建根文件系统 /************************** ...

随机推荐

  1. BZOJ 4199: [Noi2015]品酒大会 后缀自动机_逆序更新

    一道裸题,可以考虑自底向上去更新方案数与最大值. 没啥难的 细节........ Code: #include <cstdio> #include <algorithm> #i ...

  2. Spring 与CXF整合(spring3.2,cxf3.1.11)

    1,jar包导入,从官网下载zip文件后里面会有很多jar包,哪些必须哪些不是必须,我能力有限,从其他人那参考了导入的一下jar包. 2.配置相关文件 web.xml中配置servlet <se ...

  3. pyinstall 常见错误

    字符编码错误: https://blog.csdn.net/weixin_42426496/article/details/81102665 https://blog.csdn.net/qq_4206 ...

  4. vue mint-ui swipe 不显示或显示空白

    vue mint-ui swipe 不显示或显示空白? 解决需要在mt-swipe上层元素设置高度 <div> <div> <mt-header title=" ...

  5. luogu P1622 释放囚犯

    题目描述 Caima王国中有一个奇怪的监狱,这个监狱一共有P个牢房,这些牢房一字排开,第i个紧挨着第i+1个(最后一个除外).现在正好牢房是满的. 上级下发了一个释放名单,要求每天释放名单上的一个人. ...

  6. 批量删除harbor中的镜像

    一 说明 这个是我第一篇博客,所以我想放上原创的东西,尽管我一直都很担心自己写得太low,但是总要学会尝试,学会改变自己,相信自己.在写这个脚本时,由于我接触LInux不是很多,能力有限,仅仅是为了让 ...

  7. Java取得环境变量和系统属性

    取得所有的环境变量 public class GetEnvAndProp { public static void main(String[] args) { Map<String, Strin ...

  8. SpringBoot实战(四)获取接口请求中的参数(@PathVariable,@RequestParam,@RequestBody)

    上一篇SpringBoot实战(二)Restful风格API接口中写了一个控制器,获取了前端请求的参数,现在我们就参数的获取与校验做一个介绍: 一:获取参数 SpringBoot提供的获取参数注解包括 ...

  9. ifram 实现左侧菜单,右侧显示内容

    一般都是左侧的导航栏中的a标签中写一个target(a标签有target属性), 右侧的div标签中写一个iframe,在iframe中有name的属性,在左侧a标签中的target写上iframe中 ...

  10. 转发真阿当老师的一片文章 受益匪浅 (出处:http://cly84920.blog.163.com/blog/static/24750013320158203575958/)

    忽悠程序员做一辈子程序员,以白胡子白头发hacker为目标的人有两种: 1,自己不写程序,但需要有将才为自己打下手的人,这种人往往看他资质和勤奋均平平,却成了你领导.别不服,这种人虽不见得有帅才的能力 ...