img optimizer

Visual C++ .NET: What's New for MFC Developers?

发表于2002/1/1 14:27:00  904人阅读

Visual C++ .NET
What's New for MFC Developers?

By Anson Tsao and Walter Sullivan


It has been three years since the last major update to MFC and ATL. With all the press on Microsoft® .NET, MFC and C++ developers may be feeling left out. But don't worry—with the upcoming release of Visual Studio .NET, not only do developers using Visual C++® get a brand new IDE with tight integration for server-side development and a much improved C++ compiler, MFC and ATL have also received significant new features. The clear message is that MFC continues to be a great framework for developing sophisticated, rich client applications for all Windows® platforms. In this article, we'll provide you with a survey of the new features that you can use in your MFC applications.

·                     There's a new MFC DLL (MFC70.DLL) that is no longer backward binary-compatible with MFC42.DLL, but your source is still compatible (although message maps have been made more type-safe, so that may break some code).

·                     MFC and ATL are much better integrated, and common classes such as CString are now shared between the two libraries.

·                     Header files are synchronized with the latest Platform SDK, supporting UI features in Windows 2000 and Windows XP such as themes and manifest resources, Active Accessibility®, and new common dialog boxes.

·                     Many new UI classes have been added, including support for using DHTML in dialog boxes and enhanced bitmap support with CImage.

·                     New utility classes can be used in both MFC and ATL applications, such as regular expressions, performance counters support, and security.

·                     Now there's support for consuming Web Services in MFC applications and writing Web Services and applications with ATL Server classes.

·                     High-performance access to databases has never been easier using the new OLE DB attributes and classes.

·                     STL has been updated.

Integrating MFC and ATL
Wouldn't it be nice to use CString in thin ATL ActiveX® controls, or use ATL to implement COM objects in large MFC applications? Well, now you can. In MFC 7.0 a number of utility classes have been made shared classes: CString, CPoint, CRect, CSize, and CImage. These classes can be used in ATL with no MFC dependencies.
CString has been totally rewritten based on the template class CStringT<>. Different specializations of CStringT<> allow for different character types (char, wchar_t, TCHAR) and dependency on the C runtime (CRT). You can also provide custom memory management for CString classes.
MFC 7.0 adds a new string type called CFixedStringT<>. Fixed strings provide a user-defined amount of storage within the string itself so there's no need for additional memory allocation until the internal storage is exhausted. 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.
The following example shows how to create a fixed string with a 1024-character internal buffer.

// allocating 1024 character internal buffer
CFixedStringT< CString, 1024 >    string;

New UI Features and Updates
MFC 7.0 has new DHTML dialog support, a new set of DHTML editor classes, new graphics API with GDI+, updated bitmap support with CImage, a new common control wrapper class (CLinkCtrl), updated controls and dialogs for Windows 2000 and Windows XP, enhanced ActiveX control containment, and improved type safety in message maps. Let's take a look at each of these new and updated features.

DHTML Dialog Support and Editor Classes
The new DHTML dialog support brings graphics-rich HTML user interfaces to desktop applications. Now your desktop apps can have both the snazzy graphics that users have come to expect and the snappy interaction that's not possible with generic Web applications. MFC 7.0 has two classes to support using DHTML as dialog boxes: CDHtmlDialog and its specialization, CMultiPageDHtmlDialog. DHTML dialog boxes can load the HTML pages either from an application's resources or any valid URL.
If you have used CHtmlView before, you know that setting and getting data from HTML elements requires you to traverse the IHTMLDocument2 COM objects, and responding to events from HTML controls is even more painful. CDHtmlDialog greatly simplifies the interaction with the HTML elements with a set of extensions to the familiar DDX_ dialog data exchange macros and a DHTML event map.
The DDX_DHtml_ macros are used in DoDataExchange in much the same way as DDX_ macros are used in traditional dialog boxes. They allow you to set and get various properties of named HTML elements. For example, DDX_DHtml_CheckBox is bound to the value of a checkbox control, and DDX_DHtml_ElementInnerHtml sets and retrieves the HTML between the start and end tags of an element. To bind HTML elements to DDX, the elements must have the 'id=' attribute, as you can see in the following code.

    Filename: <SPAN ID="filename"></SPAN><BR>
    <INPUT ID="readonly" TYPE="checkbox" ACCESSKEY="r">
void CPropertiesDlg::DoDataExchange(CDataExchange* pDX) { 
    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:

DHTML_EVENT_ONCLICK(_T("browse"), OnBrowse)
                  T("hotElement"), OnMouseOverElement) 

The event map is set up in the familiar MFC fashion, including DECLARE_DHTML_EVENT_MAP in the class definition and defining the map entries between BEGIN_DHTML_EVENT_MAP and END_DHTML_EVENT_MAP in the implementation file.
CMultiPageDHtmlDialog is useful for wizard and property page style dialog boxes. It allows you to load multiple pages into the same dialog boxes and respond to the events from each page using embedded event maps.
This release also gives you a WYSIWYG DHTML editor to generate or modify HTML documents. This means that embedding HTML editing capability in your application is not much more difficult than adding a text control. Similar to the MFC 6.0 support for the rich edit control, MFC 7.0 exposes HTML editing through a control class, CHtmlEditCtrl, and the view/document combo of CHtmlEditView/CHtmlEditDoc. You will, however, still need to provide the UI elements to change fonts, styles, color, and so on.
GDI+ is a new graphics subsystem for Windows and provides a new set of graphics APIs for rendering 2D graphics, images, and text. GDI+ is the only API for drawing in the .NET Framework, but in native C++ it is a complement to the current GDI to provide more advanced rendering capabilities.
Here's what you get with GDI+: antialiased 2D drawing, alpha channel support, universal transformations and floating point coordinates, gradient brushes, cardinal splines, scalable regions, and a new programming model.
GDI+ is exposed through conventional C APIs, but there's also a set of C++ classes that encapsulates its functionality. Unfortunately, the GDI+ classes are not written in the style of MFC, instead they are named with the .NET Framework conventions. Nevertheless, they can be easily used in MFC apps. To use GDI+, you have to include the gdiplus.h header file and link with gdiplus.lib. You also have to initialize the library before calling any of its functions. The GDI+ feature can be easily redistributed. GDI+ is standard on Windows XP. For Windows 98, Windows Me, Windows NT® 4.0, and Windows 2000, you can simply copy GdiPlus.DLL into your app's directory.
GDI+ uses a different programming model from GDI. Instead of selecting brushes and pens into a device context, in GDI+ you pass the pen or brush into every drawing command. Graphics elements are no longer drawn with both the pen and brush together; they are outlined and filled using separate functions (such as DrawRectangle and FillRectangle).
The following code shows how you would draw an outline of a rectangle (3.5 inches × 4.4 inches) in response to OnDraw.

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 for Managing Bitmaps
The long-awaited update to bitmap support is also in this release of MFC. CImage can load and save images in JPEG, GIF, BMP, and PNG file formats. However, a number of the functions are not supported in older versions of Windows (like Windows 95), and a few functions behave differently in different OSs, so make sure you check for version compatibility. CImage is a shared class, usable in both MFC and ATL. CImage also encapsulates the device-independent bitmap (DIB) section functionality, allowing you to directly manipulate bitmap bits.
What are the cool things you can do with CImage?

·                     AlphaBlend supports pixel-level color blending for transparent and semi-transparent effects.

·                     PlgBlt maps a rectangle in the bitmap to a parallelogram in the destination, optionally with a bit mask.

·                     TransparentBlt specifies a color as the transparent color.

·                     MaskBlt combines the bitmap with a mask bitmap when drawing to the destination.

·                     CImage::GetDC allows you to draw directly onto the CImage bitmap.

You can also use CImage where MFC expects CBitmap by doing the following:

CImage    image;
Image.Load( "test.gif" );
CBitmap*    pBitmap = CBitmap::FromHandle( image.m_hBitmap );

CLinkCtrl Common Control Wrapper Class
The only new common control wrapper class added to MFC 7.0 is CLinkCtrl. A convenient way of placing hyperlinks in windows, it is only available for Windows XP, wrapping the Win32® SYSLINK control. CLinkCtrl supports one or more <A> hyperlink anchors and can link to any valid URL. You can respond to the hyperlink clicks by responding to OnNotify (NM_CLICK and NM_RETURN) messages.
Windows 2000 and Windows XP Support
All Win32 and common controls, CCheckListbox, file, and print dialog boxes have been updated to include Windows 2000 and Windows XP support. CWnd methods have also been updated to reflect new UI features such as layered windows and animated windows. CListView now supports tiled views on Windows XP.
MFC 7.0 apps are now created with a default manifest resource, allowing them to respond to Windows XP themes right out of the box. You can further override individual themes for controls by calling SetWindowTheme. As you can see in Figure 1, Windows XP themes change the visual styles of the caption bar, scroll bars, and other aspects of a window.

Figure 1 Windows XP Themes

CWnd has been updated to add Active Accessibility support. Calling EnableActiveAccessibility allows you to override the default support to add your own non-windowed elements. MFC handles normal windows as well as windowless ActiveX controls. Windowless controls are lightweight ActiveX controls that are optimized for faster rendering.
Windows 2000 and Windows XP have new File Open and Save dialog boxes (see Figure 2). MFC 7.0 applications using the CFileDialog class automatically take advantage of the new user interface.

Figure 2 New File Open Dialog Box

For printing support, Windows 2000 has more extensible print property sheets (see Figure 3), replacing the older print dialog box. The new CPrintDialogEx encapsulates this functionality. You can customize the print property sheet by either extending the lower portion of the General page or adding your own property sheets.

Figure 3 Print Property Sheets

Enhanced ActiveX Control Containment

MFC 7.0 added support for containing windowless ActiveX controls and the control container is now overridable. Many new control containers such as Visual Basic® 6.0 can take advantage of this performance optimization, and now MFC does, too. ATL has support for building windowless ActiveX controls.
Now that the container hosts ActiveX controls, the control container is accessible to your application. You can override pieces of the COleControlSite to add custom containment behavior. This greatly increases the flexibility you now have with MFC.
Type-safe Message Maps
And finally, MFC 7.0 message maps have been made more type-safe by providing stricter type checking of return values and parameters of the message handler functions. This will alert the developer to unsafe handlers that previous versions of MFC let slip. For example, it used to be OK to return void (instead of LRESULT) on an ON_MESSAGE or ON_REGISTERED_MESSAGE handler, and the compiler would not generate any warning. With MFC 7.0, the compiler will generate an error message.
Smorgasbord of Utilities
New utilities in MFC include a regular expression class, 64-bit date and time functions, template collection classes, security classes, wrappers for the Win32 CryptoAPI functions, classes and macros to support performance counters, and support for thread pools. In the next few sections we'll briefly describe these new utilities.
Regular Expression Class
The regular expression class adds to MFC's string searching and pattern matching capabilities. Regular expression support is provided by two templates: CAtlRegExp and CAtlReMatchContext. These templates can be specialized to support both Multibyte Character Set (MBCS) and Unicode strings.
To use regular expressions, CAtlRegExp must first be initialized with the search expression using the Parse method, as you can see here:

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

Then the CAtlRegExp object can be used to match against a text string. The results of any matches are returned to the CAtlReMatchContext object. If matches are found in the string, CAtlReMatchContext::GetMatch returns the starting and ending positions of the matched sequences.
64-bit Time and Date Functions
Your code may be safe from the Year 2000 bug, but did you know that the time and date functions in the standard C runtime library will fail after 19:14:07 on January 18, 2038? The new 64-bit time and date functions can be used to represent a date until midnight of December 31, 3000.
CTime and CTimeSpan have new methods to use 64-bit time/date values, including CTimeSpan::GetDays64, CTimeSpan::GetTotalHours64, CTimeSpan::GetHours64, CTimeSpan::Serialize64, CTime::GetTime64, and CTime::Serialize64.
All these methods use the __int64 native compiler type. C runtime library functions have also been updated to support 64-bit time/date: _ctime64, _wctime64, _ftime64, _futime64, _gmtime64, _localtime64, _time64, _utime64, _wutime64; _findfirst64, _wfindfirst64, _findnext64, _wfindnext64; and _stat64, _wstat64.
ATL Collections and Array Classes
ATL 7.0 adds five new template collection classes: CAtlArray, CAtlList, CAtlMap, CRBMap, and CRBMultiMap (described in Figure 4). Why do you need more collection classes when you already have MFC and STL collections? The new classes expand on the existing collections to allow for element types that are not 100 percent compatible with primitive type comparison and copying semantics such as smart pointers, auto pointers, and so on. The new collections accomplish this by using element trait classes, which define how the elements are copied, moved, compared, and hashed. A number of element traits are already defined for commonly used element types: CAutoPtrElementTraits, CAutoVectorPtrElementTraits, CComQIPtrElementTraits, CStringElementTraits, and so on. Unlike MFC collections, ATL collections do not support serialization.
To use the collections, specify both the element type and element trait in the templates, like so:

CRBMap< CString, CStringElementTraits<CString>, 
       CAutoPtr<CMyObject>, CAutoPtrElementTraits<CMyObject> >;

This defines a dictionary, mapping CString's to CMyObject auto pointers. CMyObject's elements are deleted automatically when they are removed from the collection.
Security Classes
Security classes provide wrappers for all the common Windows NT security structures for access control. ATL 7.0 defines wrappers for:

·                     Access control lists with CAcl

·                     Discretionary access control lists with CDacl

·                     System access control lists with CSacl

·                     Security Identifier (SID) with CSid

·                     Access token with CAccessToken

·                     Access token groups with CTokenGroups

·                     Access token privileges with CTokenPrivileges

·                     Security descriptors with CSecurityDesc

·                     Security attributes with CSecurityAttributes

·                     A number of global functions

Unfortunately, ATL 7.0 does not provide higher-level abstractions to the Windows NT security model; you still have to understand Win32 security to make use of these classes.
Cryptographic Classes
Cryptographic classes are wrappers to the Win32 CryptoAPI, providing functionality for encrypting, decrypting, hashing, digital signature, and key management (see Figure 5). Just like the security classes, you have to understand the Win32 CryptoAPI to make use of these classes.
Performance Counters
Performance counters are very useful for monitoring the health and performance of a running application by providing various operating statistics at runtime. A new set of classes and macros have been added to support performance counters, and they are very easy to add into your own application using C++ attributes.
To provide performance counters from your application, you need to implement three types of objects: a Performance Monitor Object Manager, Performance Objects, and Performance Counters.
There is usually only one Performance Monitor Object Manager in your app; it exposes a number of Performance Objects, each contains one or more Performance Counters. The performance counter classes need to reside in a DLL, so you may need to create a separate DLL for your app for the counters.
Here's how to define the Performance Monitor Object Manager:

[ perfmon( name="MyApplication", register=true )]
class CMyApplicationPerfMon

Figure 6 shows how to define a Performance Object and Performance Counter.
To use the counters in your application, you need to initialize the Performance Monitor Object Manager and create an instance of the Performance Object, as you can see in Figure 7.
With the Performance Object, you can update the performance counters directly as members of the Performance Object:

void    SetCounter( ULONG value )
    if ( g_pStatsObject )
        g_pStatsObject->SampleCounter = value;

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.
Thread Pools
Thread pools are very useful in high-performance server apps where you want to maximize the concurrency of your app while minimizing the context switching overhead. The optimal number of concurrent threads is generally a multiple of the number of processors on your server, and the thread pool allows you to queue up worker tasks to be executed.
The MFC 7.0 thread pool implementation is based on the Windows NT I/O Completion Port. CThreadPool is a templated class that you can specialize with your own worker class. When a thread is available, your worker's Execute function will be called. Workers are queued and executed on a FIFO basis.
To define your worker class, you need to follow the Worker archetype by defining:

·                     Initialize, which is called once per thread.

·                     Execute, which is called once for every request queued.

·                     Terminate, which is called once per thread.

·                     In the worker class, declare the RequestType typedef, which defines the type of data to be processed by Execute.

RequestType is an opaque type that cannot be larger than sizeof(ULONG_PTR) (that is, 32 bits on x86 machines), so unfortunately you cannot use large primitive types (such as double) or class instances.
The following example defines a worker class, CMyWorker, that will be used to process a task request with the request type as a pointer to CMyData.

class CMyWorker
    typedef CMyData*     RequestType;
    BOOL Initialize( void* initParam );
    void Execute( CMyData* request, void* initParam, OVERLAPPED* pOverlapped );
    void Terminate( void* initParam );

This is how you initialize and queue a task for the thread pool:

CThreadPool< CMyWorker >    threadPool;
threadPool.QueueRequest( new CMyData());
threadPool.Shutdown( 1000L );

Web Services, Web Applications, andNetwork Classes
New in MFC 7.0 is support for consuming Web Services in MFC applications, writing Web Services, and writing Web applications with ATL server classes. In the following sections we'll show you how to generate code to access Web Services and call methods exposed by the service. We'll also explain how to generate HTML and inject it into an IStream with the support for HTML generation in MFC 7.0, how to use the MFC 7.0 new lightweight HTTP client, and how to send mail from MFC applications with new support for SMTP and MIME-encoded messages in MFC 7.0.
Consuming Web Services in MFC
The Microsoft .NET vision centers on Web Services, where distributed systems on the Internet provide software building blocks that can be utilized through XML-based SOAP remote procedure calls, and UDDI/WDSL to discover and describe the available services. Web Services promise a whole new class of applications such as the Microsoft service codenamed "HailStorm," where the user can really take advantage of the ubiquity of the Internet. By integrating MFC desktop applications to Web Services, you can take advantage of server-based services and not be limited to a Web-based user interface. Unlike DCOM, Web Services can be easily accessed through firewalls and provide truly loosely coupled applications.
Using Web Services in MFC is not much more difficult than using #import with COM objects. SProxy.exe is a command-line tool that generates C++ client code to access Web Services. The generated code is a proxy class that encapsulates everything that you need to do in order to call the remote service.
Here's an example of using sproxy.exe to generate myService.h from the Web Service exposed by an ATL Server:

sproxy /out:myservice.h 

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" ));

HTML Generation Support
HTML generation support is provided by the CHtmlGen class and CHtmlGenBase<> template. CHtmlGen allows you to inject fully formed HTML into an IStream. To use CHtmlGen, simply initialize it with an IStream and call the various methods to generate the HTML tags, as you can see in the following code.

CHtmlGen    out;
out.initialize( aStream );
out.title( "Test HTML" );
out.a( "http://www.microsoft.com", "Microsoft" );

A helper class called AtlHtmlAttrs is used to construct and hold name/value pairs of HTML attributes.
HTTP Client
CAtlHttpClient is a lightweight HTTP client that can send requests to and receive responses from HTTP servers. CAtlHttpClient supports manually set proxies and various authentication schemes (currently, Basic and NTLM implementations are supplied). CAtlHttpClient makes synchronous HTTP requests, so you need multiple threads and CAtlHttpClients to make concurrent requests. The following code navigates to a URL and retrieves a header value and the body content of the response.

CAtlHttpClient    client;
client.Navigate( _T(http://www.microsoft.com ));
CString     contentType;
client.GetHeaderValue(_T("Content-Type"), contentType );
BYTE*        pBody = client.GetBody();

Authentication is supported by CBasicAuthObject and CNTLMAuthObject. To use CBasicAuthObject, you must also supply a class that implements IAuthInfo, which returns the user domain, name, and password. CNTLMAuthObject defaults to using your current user name and password, but you can also supply an implementation of IAuthInfo for alternate identities. Custom authentication schemes can be implemented by deriving from CAtlBaseAuthObject.
The following code adds the NTLM authentication scheme to an HTTP client. Without additional setup, the client will use the current logged-on user's identity for authentication.

CNTLMAuthObject    ntlmAuth;
client.AddAuthObject( _T("NTLM"), &ntlmAuth );

SMTP Support and MIME-encoded Message Support
The new SMTP support in MFC 7.0 allows you to send mail from MFC applications without using MAPI, Outlook®, or any other mail client. CSMTPConnection connects directly to an SMTP server and allows you to send MIME-encoded e-mail.
The mail message can be constructed using the CMimeMessage class, which also allows you to attach files, raw data, and other MIME-encoded messages. CMimeMessage supports all the common header fields including sender, sender name, recipients, CC, BCC, subject, priority, and content type. This shows how to construct a MIME message:

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 );

High-performance Database with OLE DB
ATL's OLE DB Consumer Templates are well-known for their superior performance and ease of use in accessing the OLE DB API. In MFC 7.0 it's even easier to use the ATL OLE DB Consumer Templates through C++ attributes. Attributes are a declarative syntax you place on a class, class member, or in global scope to give the compiler information about that object. The compiler can then generate code, generate metadata, or otherwise respond to the information in the attributes.
For OLE DB, there are five attributes you will use: [db_source], [db_command], [db_column], [db_accessor], [db_table], and [db_param]. These map closely to the OLE DB Consumer Template classes and maps, so they're probably somewhat intuitive. Figure 8 is a code example that gives you an idea of what they look like.
The code in Figure 8 declares a class which opens a connection to the database with the provided command. You instantiate it as you would any other normal C++ class. As you call the class's MoveNext function, the first three columns get automatically bound to the corresponding member variables. Binding by ordinal is a performance optimization. If you want to free yourself from the layout of the row, you can bind by name and let the compiler look it up. The use of attributes simplifies data access using OLE DB. Since attributes just expand to C++ code, you can pass the compiler a command-line switch to see the code after the attributes are expanded.
Updates to STL
New hash_map, hash_multimap, and hash_set classes are new hash-table-based extension classes in the Standard Template Library (STL). They are similar to std::map, std::multimap, and std::set, but with different performance characteristics. Hash-table-based collections generally have faster lookup performance than the binary tree-based collections. Unlike map and set, the hash classes are not ordered containers.
The latest STL implementation also fixes the known multithreading bugs in previous versions of the library, specifically in the map/set implementations and basic_string. In Visual C++ 6.0, basic_string was reference-counted, but the implementation was not thread-safe. In this version of STL, basic_string has been changed to a strictly copied implementation, eliminating the need to do thread-safe reference counting.
And finally, STL has better DLL support. STL no longer holds local static data members, so they can be exported from DLLs without problems.
Visual Studio .NET is a giant step forward for enhancing programmer productivity, and the Microsoft .NET initiative will likely change the programming landscape in the coming years. With this release of Visual C++, MFC 7.0 has been updated to live in this new world of Web Services, while gaining better C++ standard compliance, full integration with ATL, and numerous enhancements to UI and utility classes. Best of all, these new features can be added to your existing MFC applications.

Anson Tsao has 12 years of industry experience in large-scale Windows and C++ development. He recently joined the Microsoft Visual C++ Libraries team.
Walter Sullivan has been at Microsoft for 11 years, working in the Visual C++ product team almost the whole time. Currently he is the Lead Program Manager for the Microsoft C++ Class Libraries.


0 0



取 消