游戏开发论坛

 找回密码
 立即注册
搜索
查看: 1494|回复: 4

应用MFC开发高级应用程序1 wxh zt

[复制链接]

1367

主题

1993

帖子

2118

积分

金牌会员

Rank: 6Rank: 6

积分
2118
发表于 2004-11-5 20:09:00 | 显示全部楼层 |阅读模式
[摘要]:目前在Windows下开发应用程序的工具虽然很多,但是C/C++作为一种非常成
熟和高效的开发语言在大型复杂项目的开发中仍然得到了广泛应用。为了减轻程序
开发负担,提高开发效率,各种流行的C++都提供了类库,本文就是针对如何在
Visual C++环境中使用MFC类库来开发高级程序所需要解决的一些问题进行了的探
讨,重点讨论了利用MFC开发单文档多视应用程序和DDE应用程序的方法。


一、使用C/C++
随着Windows系列操作系统的日益普遍,传统的基于DOS编程逐渐转向Windows下编程
已经成为必然趋势。目前在Windows下开发应用程序的工具很多,典型的如Borland
C++、Visual C++、Visual Baisic以及Delphi等等。每种开发工具都各有其特点,
一般来讲用户可以根据自己的使用习惯和开发项目的性质来选择具体的开发语言。

Visual Basic是一个被软件界称之为划时代的革新产品,该软件改变了人们开发
Windows程序的方式,它采用交互式的可视化操作,使得人们开发Windows程序的每
一过程都有直观形象的反馈,从而加速整个开发进程。Visual Basic使得Windows程
序设计人员不再只依赖于复杂的SDK编程,使得开发Windows程序非常容易,可以
说,用户学习并使用VB来开发Windows应用的时间是最短的。Visual Basic版本几经
演变,目前已经发展到5.0。在4.0版本中,由于完全使用了面向对象的编程概念,
同时具有Windows 3.1和Windows 95下的版本,因而使得其开发复杂程序的功能逐渐
增强。VB5.0则抛弃了Windows 3.x的用户,只能在32位Windows中使用,据悉,该版
本吸收了Delphi的成功策略,引入了本地代码(Native Code)编译器,从而使得程序
执行速度大大加快,克服了以往版本由于执行文件采用P-Code代码而导致运行速度
慢的特点,根据微软的声明,该版本的采用本地代码编译后得到的应用程序在某些
情况下执行速度较以往提高了10~20倍,执行速度可以直逼与采用Visual C++编写的
应用,而应用开发速度则是VB的强项,因此Visual Basic 5.0非常具有竞争性。目
前Visual Basic非常广泛地用于开发各种Windows程序,如数据库前端应用程序和多
媒体应用等。但是,在作者看来,采用VB也有一定的缺点,原因有以下几点:
1. Visual Basic来源于Basic语言,虽然经过微软的不断增强,但是仍然缺乏非常
灵活的数据类型和编程策略,因而在开发一些项目必须的复杂数据结构遇到麻烦,
如链表、图和二叉树等等。由于在中大型项目开发后期,开发工作不再以界面为
主,而是在算法设计和底层软硬件工作,这就使VB开发项目的后期工作量大幅度增
加,为了达到项目要求,经常需要再转向C/C++开发一些专用的动态连接库来解决问
题。
2. Visual Basic运行速度慢,前文讲过,采用P-Code代码虽然执行文件很小,但是
在运行时需要解释执行,并且,它的运行必须有对应的VBRUN.DLL和所使用的VBX或
者OCX支持。对于浮点操作密集或者循环嵌套很多的应用来说,VB没有采取特别的优
化,因而执行速度远不如用C/C++和Fortran开发的应用速度快。VB 5.0虽然通过引
入本地代码编译器大大弥补了这个缺陷,但是由于其只能运行于32位Windows环境因
而在16位Windows上速度问题仍然得不到解决。虽然目前转向32位Windows的趋势非
常强劲,但是不容忽视由于硬件的限制或者使用习惯等诸多原因,还有许多用户仍
然在16位Windows上工作。在计算机十分普及的美国,96年使用16位Windows的用户
仍然超过了使用32位Windows的用户,任何进行系统软件设计的人员都应该照顾到这
些仍然使用16位Windows的用户。
3. VB不能灵活地使用系统资源。熟悉Windows编程的人都知道,如果要直接访问硬
件或者要编写对系统进行有效访问的应用程序,没有Windows API函数的帮助则非常
困难,但是令VB程序员失望的是,API函数是用C语言和汇编语言实现的,是为C编程
准备的,如果要在VB里面使用这些上千个API函数则比较麻烦,特别是,如果设计人
员不懂C语言则尤其困难。由于API函数的复杂性,而其本身不是为了方便VB编程而
提供的,因此在VB里面调用API函数需要一定的技巧,这些技巧足够用一本很厚的书
来表述。VB程序员可以从书店里找到好多本类似的书籍。可以说,任何一个VB程序
员发展到一定阶段都需要与众多的API函数打交道。另外,由于VB不支持端口操作,
因此,如果要编写类似数据采集等需要与硬件交互的程序则需要求救于C/C++语言。

4. Visual Basic项目分发和管理困难,其原因同上讲的,VB应用的运行不能脱离VB
的运行库和所使用的控件,因此,如果开发人员要将VB应用分发给用户那么一定要
带上VB的运行库和所使用的控件,并且要保证正确安装,这就导致即使一个非常简
单的应用也需要附带大量其它相关支撑库程序,对于VB 4.0及更高版本,由于大量
的使用了OLE控件(在VB中称为OCX),其安装更为复杂。
Delphi软件是国际宝兰公司(Borland)的得意之作,也是备受软件界推崇,与VB一
样,它完全是一个交互式的可视化开发平台,支持Client/Server应用程序的开发,
其最新版本2.0可以开发Windows 3.x、Windows 95和Windows NT的应用程
序。Delphi开发速度也非常快,与VB相比,由于具有本地代码编译器因此它产生的
可执行文件执行速度大大加快。Delphi软件是一个非常有竞争力的软件,采用的是
面向对象的Object pascal语言,支持硬件操作和API调用。但是由于采用的编程语
言为Pascal,这种语言并不非常流行,许多程序设计人员完全不熟悉这种语言,因
此极大地限制了该软件的使用,如果宝兰公司能够将Delphi软件提供的RAD开发机制
引入到其Borland C++中,则可能会形成一个非常成功的产品(目前该版本已经推
出,即C++ Builder,笔者注)。
VB和Delphi引入的可视化开发方法还有一个共同的缺点就是各个版本之间的兼容问
题。一般来讲,采用这些可视化开发工具开发的应用程序在移植到高版本时不会遇
到太大困难,但是一旦往相反方向移植则困难重重,有时甚至不可能。C/C++语言则
不具有这种局限性,各个版本之间的相互移植并不困难,高版本往低版本移植一般
只需重建工程文件即可大功告成。
综上所述,根据作者的观点,如果要开发一个大型复杂的应用程序首选的还是
C/C++,特别是在16位Windows下。虽然这会使前期工作增加,但是在项目的中后期
将逐渐会领略到其优越性和开发效率,其灵活高效的执行代码适合于对速度和应用
程序之间的协同性要求很高的场合。纯粹基于Windows SDK来开发Windows程序是一
项艰巨的工程,值得庆幸的是目前各种流行的C/C++开发工具都提供了类库开发框架
来简化整个开发过程而又不失其固有的灵活高效性,不同的开发语言所提供的类库
开发框架不同,如Borland C++提供的OWL(Object Windows Library)和 Visual C++
提供的MFC(Microsoft Fundmental Class),这两种类库都封装了大量的Windows
API和Windows的开发元素而使得开发任务简化,两种类库各有其优点,据作者掌握
的资料,采用MFC编写的应用程序执行代码更小,执行速度也更快,这大概是因为该
软件的开发者是开发Windows操作系统的Microsoft公司的缘故吧,现在MFC正逐渐成
为Windows下的类库开发标准,正被越来越多的其它C/C++编译工具所支持,如
Watcom C++。使用MC类库同时配合Visual C++提供的AppWizard、ClassWizard和
AppStudio可以大幅度提高开发效率。笔者在工作中积累了一些MFC的使用经验现在
提出来供大家参考,希望对广大同行有所帮助,尤其是那些仍然致力于16位Windows
编程的程序员。本文使用的Visual C++ 1.51编译器,但是其方法同样适用于其它
VC++版本,包括Visual C++ 4.x。
二、MFC编程综述
采用MFC开发Windows程序之所以能够大幅度提高开发速度和效率主要是因为MFC在类
层次封装了大量Windows SDK函数和典型Windows应用的缺省处理,这样,用户只需
要较少的编程就可以实现自己的开发任务。如果在MFC基础上再配合Visual C++提供
的AppWizard、ClassWizard和AppStudio工具那么更可以大幅度加快开发进程。MFC
提供大量的基类供程序员使用,常见的如CWinApp类、CFrameWnd类、CMDIFrameWnd
类、CMDIChildWnd类、CView类、CDC类和CDocument类等等。通过从这些基类中派生
出用户自己的类,然后重载特殊的几个函数就可以生成一个独立的应用程序。可以
说,采用MFC编写Windows应用程序是非常方便的,虽然其学习过程并不简单,但是
其提供的灵活高效性足以使任何Windows程序开发人员为之付出努力。如果用户不曾
使用过MFC,那么用户可以通过附录中所列的参考书去学习MFC的强大功能。
采用MFC应用框架产生的应用程序使用了标准化的结构,因而使得采用MFC编写的程
序的在不同平台上的移植变得非常容易,事实上,MFC的16位和32位版本之间差别很
小。MFC提供的标准化结构是经过众多专家分析调研后总结编写出来的,一般情况下
可以满足绝大多数用户的要求,但有时用户也可以通过重载一些函数来修改其缺省
的风格从而实现自己特有的风格,如自定义应用图表和灰色背景等。在MFC提供的文
档视结构中,文档、视和资源之间的联系是通过定义文档模板来实现的,如:
m_pSimuTemplate = new CMultiDocTemplate(
IDR_SIMUTYPE,
RUNTIME_CLASS(CSimuDoc),
RUNTIME_CLASS(CMyChild), // Derived MDI child frame
RUNTIME_CLASS(CSimuView));
上中第一项IDR_SIMUTYPE就包括了视口的菜单,加速键和图表等资源,如果用户使
用AppWizard来产生的应用基本框架,那么其也同时产生了缺省的图标,如果用户不
满意缺省图标(实际上用户很少满足于缺省图标),只需要将缺省图标删除,然后
编辑或者直接引入一个新的图标,在存储这一图标时只需要使用与被删除图标同样
的ID即可实现替代。
熟悉Windows程序开发的人都知道,在Windows上通过使用灰色背景可以增强应用程
序的视觉效果,曾有人戏称,灰色是图形界面永恒的颜色。使用MFC产生的应用程序
的背景缺省为白色,如果用户希望改变成灰色或者其它颜色,那就需要使用单独处
理,解决的办法很多,如在每次视口的OnPaint()事件中采用灰色刷子人为填充背
景,但是这不是最好的办法。笔者发现最好的办法就是采用AfxRegisterWndClass()
函数注册一个使用灰色背景刷的新的窗口类,这需要重载PreCreateWindow()函数来
实现这一点,如下程序代码片段所示:
BOOL CSimuView:reCreateWindow(CREATESTRUCT& cs)
{
HBRUSH hbkbrush=CreateSolidBrush(RGB(192,192,192));//创建灰色背景刷
LPCSTR lpMyOwnClass=AfxRegisterWndClass(CS_HREDRAW
|CS_VREDRAW|CS_OWNDC,0,hbkbrush);//注册新类
cs.lpszClass=lpMyOwnClass;//修改缺省的类风格
return TRUE;
}
采用这种方法速度最快,也最省力。同时,还可以在PreCreateWindow()函数定义所
希望的任何窗口风格,如窗口大小,光标式样等。
三、使用单文档-多视结构
如果用户使用过MFC进行编程,那么就会发现借助于AppWizard基于MFC无论编写SDI
(单文档界面)还是编写MDI(多文档界面)都是十分方便的。MDI应用程序目前使用越
来越普遍,人们熟悉的Microsoft公司的Office系列产品以及Visual系列产品都是典
型的多文档应用程序。这种多文档界面具有多窗口的特点,因而人们可以在一个程
序中使用多个子窗口来实现不同数据的浏览查看。如果用户要实现在MDI各个窗口之
间针对同一数据进行不同的可视化就是一件比较麻烦的事情。值得庆幸的是,MFC提
供的文档-视结构大大简化了这一工作。文档-视结构通过将数据从用户对数据的观
察中分离出来,从而方便实现多视,亦即多个视口针对同一数据,如果一个视口中
数据发生改变,那么其它相关视口中的内容也会随之发生改变以反映数据的变化。
SDI和MDI这两种Windows标准应用程序框架并不是总能满足用户的需要,就作者的工
作而言,就特别需要一种被称为单文档多视的应用程序,英文可以缩写为SDMV。通
过SDMV应用我们可以利用文档类来统一管理应用程序的所有数据,同时需要采用多
窗口以多种方式来可视化这些的数据,如棒图,趋势图和参数列表,从而方便用户
从不同角度来观察数据。MDI虽然具有多窗口的特点,但是其为多文档,即通常情况
下,一个视口对应一个文档,视口+文档便构成一个子窗口。在各个子窗口之间数据
相互独立,如果要保持数据同步更新就需要采用特殊的技术了,采用这种方式既费
时又费力。通过笔者的实践发现,利用MFC本身提供的多视概念通过适当改造MDI窗
口应用程序就可以实现上述SDMV结构。
所谓SDMV应用程序本质上仍然是一个MDI应用程序,只是在程序中我们人为控制使其
只能生成一个文档类,这个文档在第一个视口创建时创建,注意,这里并不需要限
制各个视口的创建先后顺序。此后与MDI窗口固有特性不同的是,所有新创建的子窗
口都不再创建独立文档,而是把该新视口直接连接到已有的文档对象上,这样就使
其成为单文档多视的结构,所有相关数据都存储在文档对象中,一旦文挡中数据发
生改变,通过UpdateAllViews()函数通知所有相关视口,各个视口就可以在
OnUpdate()中相应数据的变化。这种响应机制如下图所示:

图 1 文档-视结构数据更新机制
由于MDI本质上并不是为这种单文档多视机制服务的,因而在实际应用时需要解决一
些问题。
1、窗口标题问题
窗口标题本来不应该成为问题,缺省情况下MDI窗口通过在文档模板中提供的资源ID
所提供的对应字符串来确定窗口标题。但是对于SDMV应用,由于各个视口实质上是
对应于同一个文挡,因此每个视口都具有相同标题,只不过增加了一个数据用于指
示这是第几个视口。如果在各个视口中指明具体的窗口名字,那么由不同的视口启
动创建文档产生的窗口标题就不同,这个名字会影响到后继视口。为了作到不同类
型的视口如棒图视口和曲线视口具有不同的标题,这就需要一定的技术处理。根据
笔者的摸索发现可以采用如下步骤实现:
首先在从标准的MDI子窗口基类CMDIChildWnd派生一个自己的子窗口类,姑且命名为
CMyChild,然后在其成员变量中增加一个CString型变量用以存储当前窗口标题:
CString winTitle;
然后在不同的视口创建过程中通过获取父窗口指针按自己的意愿对上述变量进行赋
值,程序片段如下:
pChild=(CMyChild*)GetParent();
pChild->winTitle="棒图显示窗口";
最后在CMyChild派生类中重载CMDIChildWnd基类中的OnUpdateFrameTitle()函数来
强制实现窗口标题的个性化,这一函数在各种类库手册上和联机帮助中都没有,但
的确有这样一个具有保护属性的函数用来实现窗口标题的更新操作,这可以从MFC类
库的源代码中找到该函数的实现。重载后的源代码如下:
void CMyChild::OnUpdateFrameTitle(BOOL bAddToTitle)
{
// update our parent window first
GetMDIFrame()->OnUpdateFrameTitle(bAddToTitle);

if ((GetStyle() & FWS_ADDTOTITLE) == 0)
return; // leave child window alone!

CDocument* pDocument = GetActiveDocument();
if (bAddToTitle && pDocument != NULL)
{
char szOld[256];
GetWindowText(szOld, sizeof(szOld));
char szText[256];

lstrcpy(szText,winTitle); //Modified by author!
if (m_nWindow > 0)
wsprintf(szText + lstrlen(szText), ":%d", m_nWindow);

// set title if changed, but don't remove completely
if (lstrcmp(szText, szOld) != 0)
SetWindowText(szText);
}
}
2、如何创建SDMV应用
如何创建SDMV应用比较麻烦,下面通过举例来具体说明。该例子假设用户需要建棒
图类型和曲线形式的两种视口,假设用户已经利用CView基类派生并且实现了这两个
类,分别对应于CMyChart和CMyTraceView两个类。
1) 在应用类(从CWinApp派生出来的类)的头文件中加入下列变量和函数原型说
明:
CMultiDocTemplate* m_pMyTraceTemplate;
CMultiDocTemplate* m_pMyChartTemplate;
int ExitInstance();
2) 在应用类的InitInstance成员函数中删除对AddDocTemplate函数的调用和
OpenFileNew()语句,并且加入如下代码:
m_pMyTraceTemplate = new CMultiDocTemplate(
IDR_MYTRACEVIEW,
RUNTIME_CLASS(CSimuDoc),
RUNTIME_CLASS(CMyChild), // Derived MDI child frame
RUNTIME_CLASS(CMyTraceView));

m_pMyChartTemplate = new CMultiDocTemplate(
IDR_MYCHART,
RUNTIME_CLASS(CSimuDoc),
RUNTIME_CLASS(CMyChild), // Derived MDI child frame
RUNTIME_CLASS(CMyChart));
3) 实现ExitInstance()函数,在其中删除所用的两个辅助模板:
int CTestApp::ExitInstance()
{
if(m_pMyChartTemplate) delete m_pMyChartTemplate;
if(m_pMyTraceTemplate) delete m_pMyTraceTemplate;
return TRUE;
}
4) 在菜单资源中去掉File菜单中的New和Open项,加入New Chart View和New
Trace View两项,在对应的菜单命令中实现如下:
void CMainFrame::OnNewMychart()
{
// TODO: Add your command handler code here
OnNewView(((CSimuApp*)AfxGetApp())->m_pMyChartTemplate);
}
void CMainFrame::OnNewMyTrace()
{
// TODO: Add your command handler code here
OnNewView(((CSimuApp*)AfxGetApp())->m_pMyTraceTemplate);
}
上中OnNewView的实现如下:
BOOL CMainFrame::OnNewView(CMultiDocTemplate* pDocTemplate)
{
CMDIChildWnd* pActiveChild = MDIGetActive();
CDocument* pDocument;
if (pActiveChild == NULL ||
(pDocument = pActiveChild->GetActiveDocument()) == NULL)
{
TRACE0("Now New the specify view\n");
ASSERT(pDocTemplate != NULL);
ASSERT(pDocTemplate->IsKindOf(RUNTIME_CLASS(CDocTemplate)));
pDocTemplate->OpenDocumentFile(NULL);
return TRUE;
}

// otherwise we have a new frame to the same document!
CMultiDocTemplate* pTemplate = pDocTemplate;
ASSERT_VALID(pTemplate);
CFrameWnd* pFrame = pTemplate->CreateNewFrame(pDocument, pActiveChild);
if (pFrame == NULL)
{
TRACE0("Warning: failed to create new frame\n");
return FALSE; // command failed
}
pTemplate->InitialUpdateFrame(pFrame, pDocument);
return TRUE;
}
OnNewView是整个SDMV应用的核心组成,它的任务是创建一个新的指定类型的视口,
它首先判断是否有活动视口存在,文档是否已经创建,正常情况下活动视口存在则
表明文档存在,如果不存在则利用所指定的文档模板创建一个新的活动视口,否则
则只创建视口,同时将其连接到已存在的文档对象上。
通过以上步骤就可以实现SDMV应用,在其后的具体应用中利用文档对象的
UpdateAllViews()函数和视口的OnUpdate()函数就可以很好的工作了。
四、使用DDE服务
Windows 3.x是一个分时多任务操作环境,在此环境下,多个应用程序可以并发地执
行。为了在并发执行的多个任务之间共享数据和资源,Windows 提供了几种机制,
主要是通过剪贴板(Clipboard)和动态数据交换(Dynamic Data Exchange)。前者对
于用户需要直接参与的数据交换来说,是一个非常方便的工具,但是如果希望数据
交换自动进行时就必须依靠DDE技术了。编写DDE应用的技术也发展了好几代,从最
初的基于消息的DDE到基于DDEML(动态数据交换管理库),再到现在流行的OLE技
术。DDE技术的发展使得程序开发人员编写DDE应用更为简洁。从发展趋势来看,基
于OLE的数据交换是最好的,它特别符合当今软件领域的客户-服务器机制
(Client-Server)。为适应多平台和Internet的需要,在OLE基础上微软又开发了
ActiveX技术。但是不容忽视的是,基于传统的DDE数据交换也自有它的应用空间,
使用仍然广泛。目前在Windows 3.x下,基于OLE的远程数据交换还很不成熟,但是
在WFW(Windows for Workgroup)下基于网络动态数据交换的技术却很成熟,目前也
应用非常普遍。关于DDE应用的开发和NetDDE的应用可以参看附录7。

139

主题

2005

帖子

2057

积分

金牌会员

Rank: 6Rank: 6

积分
2057
QQ
发表于 2004-11-5 21:25:00 | 显示全部楼层

Re:应用MFC开发高级应用程序1 wxh zt

MFC不适合做游戏开发……个人意见。
做编辑器还行

26

主题

324

帖子

325

积分

中级会员

Rank: 3Rank: 3

积分
325
QQ
发表于 2004-11-6 20:37:00 | 显示全部楼层

Re:应用MFC开发高级应用程序1 wxh zt

已彻底抛弃MFC了,打算编辑器也dx来做……

26

主题

324

帖子

325

积分

中级会员

Rank: 3Rank: 3

积分
325
QQ
发表于 2004-11-6 20:52:00 | 显示全部楼层

Re:应用MFC开发高级应用程序1 wxh zt

或许是MFC抛弃我,谁知道呢……

13

主题

978

帖子

978

积分

高级会员

Rank: 4

积分
978
发表于 2004-11-6 21:30:00 | 显示全部楼层

Re:应用MFC开发高级应用程序1 wxh zt

倒!开发游戏怎能用mfc呢?文章不错,可惜太老了……还vb5.0呢……
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2025-12-23 04:01

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表