oth users and developers have been anxiously awaiting the release of Windows® XP. Windows XP represents a number of milestones in the evolution of the Windows operating system. This is the first version of a Win32®-based operating system that is intended for both home and business use.
Windows XP includes a number of improvements to the base operating system as well as a number of new accessories that improve the user experience. The most obvious change in Windows XP is the new shell with its radical new look, revised Start menu, and updated task bar. The new look is based on the ability of Windows XP to be "skinned," allowing the user interface to be radically changed with distributable theme files.
Another new feature of Windows XP is fast user switching that allows multiple users to be logged onto their own sessions at the same time on the same machine. ClearType®, the Microsoft font display technology for LCD screens, and the successor to Graphical Device Interface (GDI), called GDI+, are also new. The control panel even has a new categorization scheme to make it easy for users to find the control panel applet they need.
These new features will be a boon to users, but they will also influence how you design your apps. Windows XP goes to great lengths to be compatible with legacy apps, but there are new features that might require workarounds. And there are some new features you should take advantage of when designing your app.
Building Applications for Windows XP
Before taking advantage of the new features of Windows XP, you'll need to know how to build applications for Windows XP and how to detect when it's running. To build applications that use the new functions in Windows XP you need to set the proper build equates. You will need to define both _WIN32_WINNT and WINVER as 0x0501.
Just because you compile for Windows XP doesn't mean that your application can't run on previous versions of Windows systems. If you want to be backward compatible with previous versions of Windows, the usual precautions apply. For example, don't link to functions that exist only in Windows XP, use the proper size values in structures when running on older versions of Windows, and so forth. The key is to know which version of Windows your app is running on.
Starting with Windows 2000, Microsoft recommends that applications use a new version-checking API. Instead of using that old standard GetVersionEx, applications should use VerifyVersionInfo, which performs a similar function but in a subtly different way. Of course, if your application needs to run on versions of Windows earlier than Windows 2000, you'll have to use GetVersionEx since VerifyVersionInfo is only supported on Windows 2000 and Windows XP.
Fortunately, GetVersionEx was enhanced in Service Pack 6 of Windows NT® 4.0 so that it provides all the information you need to know about not only the version of Windows, but also whether you are running on a Personal, Professional, or Server edition of Windows XP. The Personal edition of Windows XP is a new product aimed at the home market, replacing Windows Me. Knowing if you're running on the Personal edition of Windows XP may be important since there are a number of features in the Professional version (many security-related) that are not available on the Personal edition.
To query which version of Window XP is running, you can use either VerifyVersionInfo or GetVersionEx. The improved version of GetVersionEx accepts the OSVERSIONINFOEXW structure, which includes fields for determining which edition of the operating system is running. Figure 1 shows the structure.
The new fields are the service pack version fields, the SuiteMask, and the ProductType. Interestingly, the difference between the Professional and Server editions of Windows XP is indicated in the wProductType field while the difference between the Professional and Personal editions is indicated by the wSuiteMask field. The code in Figure 2 attempts to use the new OSVERSIONINFOEXW structure to determine if Windows XP is running and, if so, if it's the Personal, Professional, or one of the Server editions.
Knowing how to detect the various versions of Windows XP is only the first step. Now it's time to use some of its new features.
Fast User Switching
Fast user switching affects applications that access hardware or that can only tolerate one instance of their application running on a machine at any one time. Windows XP implements fast user switching using terminal services to maintain a separate user state for each user currently logged on to the system. While this is great for the user, applications can be launched by one user and then again by another user in a different session. Since these two instances are running on the same physical machine, they could cause a conflict.
The best solution is to modify your application to tolerate multiple users running the application simultaneously. To do this, make sure you don't maintain the state of the application in a file that would be used by both instances of the application. For example, don't keep temporary data in the application's working directory. If another user opens the same application, the second instance will attempt to use the same file name in the same directory to store its temporary data. The solution is to use GetTempPath to get the user's temporary directory. Windows XP maintains a separate temporary directory for each user so the data won't conflict when more than one user is logged on.
Of course, you should always use the system functions to query the proper directories to use. Use SHGetFolderLocation to query the directories for startup, desktop, My Documents, and so forth. While this has been a requirement for a number of years for Windows-based application certification, some legacy applications still don't always follow this rule. The Windows XP fast user switching feature is sure to uncover a few more of these noncompliant apps.
There are some applications that need to know when the system switches to another user. For example, Microsoft® Windows Media™ player stops playing music files when the system switches users. After all, the new user may have different taste in music.
An application can use the WTSRegisterSessionNotification function to be notified when the system switches to another user. Windows XP will send messages to notify registered windows when a user switch occurs. WTSRegisterSessionNotification takes two parameters, the handle of a window that will receive the notification messages and a flag to indicate when it wants to be notified. The notification flag can be set to either NOTIFY_FOR_THIS_SESSION to be notified when the user logs on or off the current session, or NOTIFY_FOR_ALL_SESSIONS to be notified when the system switches between any sessions. When the system switches to another user, the application's window will receive a WM_WTSSESSION_CHANGE message. The wParam parameter contains a notification code indicating the reason for the message, if a session is starting, ending, or if the system is simply switching from one user to another.
Most applications will be able to either tolerate being run in multiple sessions or recover gracefully simply by knowing when another session has started or ended. However, there are some applications that simply cannot have more than a single instance running on a single machine. With fast user switching, the standard methods of searching for another instance of your application don't necessarily work. For example, you can't use FindWindow to search for another instance of your main window since the system maintains a separate window class list for each user session.
Instead, an app needs to create a globally named kernel object such as an event or mutex. While the system maintains a separate kernel namespace for each session, it also maintains a global namespace visible to all sessions. An app running under Windows XP can request that a kernel object be named in the global namespace by prefixing the object name with "Global\" as in:
g_hEvent = CreateEvent (NULL,FALSE,FALSE,TEXT ("Global\\MyEventName"));
The code in Figure 3 uses a globally named event to indicate that another copy of an application is running in the current session or another session.
The use of the "Global\" prefix is supported on Windows XP and Windows 2000 with Terminal Services installed. Note that "Global\" is case specific. Windows versions before Windows 2000 do not tolerate a backslash character in the names of kernel objects, so to be backward compatible you will have to perform a runtime check and remove the prefix when running on older systems. Windows 2000 without Terminal Services just ignores the "Global\" Prefix.
Side-by-side Assembly Sharing
Many of you have had the joyful experience of installing a new application only to have the installation break a previously working application. Typically, the problem is due to the installer overwriting a DLL shared by both the new application and the old one. To avoid this problem of DLL Hell, Windows 2000 can detect applications that need to be isolated, that is, to use local copies of DLLs specific to that application. Windows XP goes a step further by allowing multiple versions of DLLs to be available to applications as needed.
The concept of supporting multiple versions of a DLL on the system at one time is referred to as side-by-side assembly sharing. It allows developers of shared assemblies to provide updates to their assemblies without having to guarantee backward compatibility since the new assembly can reside on the machine alongside the old version. It also allows consumers of these shared assemblies to upgrade to the new assemblies at their leisure, not simply when the new assembly is forced onto the machine to support a new app.
The key to side-by-side assembly sharing is the manifest file. There are two types of manifest files: those that describe assemblies shared among applications and those that describe applications consuming side-by-side assemblies. Assembly manifest files describe the various objects within an assembly that are to be versioned. The objects aren't simply DLLs; they can also be window classes, COM servers, interfaces, or type libraries. When an application creates one of the versioned objects, Windows XP determines the version context of the requesting application and redirects the application to the properly versioned object.
Apps use manifest files to specify which version of an assembly they need. Windows XP ships with these three assemblies:
- Shell Common Controls version 6.0 (COMCTL32.DLL)
- GDI Plus version 1.0 (GDIPLUS.DLL)
- Visual C++® Runtime Libraries (VCTRL) version 6.0 (VCNTL.DLL)
Each of these versioned assemblies has an assembly manifest that accompanies Windows XP. Third parties can also provide versioned assemblies for their redistributable modules.
The new common control library in Windows XP is of particular interest. Legacy applications that run under Windows XP are directed by default to version 5.0 of the common control library. This older library uses the square 3D-look controls that have been the mainstay of the Windows UI appearance for a number of years.
To use the new version 6.0 of the common controls, an application must explicitly request the new version by providing an application manifest. The application manifest can be provided to Windows XP as a discrete file or as a resource attached to the executable. When provided as a discrete file, the manifest file must be located in the same directory as the executable and have the same name as the executable with ".manifest" appended to the name. For example, the manifest file for foobar.exe would be foobar.exe.manifest.
Manifest files use XML syntax. The full schema of a manifest file is defined in the latest Windows Platform SDK. Figure 4 shows a manifest file for the application MyApp that requests the new common control library.
The first few lines describe the manifest's XML schema. The first assemblyIdentity element describes the application that is consuming the side-by-side assembly. The name attribute specifies the application's name and the version attribute specifies the application's version in a four part version number. The remainder of the attributes are fairly standard, specifying the application type, Win32, the processorArchitecture, x86, and a description tag.
The dependency element lists the specific dependencies and their versions that the application wants to use. In Figure 4, MyApp is requesting the common control library version 6.0. The common control library is specified in the single dependentAssembly element listed under the dependency element. The only new attribute in the description of the common control library is the publicKeyToken. This field specifically describes the assembly. You can query the proper values for this assembly and other side-by-side assemblies by examining the assembly manifest files stored in the WinSxS directory under the Windows system directory.
Legacy applications can use the new common control library, and therefore acquire the new look of Windows XP as well as the additional functionality of the new common controls by providing a manifest file in the same directory as the application itself. In fact, it's an interesting exercise to create manifest files for legacy applications on your system. Of course, there is a possibility that enabling a third party's applications to use the new common control library will introduce bugs into the applications, so perform this experiment at your own risk.
If you are developing new applications, you can provide the manifest information as a resource within the application. To do this, create an entry in your resource file with a resource type of RT_MANIFEST. This new resource type is defined in the latest version of the Platform SDK. The resource should specify the file name of a manifest file, which will be included in the resource data attached to the resulting EXE file. The following line of code defines a manifest resource that should be included in an app.
1 RT_MANIFEST "test.manifest"
The file test.manifest has the same XML format as the discrete .manifest file. If you are using Visual Studio® 6.0, you'll have to manually create your own manifest file to include with your application as well as the resource statement to include that manifest.
.gif)
Figure 5 App without a Manifest File
Figure 5 and Figure 6 show two views of an application I wrote a number of years ago. Figure 5 shows the application without a mainifest file in the application's directory while Figure 6 shows the application launched with a manifest file in its directory to enable the new common control library. Notice how the controls in the window take on the new themed look familiar to users of Windows XP.
.gif)
Figure 6 App with a Manifest File
Themes
In Figure 6, notice that even using the new common control library, the upper right button in the window doesn't take on the new look of Windows XP. The reason is that the button in question is an owner-drawn button and therefore doesn't pick up the new look by default. In the past, developers only had to worry about updating the look of their owner-drawn controls when a new version of Windows changed the default look of the system controls. Now, with the release of Windows XP, the user can change the look of the controls simply by changing the theme in the control panel. This creates a problem for application-drawn controls since you can't predict what theme the user will choose.
Fortunately for programmers who want to create custom or owner-drawn controls, Windows XP does provide functions to render the themes so that their controls fit into the currently selected theme.
Drawing custom controls using the theme engine is surprisingly easy. First, query to see if themes are enabled for the application using the IsThemeActive. If this function returns true, open an HTHEME handle to the theme data for the control that most closely matches the type of control using the function OpenThemeData. For example, if your control looks similar to a button, the call would look like this:
HTHEME hTheme;
if (IsThemeActive())
hTheme = OpenThemeData (hWnd, L"button"));
else
hTheme = 0; // Need to draw the old fashioned way.
The name of the window class can actually be a semicolon-delimited list of multiple window class names. OpenThemeData will search the list of class names to find the first class with theme data. You should also be careful to see if a theme handle is actually returned. Some themes may not have a theme for the window classes requested. If zero is returned, the control must be drawn with standard GDI or GDI+ calls. Note that the string passed to OpenThemeData is hardcoded as a Unicode string. The Theme API has only a few functions that have string parameters, and those only support Unicode strings.
Once you have a theme handle, the control is drawn using a combination of the theme drawing functions listed here. For each call to the drawing functions, you pass the part of the control you want to draw and its state, such as normal, hot, or selected. The theme API does the rest.
DrawThemeBackground Draws background image for the control
DrawThemeEdge Draws the edges of a rectangle
DrawThemeIcon Draws the icons used within a control
DrawThemeLine Draws a line within a specified rectangle
DrawThemeText Draws text with the proper theme font and size
For simple controls, all that is necessary to completely render the control is to call DrawThemeBackground and DrawThemeText. The code in Figure 7 draws a control that mimics a standard pushbutton.
The neat thing about this code is how short and simple it is. The routine is called WM_ PAINT and it's used for mouse messages such as WM_ LBUTTONDOWN, WM_MOUSEHOVER, and WM_MOUSELEAVE. The DrawControl code isn't concerned with the control colors or font for the text since all this is taken care of by the theme engine.
When the control is destroyed, it should call CloseThemeData to close the theme data handle returned from OpenThemeData. You should also monitor for WM_THEMECHANGED messages, which are broadcast when the user changes the theme. When one is received, you should close your current theme handle, query to see if themes are supported for your application, and if so, reopen a theme handle and redraw your themed controls.
There are a number of other theme functions that let you query the size and features of various theme components. In addition, an application can dictate whether themes are used to draw its controls, controls within Web pages displayed within the application, and non-client areas by invoking the function SetThemeAppProperties. This function takes three self-explanatory flags: STAP_ALLOW_CONTROLS, STAP_ALLOW_WEBCONTENT, and STAP_ALLOW_NONCLIENT that can be OR'ed combined to enable the new theme look. Themes are enabled by default, so you only need to call this function to disable the selected portion of the theme by not passing one of the flags.
Themes are a double-edged sword for Windows XP-based apps. The fact that the user can change the themes adds a bit of work for the developer, but the handy functionality of the theme API makes drawing controls in the style of the current theme fairly easy.
Using ClearType
I think one of the killer features of Windows XP is its support of ClearType. ClearType is a mechanism for drawing on the screen that takes advantage of the ability to address at the sub-pixel level on color LCD screens. Text displayed in ClearType appears sharper than when drawn using the standard anti-aliasing technique which, by the way, is also supported on Windows XP.
In the Display control panel applet, there is a general switch to enable ClearType for all applications using TrueType fonts. Applications can detect the state of this global bit using SystemParametersInfo with the SPI_GETFONTSMOOTHING flag. If font smoothing is enabled, you can query the type of smoothing used with a call to SystemParamtersInfo using the SPI_GETFONTSMOOTHINGTYPE parameter. The call will return FE_FONTSMOOTHINGSTANDARD if standard anti-aliasing is used and FE_FONTSMOOTHINGCLEARTYPE if ClearType smoothing is enabled. As you would expect, these parameters can be programmatically changed with the complementary commands to SystemParametersInfo, SPI_SETFONTSMOOTHING and SPI_SETFONTSMOOTHINGTYPE.
There are times when an application may want to explicitly use, or perhaps explicitly avoid using, ClearType. Controlling ClearType use in your application is a simple matter of setting the proper quality flags when you create a font for use in your application. To draw text in ClearType regardless of the state of the global ClearType flag, create a font and set the CLEARTYPE_QUALITY flag in the lfQuality parameter in CreateFont, or the lfQuality field of LOGFONT for CreateFontIndirect. The font you specify must be a TrueType font. You can specify standard anti-aliasing with the ANTIALIASED_QUALITY flag in lfQuality or, if for some reason you want to prevent smoothing completely, you can use the flag NONANTIALIASED_QUALITY.
The code in Figure 8 displays three lines of text. I've excerpted the font creation routine from the program and the code that draws the text from WM_PAINT. The first line is drawn using the system default smoothing technique, the second is drawn with standard anti-aliasing, while the third is drawn using ClearType. The only difference is the creation of the different fonts used to draw the line.
The results of the test program are shown in Figure 9. The figure shows the program along with a zoomed-in window so that the effects of the three methods of drawing the text are apparent.
.gif)
Figure 9 Using ClearType
The text on the first line is drawn using the default smoothing method. If, as I normally do, I had ClearType enabled in the Display control panel applet, the top line would have looked like the third line because when ClearType is globally enabled, all text drawn with TrueType fonts is smoothed by default.
GDI+
GDI+ is a new graphics library that, as the name implies, builds on the standard GDI functionality. Unlike GDI, which is a set of standard Windows APIs for drawing on the screen, GDI+ is a set of class libraries that more easily integrate into C++ programs. Aside from the language issues, GDI+ provides improved functionality with features such as gradient brushes, persistent path objects, scaleable regions, and a matrix object for easy transforms.
While a complete explanation of GDI+ would require a separate article, I'll provide a brief introduction here if only to whet your appetite. While GDI is centered on the device context and its requisite handle, GDI+ is oriented around the graphics class. When you want to draw on the screen, you create a graphics class and invoke methods on that class. Not that the device context goes away—it's just encapsulated by the graphics class.
GDI+ adds much more than its object-oriented programming model; it exposes much greater functionality than traditional GDI. The code in Figure 10 only hints at the power of GDI+. It draws a gradient fill ellipse overlaid with a line of text with the letters filled with an alphablended gradient brush. Figure 11 shows the window produced by the code.
.gif)
Figure 11 GDI+ Demo Program
To use the GDI+ library, you will need to include the gdiplus.h include file and use the gdiplus namespace, as shown here:
#include <Gdiplus.h>
using namespace Gdiplus;
In addition, you will need to initialize the GDI+ library when your program starts and deinitialize it before your program terminates. The code in Figure 12 shows you how.
I haven't even scratched the surface of the capabilities of GDI+. I hope that this taste will encourage you to look into its advantages. It's a welcome addition to the Windows API.
Control Panel Categories
Finally, for those writing control panel applets, Windows XP has a new control panel application that categorizes the applets. To place your applet in the proper category, you will need to modify the registry under the key:
[HKEY_LOCAL_MACHINE]\Software\Microsoft\Windows\CurrentVersion\
ControlPanel\ExtendedProperties\
{305CA226-D286-468e-B848-2B2E8E697B74} 2
Under this key, you list your control panel applet as the name of a DWORD value. For the value itself, specify the category. Figure 13 shows a list of the valid categories.
Figure 14 shows RegEdit with the control panel category key values. Notice that most of the paths to the control panel applets are specified using environment variables.
In this article I've only touched on some of the new features of Windows XP. For the most part, I focused on the areas of Windows XP that might affect how your application runs. Fortunately, there is little if anything you must do to your application, but there are lots of places where Windows XP provides new functionalities that can enhance your application.
For more information on the new feature of Windows XP, don't forget to read Dino Esposito's article "New Graphical Interface: Enhance Your Programs with New Windows XP Shell Features" in this issue.
|