MFC--非模式对话框程序(mfc框架)

这一部分通过讲一个由MFC向导生成的基于对话框的程序来说明MFC的程序的框架和初始化过程,为什么要从对话框说起来,因为对话框程序是MFC程序中最简单的,尤其是MFC向导生成的对话框程序呢,又是模式对话框,不需要我们运行消息循环,是最简单的MFC程序,所以,对于了解MFC的程序结构,是很好的。对MFC向导生成的程序进行讲解之后呢,我们就通过修改向导生成的MFC框架,生成一个非模式对话框。这一部分说的东西,是围绕着在“MFC---序幕”中谈到的内容,在那篇文章中,我说到了,所以的windows窗口程序都是按照那个结构来运行的,所以掌握那章中的内容,对于学习MFC来说,是相当的重要的。

我们知道在sdk编写的一个窗口程序中,我们知道程序的入口函数是winmain,但是在mfc中我们找不到winmain,由于MFC具有封装了API和一些别的机制,如COM,而且MFC是基于C++的,是一个面向对象的编程平台,所以是通过类和对象来管理程序,那么C***App类就代表了程序,什么意思,我觉得更准确的说是代表了主线程,它的作用就是管理线程,从app类的数据程序中,我们可以看到,有线程id,主窗口指针,程序名,程序实例句柄等等,我们对比在“MFC--序幕”中说的windows程序的结构,当中说明了windows窗口程序的基本结构就是:设计窗口和注册窗口类,创建窗口,显示窗口,更新窗口,消息循环,窗口过程。和窗口有关的都有窗口类来管理,除了设计窗口和注册窗口类,其余的创建,显示,更新,窗口过程,都是由窗口类管理,别的就是由app类来管理,在mfc中窗口的创建过程也是遵循了这个窗口创建的过程,只是它由类来管理了。另外,还要说明的就是mfc底层的结构很复杂,如果我们通过逐句去理解,是很难学习MFC的,但是由于我们知道一个窗口程序的创建过程,那么我们理解的时候,也是通过这个过程来理解,本来MFC也是围绕这个核心来的。仍然,对比“MFC--序幕”,我们来学习MFC创建窗口的过程。

C***App the app

*****winmain****

{

********************

   app.InitApplication()//初始化应用程序,如将程序名,实例句柄等等保存到app的数据成员中等

   BOOL result=app.InitInstance()/别的初始化,命令行解析,窗口创建,添加文档模板等等。

   if(result)

   {

      app.run()/如果InitInstance函数返回TRUE,那么运行消息循环

    }

app.ExitInstance//做线程清理

**********************************

    return **;消息循环退出,程序返回

}

上面这部分,都是在MFC底层实现的,我们看不见,我这里这个模式只是说明了在底层的基本过程就是按照这个过程来实现的,但是实际肯定要比我这个复杂的多,当中的星号*代表了省略的部分。那么在实际的向导生成的程序中,我们只需要注意的就是InitInstance和ExitInstance,run函数是运行消息循环,这个我们不用管,它会把消息送到指定的窗口,我们只用窗口的消息循环处理消息,就可以了。那么现在我们可以看看向导生成的,MFC也公开的函数(MFC中有很多未公开的函数),如下由向导生成的:

BOOL Ctest6App::InitInstance()
{
// 如果一个运行在 Windows XP 上的应用程序清单指定要
// 使用 ComCtl32.dll 版本 6 或更高版本来启用可视化方式,
//则需要 InitCommonControlsEx()。否则,将无法创建窗口。
INITCOMMONCONTROLSEX InitCtrls;
InitCtrls.dwSize = sizeof(InitCtrls);
// 将它设置为包括所有要在应用程序中使用的
// 公共控件类。
InitCtrls.dwICC = ICC_WIN95_CLASSES;
InitCommonControlsEx(&InitCtrls);前面这部分就是为了可以在对话框快中使用各种控件,初始化控件库
CWinApp::InitInstance();///这个是调用基类的InitInstance,必须调用的

AfxEnableControlContainer();是这个对话框支持OLE控件,如ActiveX控件

// 创建 shell 管理器,以防对话框包含
// 任何 shell 树视图控件或 shell 列表视图控件。
CShellManager *pShellManager = new CShellManager;

// 标准初始化
// 如果未使用这些功能并希望减小
// 最终可执行文件的大小,则应移除下列
// 不需要的特定初始化例程
// 更改用于存储设置的注册表项
// TODO: 应适当修改该字符串,
// 例如修改为公司或组织名
SetRegistryKey(_T("应用程序向导生成的本地应用程序"));/在注册表中写入一个位置,可以不要这一句
Ctest6Dlg dlg;                声明了一个对话框对象,模式对话框
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();/显示一个模式对话框
if (nResponse == IDOK)
{
// TODO: 在此放置处理何时用
//  “确定”来关闭对话框的代码
}
else if (nResponse == IDCANCEL)
{
// TODO: 在此放置处理何时用
//  “取消”来关闭对话框的代码
}


// 删除上面创建的 shell 管理器。
if (pShellManager != NULL)
{
delete pShellManager;
}
// 由于对话框已关闭,所以将返回 FALSE 以便退出应用程序,
//  而不是启动应用程序的消息泵。
return FALSE;///由于是模式对话框,不需要我们运行消息循环,所以我们返回FALSE。
}
/窗口的创建和显示都是在app类的InitInstance中实现的。到现在窗口就生成并显示了。剩下的就是窗口过程,那么MFC是通过消息映射来处理消息的,也就是,原来我们是通过在一个wndproc的函数中使用switch和case语句来处理我们要处理的消息,现在呢,mfc对于标准的窗口消息都在内部定义了一个消息和函数的映射关系,当消息到来的时候,我们处理相应消息的函数就可以了,具体关于MFC的消息,会在以后的文章中描述。现在我们就可以生成并运行程序了。

我们再来总结一下:app类的InitInstance----dlg类对象---对象的domodal----显示窗口。从这个最简单的MFC程序我们可以得出结论,我们对MFC的操作,就直接从窗口开始了,也就是从app类的Initinstance开始,窗口创建,显示,更新都在这里边完成,窗口过程,就在我们的窗口类的消息映射中完成。而对于消息循环以及别的东西,MFC都为我们做好了。为了说明前面阐述的,我现在就在此基础上,我将它变成一个非模式对话框,我们都知道非模式对话框和普通窗口几乎一样,也有消息循环,窗口的销毁一样,只是由微小的差别,对话框类是系统预定义的,不需要我们使用WNDCLASSEX来声明和注册。

仍然是从Initinstance开始,非模式对话框的创建和普通窗口一样,使用CreateWindowEx的方法,但是在mfc中,是使用对象的成员函数CreateEx,这和CreateWindowEx是相通的,其实它的底层就是调用这个函数。如下:

BOOL Ctest6App::InitInstance()
{
// 如果一个运行在 Windows XP 上的应用程序清单指定要
// 使用 ComCtl32.dll 版本 6 或更高版本来启用可视化方式,
//则需要 InitCommonControlsEx()。否则,将无法创建窗口。
INITCOMMONCONTROLSEX InitCtrls;
InitCtrls.dwSize = sizeof(InitCtrls);
// 将它设置为包括所有要在应用程序中使用的
// 公共控件类。
InitCtrls.dwICC = ICC_WIN95_CLASSES;
InitCommonControlsEx(&InitCtrls);
CWinApp::InitInstance();

AfxEnableControlContainer();

// 创建 shell 管理器,以防对话框包含
// 任何 shell 树视图控件或 shell 列表视图控件。
CShellManager *pShellManager = new CShellManager;

// 标准初始化
// 如果未使用这些功能并希望减小
// 最终可执行文件的大小,则应移除下列
// 不需要的特定初始化例程
// 更改用于存储设置的注册表项
// TODO: 应适当修改该字符串,
// 例如修改为公司或组织名
SetRegistryKey(_T("应用程序向导生成的本地应用程序"));

Ctest6Dlg *dlg=new Ctest6Dlg;  /声明一个对话框对象
m_pMainWnd =dlg;            将对话框对象的地址赋给APP对象的m_pMainWnd,这一步推荐,主要是在主窗口对象在这个MFC项目中传递时必须用到
dlg->Create(IDD_TEST6_DIALOG,0);///创建对话框
dlg->ShowWindow(SW_SHOW);/显示对话框
dlg->UpdateWindow();///更新对话框,这一步可以不要,非常你的对话框的客户区有绘制的内容,建议使用
// 删除上面创建的 shell 管理器。
if (pShellManager != NULL)
{
delete pShellManager;
}

// 由于对话框已关闭,所以将返回 FALSE 以便退出应用程序,
//  而不是启动应用程序的消息泵。
return TRUE;///由于需要消息循环,所以返回TRUE
}

看到这里,也许大家会想起在"MFC---序幕"中看到的内容,没错,我说过,所有的windows窗口程序都是按照那个思路来的,有了那个思路,我们可以灵活的修改MFC程序,我们接着往下看,还有更多你在"MFC---序幕"中熟悉的代码。

由于非模式对话框的程序的销毁过程和默认对话框不一样,非模式对话框的销毁过程和普通窗口是一样,首先收到WM_CLOSE消息,在处理WM_CLOSE的时候,调用DestroyWindow函数,之后我们就会收到WM_DESTROY消息,在处理这个消息的时候,我们调用PostQuitMessage(0),这个函数导致消息循环收到WM_QUIT消息,退出消息循环,退出程序,这个过程在"MFC---序幕"中我已经说到,这里又啰嗦了一下,好,我们就按照这个思路进行,我们通过类向导,分别添加消息WM_CLOSE,WM_DESTROYWINDOW消息的响应函数,处理如下:

void Ctest6Dlg::OnClose()
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
this->DestroyWindow();
//CDialogEx::OnClose();///添加WM_CLOSE处理函数的时候,这一句是自动生成的,我们要注释起来,因为这一句的默认处理就是调用EndDialog,模式对话框的处理方式,这里我们是非模式对话框,所以要注释起来
}

void Ctest6Dlg::OnDestroy()
{
CDialogEx::OnDestroy();
PostQuitMessage(0);
// TODO: 在此处添加消息处理程序代码
}

在这里,在我们的'MFC--序幕"中的讲解,应该结束了,但是呢,在mfc中,窗口是由一个MFC类的对象来管理,到这里,我们只能说,我们把窗口销毁了,但是窗口对象还在,还记得我们前面用了new操作符,那么我们必须删除窗口对象,在网上,也有一些改MFC向导生成的对话框程序为非模式对话框,但是,方法不对,它们删除对话框对象指针时在WM_DESTROY中删除的,也就是在mfc消息映射函数On_Destory中调用delete this删除的,这样不对,因为窗口对象还有东西要清理,如果现在就把窗口对象处理掉,那么势必是不对的,造成一些资源的泄露。在msdn中有这样的说明,正确的处理是在窗口对象的虚函数PostNcDestroy中处理的,如下:

void Ctest6Dlg::PostNcDestroy()
{
// TODO: 在此添加专用代码和/或调用基类
delete this;
CDialogEx::PostNcDestroy();
}

到目前,我们就修改完成了,是不是感觉和“MFC--序幕”中说的一样呢。我们来运行一下:


我们看输出窗口:


没有内存泄露,正常退出。

/

这是一个简单MFC程序,对于我们理解MFC的结构是最好的,它基于对话框程序,是最简单的MFC程序,在此基础上,我们做了修改,生成一个非模式对话框程序,同时与"MFC--序幕"文章中的内容对比,更加清晰的理解了MFC程序的过程。最后,我要说的,就是MFC的是一个框架,一方面我们要依耐它,另一方面,我们也要利用它。这句话也许对于初学MFC的人,可能感受不是深,但是对于进行了一定程度MFC学习的人来说,应该有这个感受,尤其是学习了ActiveX, OLE等等,更加强烈。在后面的文章中,我还会提到MFC的一些灵活使用的方法,原理也都是来自“MFC--序幕”中的原理来的,所以这也是我为什么把它作为我MFC系列文章的开始的原因。文章相关信息--请参考msdn

///

本章文章代码:http://download.csdn.net/detail/xinzhiyounizhiyouni/6519027

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值