What's New for MFC Developers?
By Anson Tsao and Walter Sullivan
翻译:依栏望海(kevin_kbh@21cn.com)
【译者:因为时间关系,俺在一些省略了一些细节的翻译,不过俺还是都点到了。有兴趣的朋友
可以继续做。大家可以随意转载,但请不要修改本文内任何文字。如果可以的话,转载时请用
mail通知俺。谢谢!有任何想和俺交流的更是欢迎之至!15:29 2002-1-8】
MFC和ATL已经有三年没有大的更新了。在Microsoft.NET的强大宣传下,MFC和C++开发者感到被抛弃了。
不过,不用担心——在即将到来的Visual Studio.NET中,Visual C++的开发者不仅得到一个和服务器
端开发全面而紧密的崭新的IDE,大大改良的C++编译器,MFC和ATL也得到了新的重要特性。有一个清晰
消息就是MFC依然会是在所有windows平台上,拥有众多客户程序,最好最悠久的framework。在这篇文章里,
我们将带你纵览这些可以用在您的MFC程序上的新的特性。
·使用新的MFC DLL (MFC70.DLL),不再兼容MFC42.DLL,但你的源程序依然保持兼容性(消息映射更加类
型安全,所以可能导致某些代码违例)。
·MFC和ATL更加结合紧密了,一些通用的类如CString可以同时被两个库使用。
·头文件与新的Platform SDK同步,支持2k和XP中的新UI特性,如主题(themes),manifest resources,
Active Accessibility和新的通用对话框。
·增加了不少UI的类,包括支持DHTML的对话框和支持扩展BITMAP的CImage。
·新的工具类可以在MFC和ATL中使用,比如正则表达式,计数器和安全。
·MFC应用程序有力地支持web Services。ATL服务器类编写Web Services和应用程序。
·使用新的OLE DB特性使得高效率地访问数据库从来就没有像现在这样容易。
·STL被更新
一体化MFC和ATL
能否在ATL中使用CString,或者在大型的MFC中使用ATL实现COM对象?哈,现在可以了。在MFC 7。0中
有不少的工具类可以在ATL和MFC之间共享:CString,CPoint,CRect,CSize和CImage。而且,ATL可以在
无需MFC支持的情况下使用。
CString被完全改写了,现在基于模板类CStringT<>。它支持各种字符类型(char, wchar_t, TCHAR)和
建立在CRT的基础上。你还可以在CString中自行管理内存的分配。
【译者:比如
typedef struct my_chr {
unsigned int a;
unsigned int b;
unsigned int c;
} MY_CHR;
CStringT<MY_CHR> My_Str;
...
】
MFC7.0增加了一个新的字符串类叫CFixedStringT<>。这种定长字符串提供一种用户定义长度的方法,在耗尽
内在的空间前无需额外的内存。CFixedStringT被用于基于栈的变量或联合容器中的元素时,效率非常高。
它显著地减少了基于堆的分配。
【译者:这里俺也糊涂了,呵呵,请各位指正。原文如下:
CFixedStringT's are very efficient when used as stack-based variables or as keys in associative
containers, dramatically reducing the number of heap-based allocations.
】
下面的例子创建一个有1024个字符的实例:
// allocating 1024 character internal buffer
CFixedStringT< CString, 1024 > string;
新的UI特性及升级
MFC7.0提供DHTML对话框支持,有一套DHTML编辑器类,新的图形API(GDI+),更新了对BITMAP支持(CImage),
添加一个通用对话框包装类(CLinkCtrl),更新控件和对话框支持2k和XP,增强了ActiveX control容器和提高了
消息映射的类型安全。让我们逐一看看吧。
DHTML对话框及DHTML编辑器类
DHTML对话框把丰富多彩的HTML用户界面带到桌面应用程序上来。现在你的桌面App拥有时髦的图形界面和一般
web应用程序没有的爽快的交互性。MFC7.0提供了CDHtmlDialog和CMultiPageDHtmlDialog来支持DHTML。DHTML
对话框可以从资源文件或任何有效的URL中显示HTML页面。如果你曾用过CHtmlView,就知道获取和设置HTML元素
需要通过IHTMLDocument2对象,而且处理HTML的事件是非常痛苦的事。CDHtmlDialog极大地简化了这种交互,使
用一套类似DDX的扩展宏和DHTML事件映射。DDX_DHtml_宏和传统的DDX_宏同样在DoDataExchange函数中使用。它
允许获取和设置各种HTML元素的属性。例如,DDX_DHtml_CheckBox绑定复选框的值,DDX_DHtml_ElementInnerHtml
设置和获取某个元素的HTML。绑定一个HTML元素到DDX需要这个元素有的一个'id='的属性,就像下面的代码所示:
<HTML><BODY>
Filename: <SPAN ID="filename"></SPAN><BR>
<INPUT ID="readonly" TYPE="checkbox" ACCESSKEY="r">
<U>R</U>ead-only
</BODY></HTML>
void CPropertiesDlg::DoDataExchange(CDataExchange* pDX) {
CDHtmlDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CPropertiesDlg)
//}}AFX_DATA_MAP
DDX_DHtml_ElementInnerText(pDX, _T("filename"), m_strFileName);
DDX_DHtml_CheckBox(pDX, _T("readonly"), m_nReadOnly); }
To respond to HTML control events, you have to define a DHTML event map:
BEGIN_DHTML_EVENT_MAP(CDHtmlExploreDlg)
DHTML_EVENT_ONCLICK(_T("browse"), OnBrowse)
DHTML_EVENT_CLASS(DISPID_HTMLELEMENTEVENTS_ONMOUSEOVER,
T("hotElement"), OnMouseOverElement)
END_DHTML_EVENT_MAP()
这些事件映射类似MFC的样式,包括DECLARE_DHTML_EVENT_MAP的定义和在BEGIN_DHTML_EVENT_MAP与END_DHTML_EVENT_MAP
之间的定义。
CMultiPageDHtmlDialog适合导向和属性页有关的对话框。它可以在一个对话框中装载多个页面并且使用内在事件映射
响应事件。
这个新版本还提供了所见即所得(WYSIWYG)的DHTML编辑器。这意味着在你的程序中调整一个HTML元素并不比调整
一个文本控件困难。类似MFC6.0对于rich edit控件的支持,MFC7.0提供了CHtmlEditCtrl,还有类似的文档视图
类:CHtmlEditView/CHtmlEditDoc。但你仍然需要自行调整UI元素,如字体、风格、颜色、等等。
GDI+
GDI+ 是一个新的图形子系统,提供一整套图形API渲染2D图象,图片和文字。它是.NET中唯一的API,但在C++中,
它补充了GDI的渲染能力。
通过GDI+可以获得:更强的2D制图,alpha通道支持,通用坐标转换和浮点支持,还有渐变笔刷,cardinal splines,
scalable regions和新的编程模式。
GDI+以C风格的API形式发布,虽然一套C++类将其包装,但不被MFC支持而是在.NET中使用。使用GDI+,使用gdiplus.h
并且链接gdiplus.lib。还必须在使用前初始化这个库。GDI+是XP的一部分,如要在98、me、nt4.0和2k上使用就要拷贝
GdiPlus.DLL到相应的目录。
GDI+使用了与GDI不同的编程模型。在GDI+中必须在每次画图命令中传入笔或刷子。图形元素不在和笔与栓子一起工作;
勾勒边框和填充被分开成不同的函数来实现(如DrawRectangle和FillRectangle)。下面的例子演示画一个矩形
(3.5 英寸 × 4.4 英寸)。
using namespace Gdiplus;
void CMyView::OnDraw( CDC* theDC )
{
Graphics graphics( *theDC );
Pen pen( Color( 128, 255, 0, 0 ), 2.0 ); // Alpha value set
// Realworld units
graphics.SetPageUnit( UnitInch );
// Floating point coordinates
graphics.DrawRectangle( &pen, RectF( 0.0F, 0.0F, 3.5F, 4.4F ));
}
用CImage管理Bitmap
期待已久的Bitmap支持终于在这个版本实现了。CImage可以读取和存储JPEG、GIF、BMP和PNG格式的图形文档。
但是一些函数不被旧系统支持(如95),还有一些函数在不同的os上的具体实现不一样。CImage可以在MFC和
ATL中使用。它也包装了DIB的功能,可以直接操作象素点。让我们来看看CImage提供了什么吧:
·Alpha象素点混和透明和半透明效果
·PlgBlt可以随意的映射一个矩形到一个并行四边形
·TransparentBlt可以根据一种指定的颜色进行透明操作
·MaskBlt可以混和两张图片到一个输出设备上
·CImage::GetDC可以直接向一幅bitmap画图。
下面给出一个例子:
CImage image;
Image.Load( "test.gif" );
CBitmap* pBitmap = CBitmap::FromHandle( image.m_hBitmap );
CLinkCtrl通用对话框类
CLinkCtrl包装了SYSLINK控件,可以轻松地在windows上放置超链接,但只在XP上使用。它支持一个或多个
超链接,你可以在OnNotify中处理NM_CLICK和NM_RETURN消息响应点击事件。
对2k和XP的支持
所有的通用控件,CCheckListbox,文件处理和打印都被更新以支持2k和XP。CWnd的方法也被更新以支持
新的特性如分层窗口(layered windows)和活动窗口(animated windows)。CListView在XP中支持平铺风格。
MFC7.0支持XP的主题(themes)。
CWnd现在支持Active Accessibility。
新的CPrintDialogEx更新了打印对话框。
增强的ActiveX容器
MFC7.0和ATL支持无窗口的ActiveX,并且容器可以被改写。现在MFC也享受和Visual Basic一样的容器优化。
现在你可以改写COleControlSite以增加属于自己的容器行为。
类型安全的消息映射
MFC7.0加强了对消息处理函数返回值的校验。比如在OK中返回void而不是LRESULT的话,编译器会认为是一个
错误。
应有尽有的工具类
新的工具类有:正则表达式类(regular expression class)、64位日期时间函数、集合模板类、安全类、
包装CryptoAPI的函数和类、支持计数的宏和线程池支持。我们会在后面一一简要介绍。
正则表达式类(regular expression class)
这个模板类用于字符串的查询和比较,同时支持MBCS和Unicode。一共有两个模板类:CAtlRegExp和
CAtlReMatchContext。
下面给出一个例子:
CAtlRegExp<> re; // using the default character trait
re.Parse( "ab.d" );
CAtlReMatchContext mc;
re.Match( "abcd", &mc ); // returns TRUE, successful match
re.Match( "bbcd", &mc ); // returns FALSE, no match
64位日期时间函数
你的代码也许不会有千年问题,但标准的C运行库会在2038年1月18号19:14:07后出错。而新的64位日期时间函数
可以坚持到3000年12月31号。
CTime、CTimeSpan和C运行库都被更新支持__int64数据类型,包括CTimeSpan::GetDays64,
CTimeSpan::GetTotalHours64, CTimeSpan::GetHours64, CTimeSpan::Serialize64,
CTime::GetTime64,和CTime::Serialize64;还有_ctime64, _wctime64, _ftime64, _futime64, _gmtime64,
_localtime64, _time64, _utime64, _wutime64; _findfirst64, _wfindfirst64, _findnext64, _wfindnext64;
and _stat64,和_wstat64。
ATL的集合类和矩阵类
ATL7.0提供五个新的集合类:AtlArray,CAtlList,CAtlMap,CRBMap,和CRBMultiMap。它们可以更好的支持
非标准的数据类型。使用一些可以描述元素品质的类,这些类描述元素如何被拷贝,移动,比较清除。
有CAutoPtrElementTraits, CAutoVectorPtrElementTraits, CComQIPtrElementTraits, CStringElementTraits,
等等。这些ATL集合类不支持序列化。使用它们要同时指定元素类型和描述元素的品质:
CRBMap< CString, CStringElementTraits<CString>,
CAutoPtr<CMyObject>, CAutoPtrElementTraits<CMyObject> >;
这里定义了一个字典,映射CString给CMyObject的自动指针。CMyObject的元素会在集合类销毁时自动销毁。
安全类
安全类包装了NT所有的安全机制。ATL7.0包装了:
·访问控制列表(CAcl)
·任意访问控制列表(CDacl)
·系统访问控制列表()
·SID安全标识(CSid)
·访问节点(CAccessToken)
·访问结点组(CTokenGroups)
·访问结点许可(CTokenPrivileges)
·安全描述(CSecurityDesc)
·安全属性(CSecurityAttributes)
·一些全局函数
不幸的是ATL 7.0不提供高级的安全模型,你仍然需要深入了解win32的安全机制。
Cryptographic类
Cryptographic类包装了win32的CryptoAPI,提供加密、解码、散列、数字签名和密匙管理。
That's it! If you don't want to use C++ attributes, there are corresponding classes and macros
that you can use instead. You should also be aware that setting counters to specific values is
generally safe without having to worry about thread synchronization, but performing calculations
on the counters, such as incrementing or decrementing, requires the use of atomic operations
such as InterlockedIncrement or synchronization using critical sections.
性能测试(Performance Counters)
在你的程序中使用性能测试,需要实现三种类对象:性能监测管理对象,性能对象和性能测试器。
下面演示怎样定义性能监测管理对象:
[ perfmon( name="MyApplication", register=true )]
class CMyApplicationPerfMon
{
};
然后可以使用性能测试器:
void SetCounter( ULONG value )
{
if ( g_pStatsObject )
g_pStatsObject->SampleCounter = value;
}
使用它们不必担心线程同步的问题,但是测试器需要原子操作,如InterlockedIncrement或synchronization。
线程池
MFC 7.0线程池是基于NT I/O Completion Port。CThreadPool是一个支持工作线程的模板类。
当一个线程可以工作时,你的工作线程的执行函数会被调用。工作线程的查询和执行遵循FIFO(先入先
出)原则。使用线程池必须:
·一次一个线程地初始化
·逐一执行在请求队列中的线程
·一次一个线程地销毁
·在工作线程类中声明一个RequestType typedef,它会在执行时被处理
RequestType是一个不透明的类型,不能大于sizeof(ULONG_PTR)(即,x86 PC上的32位),所以你不可以
使用类的实例或大的原始类型(如浮点)。
下面是一个示例:
class CMyWorker
{
public:
typedef CMyData* RequestType;
BOOL Initialize( void* initParam );
void Execute( CMyData* request, void* initParam, OVERLAPPED* pOverlapped );
void Terminate( void* initParam );
};
下面是怎样在线程池初始化和查询一个任务:
CThreadPool< CMyWorker > threadPool;
threadPool.Initialize();
threadPool.QueueRequest( new CMyData());
threadPool.Shutdown( 1000L );
Web服务,Web应用程序和Network类
MFC7.0支持Web服务程序,编写Web服务和使用ATL服务器类编写Web应用程序。在下面的段落里,我们将
演示访问Web服务和调用服务的方法。还将解释如何生成HTML和把它写入IStream,怎样使用HTTP客户端和
怎样用MFC发mail。
To call the methods exposed by the Web Service, simply instantiate an instance of the proxy
class and call its methods, just like a native C++ class!
#include "myservice.h"
???
MyService::CMyService service;
HRESULT hr = Service.MyMethod( CComBSTR( L"A Parameter" ));
在MFC中支持Web服务
和DCOM不一样,Web服务容易透过防火墙访问,提供了一种真正的宽松的连接。使用Web服务不会比用#import
来的困难。使用命令行程序SProxy.exe就可以生成访问Web服务的C++客户端代码。生成的proxy类包含所有
你需要的一切。下面是个例子:
sproxy /out:myservice.h
http://myserver/myservice.dll?Handler=GenMyServiceWSDL
调用Web服务的方法,你只要初始化这个proxy类并直接调用其方法即可,就像普通的C++类:
#include "myservice.h"
...
MyService::CMyService service;
HRESULT hr = Service.MyMethod( CComBSTR( L"A Parameter" ));
HTML生成器
CHtmlGen类和模板CHtmlGenBase<>支持该功能。CHtmlGen把一个完全的HTML写入IStream。使用它
只要用一个IStream初始化之并调用相应的方法即可。
CHtmlGen out;
out.initialize( aStream );
out.html();
out.head();
out.title( "Test HTML" );
out.headEnd();
out.body();
out.a( "http://www.microsoft.com", "Microsoft" );
out.bodyEnd();
out.htmlEnd();
HTTP客户
CAtlHttpClient是一个轻量级的HTTP客户类,可以发出请求并获得回应。它支持代理和各种验证
(当前,支持Basic和NTLM)。CAtlHttpClient会同步发出请求,所以最好放置在多线程中。
CAtlHttpClient client;
client.Navigate( _T(http://www.microsoft.com ));
CString contentType;
client.GetHeaderValue(_T("Content-Type"), contentType );
BYTE* pBody = client.GetBody();
CBasicAuthObject和CNTLMAuthObject支持验证。下面是一个范例:
CNTLMAuthObject ntlmAuth;
client.AddAuthObject( _T("NTLM"), &ntlmAuth );
SMTP支持MIME编码消息支持
SMTP支持使你无需其他的mail客户端支持。CSMTPConnection直接连接一个SMTP服务器并
发送MIME编码的mail。CMimeMessage更可以在mail上加附件、原始数据和其他MIME编码
的消息。这个示例演示如何构造MIME消息:
CMimeMessage message;
message.SetSender( _T("someone@microsoft.com" ));
message.AddReceipient( receipient );
message.SetSubject( _T("A test message" ));
message.AddText( _T("This is a test message"));
CSMTPConnection connection;
connection.Connect( someSMTPServer );
connection.SendMessage( message );
高效地OLE DB数据库访问
【译者:这段废话真多,总之就是比以前编程更容易了。不过,DB总是比较复杂的,短短
的一段也说明不了什么问题。偷懒不翻译了 :)】
更新的STL
添加的hash_map,hash_multimap和hash_set类基于新的hash-table-based扩展类。
它们类似于std::map,std::multimap和std::set,但有着非常不一样的性能特性。
Hash-table-based比binary tree-based在查找方面快得多。和map与set不同,hash
类不是有序的。
新版的STL也消除了basic_string著名的多线程问题。不再使用计数器,而已严格地
用拷贝来实现。
最后,STL有了更好的DLL支持。再也不是本地静态数据成员了。
总结
MFC得到了更好地接近了C++标准,完全地和ATL结合,在UI和工具方面大大增强。
最好的还是这些新特性可以加入到已存在的代码中。
关于作者:
Anson Tsao有12年的行业经验,从事大型的Windows和C++的开发,最近加入
Microsoft Visual C++ Libraries小组。
Walter Sullivan已经在Microsoft工作了11年,他的所有时间几乎都花在
Visual C++ product小组上面。他现在领导Microsoft C++ Class Libraries
程序管理的工作。
About Translator
Just a C++ fans. :) Call me Kamp. I care code performance before, and now care
the performance of a certain project as a whole.