This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we've left these URLs in the text, but disabled the links.


August 1999

Microsoft Systems Journal Homepage

The Logo and Beyond: Solutions for Writing Great Windows 2000-based Apps

The Microsoft Windows Logo Program has nothing to do with the strategy behind the app or its feature set. It helps developers do a good job of implementing functional specs, keeps them informed about the latest innovations and provides a checklist for producing cutting-edge software.

This article assumes you're familiar with the Platform SDK

Code for this article: W2kLogo.exe (10KB) Dino Esposito

Dino Esposito is a trainer and consultant based in Rome, Italy. He specializes in COM and scripting, and is the author of Visual C++ Windows Shell Programming (Wrox Press 1999).

Windows® 95 was a quantum leap in the evolution of Microsoft® Windows, offering a new interface for users and new programming features for developers. The changes in Windows 2000 are more incremental, but every new version of an operating system introduces new concepts for both users and developers.

    To help maintain the standardization across applications that is a key advantage of Windows, Microsoft introduced the Designed for Windows logo program, starting with Windows 3.1. The initiative has two main purposes: it sets guidelines that applications must meet to earn the logo, and it helps users identify products that offer the familiar Windows interface. The logo program guidelines are contained in a hefty tome, formerly known as the Table of Laws, with detailed instructions on the features to include in a Windows-based application.

    In the Windows 95 and Windows NT® 4.0 logo guidelines, Microsoft added information on how to code basic features in addition to what features to include. So whether or not you are interested in obtaining the logo for your product, you'll find the specification useful for its practical suggestions on writing good applications.

    In this article, I'll look at the new logo requirements for the Microsoft Windows 2000 family of operating systems. I'll try to distill these guidelines into concrete programming solutions for delivering up-to-date applications in the millennium to come.

    Note that the guideline document, called the "The Application Specification for Windows 2000," also available at http://msdn.microsoft.com/winlogo is subject to change until Windows 2000 ships. The specification is an interactive document that relies heavily on programmers' feedback. For this reason, some of the guidelines you read here may be superseded in the future, but the changes won't be radical.

Overview of the Windows 2000 Logo

      There are two Windows 2000 logos: Windows 2000 Professional (for client applications) and Windows 2000 Server. a software product with the Windows 2000 Professional logo has been carefully tested on Windows 2000 Professional, Windows NT 4.0, Windows 98, and Windows 95. The product must work reliably on all these platforms, even though Windows 2000 Professional may be required for the user to obtain the full benefits of the product.

    The logo initiative is not only limited to the operating systems area. Analogous initiatives are in progress for Microsoft Office, BackOffice®, Visual Basic for Applications (VBA) compliance, and for hardware devices. The plan over time is for the Windows 2000 logo to mean compliance with all the other logo specifications.

    Having the logo on a product can improve sales and give you visibility through Microsoft logo-related marketing events and press releases. Commercial advantages aside, the logo program keeps developers informed about the latest innovations and provides a checklist for producing cutting-edge software. Good software results from a strategic vision for the product, an intelligently selected set of well-coded features, and various small refinements that enhance the program's usability. The Microsoft Windows Logo Program has nothing to do with the strategy behind the application or its feature set. But it helps developers do a good job of implementing functional specifications.

    If you compare the recommendations contained in the current draft of the Windows 2000 Application Specification against those in the Windows 98 and Windows NT 4.0 version, you'll see they are quite different. The Windows 98/Windows NT logo standard partitioned applications into four categories: utilities, tools, full-screen applications, and file-based applications. All must satisfy a common set of requirements along with some additional constraints, depending on the type of application. With Windows 2000, some of these requirements have been relaxed while others have been reorganized.

    For Windows 2000, there originally were two levels of adherence: Basic and Gold. The Basic logo was to be granted for programs that met a minimal set of standards, while the Gold logo was reserved for programs that exploited special Windows 2000 technologies.

    In the final document the Basic logo has been eliminated, and the Gold logo (with slight refinements) has been renamed the Windows 2000 Application Specification.

The Windows 2000 Application Specification

      A Windows-based application can be client-only, client/server, or server-only. There are separate specifications for client and server applications. Figure 1 summarizes the main requirements for each. Let's look at them in a bit more detail.

    Unless there are special circumstances, a program must consist only of 32-bit components. The 32-bit edition lets you employ 16-bit components if absolutely necessary, but you can't use flat/universal thunking. Windows 2000 (like other Windows NT platforms) doesn't allow 32-bit modules to call into 16-bit modules.

    You have two alternatives. The simpler is to detect the situation and degrade gracefully so as to prevent a GPF or worse. a more complex approach is to replace the thunk code with Windows NT-compatible solutions. For example, you could base communication on the WM_COPYDATa message, which can transfer data and messages back and forth between a 32-bit main application and a 16-bit DLL. Typically you don't have access to the source code of the 16-bit DLL, but usually a 16-bit proxy module in the middle can help (see Figure 2).
Figure Using 16-Bit
Figure 2 Using 16-Bit

      The advent of long file names (LFN) and Universal Naming Convention (UNC) made name parsing much more complex. You can no longer assume that you have a fully qualified path name if the second character of the string is a colon. Be careful when using UNC and LFN names, and ensure that your application can handle both.

    Note that although the logo guidelines include the handling of LFNs, at least two functions in the Win32® SDK still have problems with LFNs: FindExecutable and SHFileOperation. Under certain circumstances, SHFileOperation assumes that paths always begin with the drive letter, and a dialog similar to Figure 3 results when the function is trying to copy an LFN file to a relative path called MyDir. Unfortunately it eventually deduces that the folder is located on a mysterious drive M (instead of the relative MyDir) that doesn't support LFNs.

Figure 3 SHFileOperation Oops
      Figure 3: SHFileOperation

      The Windows 2000 logo guidelines suggest that applications default to the My Documents folder for saving the user documents that applications create. My Documents is a system folder whose physical location may vary from computer to computer. Under Windows 98, it defaults to C:\Windows\My Documents, but users can move it, so you shouldn't assume its location.

    To find the folder's path, issue a call to the function SHGetFolderPath using the CSIDL_PERSONAL flag. This is a Windows 2000 function that requires shell version 5.0. Windows 98 uses shell version 4.72, but the function is available with Windows 98 SR1, Microsoft Internet Explorer 5.0, and Windows NT 4.0 SP4. SHGetFolderPath, which supersedes a similar function called SHGetSpecialFolderPath, is defined in a redistributable DLL (shfolder.dll) that ensures backward compatibility with older platforms.

Installation and Migration

      The first step toward earning the Windows 2000 logo is a Windows 2000-compliant setup utility. The setup program must meet several requirements. First, it should determine in advance whether it will be able to complete the operation by checking for adequate disk space and writing rights. Second, it must not overwrite existing components with older versions, and it must properly update the reference count of shared files.

    These requirements are not new. What's new is the recommended engine for new installations: Microsoft Windows Installer Service. Any setup program built with Windows Installer is automatically Windows 2000-compliant. For a primer, check out "Gain Control of Application Setup and Maintenance with the New Windows Installer" (MSJ, September 1998).

    Note that using Windows Installer doesn't mean renouncing the user-friendly interface of installation utilities such as InstallShield or Wise. Windows Installer is the system service on which setup programs rely, and both of these products have been made Windows Installer-compatible. Windows Installer replaces setup API functions, not setup development tools.

    A Windows Installer package (.msi file) allows administrators to determine what files, and what versions of these files, will be installed. This is especially beneficial to users who maintain lists of "known good versions" of shared DLLs. Administrators can allow installation only if the files are included in the list. Furthermore, the Windows Installer Service management API functions can be used with management tools that allow files and application integrity to be checked remotely.

    The problem of how to cleanly uninstall Windows-based applications has existed since Windows was introduced. Microsoft tried to fix the problem with the Add/Remove Programs Control Panel applet, but this only works if the application registers itself correctly and the setup program is robust. The uninstall procedure can fail if someone inadvertently deletes or moves an initialization file. And sometimes you can't reinstall a program because of an existing installation.

Figure 4 Registry Entries for Uninstall
      Figure 4 Registry Entries for Uninstall

      To make uninstall routines more robust, the Windows 2000 guidelines require a few additional entries in the standard registry uninstall path. Figure 4 shows the entries currently required to receive the logo: the display name of the product and its uninstall command line. With Windows 2000 you also must enter the install location (InstallLocation), the name of the vendor (Publisher), and the version number (VersionMajor and VersionMinor).

    An application that runs on both Windows 9x and Windows NT may require specialized DLLs under Windows NT that are unneeded for Windows 9x. Since the big brother of Windows 98 is Windows 2000 Professional, ensuring that a Windows 9x-based program will survive an upgrade to Windows 2000 involves some special problems.

    When creating an application to run on both platforms, the best approach is to arrange your binaries so there is a single set of DLLs for both. There are two programming levers you can employ to achieve this. One uses traditional tools, and the second relies on a new feature of Visual C++® 6.0.

    The first technique is based on dynamic loading of libraries. You can use LoadLibrary and GetProcAddress to link to DLLs only when needed. By encapsulating these calls in a wrapper DLL, you can expose a common interface to both Windows 9x and Windows 2000.

    Alternatively, you can exploit a new feature of Visual C++ 6.0: the delay load. This lets you reference DLL functions in your code as usual while instructing the linker to delay the physical connection to the library until it's needed. Thus, if you never need that DLL, you never need to link to it. The Visual C++ delay load feature is very useful for DLLs that are needed only under one platform. For more details on how it works, see the Jeffrey Richter and Matt Pietrek columns in the December 1998 issue of MSJ.

    Applications configured to work on Windows 9x-based systems may not work correctly under Windows 2000 due to the changes in the registry and system DLLs. Thus users upgrading their operating systems may have to reinstall Windows NT-based versions of their applications—a nontrivial task in a large network environment. To help your users avoid this, you might want to consider the Migration Extension Interface (MEI).

    With this approach, you provide a file when deploying applications called a migration DLL. This DLL must export seven functions (see Figure 5) that guide the Windows 2000 setup program to correctly migrate application settings and files. The Windows 2000 setup scans a registry path where it expects to find all the migration DLLs:


 HKLM\Software\Microsoft\Windows\CurrentVersion\Setup\Migration DLLs
It loads each DLL and starts a callback-based conversation. For more information, check out http://msdn.microsoft.com/developer/windows2000/migration/default.asp and the latest Platform SDK. The same URL also makes available a Migration DLL AppWizard and a couple of sample DLLs.

    While you can use the MEI, my advice is to arrange your application to migrate smoothly without it. This means providing a uniform interface for calling system-dependent functions and managing registry settings.

User Interface Requirements

      Visual or functional inconsistencies in a program's interface leave users frustrated and confused. So the logo guidelines promote consistency and ease of use to reduce training and support costs. The UI of an application is tied to its mission, so there are few hard and fast rules. In general, you should make it as consistent as possible with the system UI, fused with the shell, usable, and friendly.

    The consistency constraint means that you should gracefully survive changes of screen resolution, color schemes, fonts, and the size of system elements like the window's caption or border. You should also consider the High Contrast option and any other feature related to the Accessibility Options in the Control Panel.

Figure 6 High Contrast Screen
      Figure 6 High Contrast Screen

      The High Contrast option increases screen legibility for people with vision problems (see Figure 6). For the most part this is handled by the system, but you must take care of some elements personally. In Figure 7, the Windows 2000 edition of Notepad is shown on the left, and a 16-bit application I wrote years ago appears on the right. In the older application, the status bar, toolbar, and any application-defined text is still written with the predefined font and is erroneously unaffected by the High Contrast option. Adapting this part of the application is up to you. For more details on accessible software design, see Greg Lowney's articles in MSDN News starting in July 1998.
Figure 7 You'll need to adapt old apps for high contrast.
      Figure 7 You'll need to adapt old apps for high contrast.

      Another interesting chapter in the Windows 2000 logo guidelines is devoted to keyboard management. a mouse gives visual access to interface elements, but some people can't use a mouse, and other people prefer keyboard access for its immediacy. So all program functions must be available through the keyboard as well as the mouse, and this availability must be well-documented.

    The obvious exceptions to this requirement are applications designed to rely on particular input devices. There also might be single features within a product for which a keyboard interface doesn't make sense. When applicable, you can use the MouseKeys option (another of the Accessibility Options). This lets the user employ the numeric keypad to control the mouse pointer and click, double-click, and drag.

The OnNow Initiative

      Applications for Windows 2000 should participate in the power management process governed by the system. The reason for this logo requirement is the OnNow initiative. The idea is to make the PC behave more like an appliance, with applications instantly available when the system is turned on or "woken up" by an event such as a telephone call. Details can be found at http://www.microsoft.com/hwdev/onnow/.

     For programmers, this means applications must be able to suspend and resume on demand without causing damage. a suspend request is often referred to as a sleep request, and arrives through a WM_POWERBROADCAST message. An application should always accept such a request unless it causes loss of data—for example, when a lengthy operation that can't be interrupted is in progress. Once all open applications accept the sleep request, a new message is sent through WM_POWERBROADCAST telling them to perform cleanup operations such as closing files, saving settings, making volatile data persistent, and so on. When the PC awakes, applications are notified through another WM_POWERBROADCAST message and must be able to recover to a stable state.

    There's also the possibility of critical sleep requests where the system suspends all applications without warning. This situation is similar to electrical blackouts. In this case, a restored Windows 2000-based program must be able to detect and signal possible loss of data and recognize damaged files.

Users, Settings, and Policies

      The Zero Administration for Windows initiative lowers administration costs by confining each user and group of users to a private niche within the overall system. The niche includes applications, desktop settings, user preferences, disk quotas, registry access rights, logon scripts, and other settings. This organization has several advantages: users can access any computer on the network and always find their personal environment, backup is easier, and many users can share the same computer.

    Zero Administration for Windows is only possible if applications collaborate. The application must take into account any system policies. System policies are groups of related settings like Shell Restrictions, System Restrictions, or Windows NT Logon. The best way to learn about them is to take a walk inside the Group Policy Editor (GPE), which lets you edit the policies at the system and user levels. The Windows 2000 GPE is no longer a standalone application, but is integrated in the Microsoft Management Console (MMC) along with many other system tools. Figure 8 uses the Windows 2000 GPE to display the available shell restrictions and their current status.

    Assigning values to each policy within a group is up to the system administrator. Each policy can be enabled, disabled, or not configured. a Windows 2000-compliant application must be aware and adhere to all policies. While this may sound like a big job, it rarely affects user applications unless you're providing system-level functionality. I have two general pieces of advice: always try to use ShellExecuteEx to create processes (more on this later), and be ready to gracefully handle error conditions when accessing files or the registry or when performing any system-level action.

    Windows 2000 also introduces a significant change in the overall role of the registry. It's no longer the preferred place to store an application's configuration data. Storing large chunks of data in the registry can take up a lot of room, especially on shared computers. Applications now must use special system folders to save user settings, application data, and temporary files. There are three types of information to save: roaming users' data, nonroaming users' data, and nonuser nonroaming data (see Figure 9). Each is saved to a separate folder.

    Roaming data are those settings that are specific to a user but machine independent—for example, the position of the toolbar buttons. This data must be saved under the CSIDL_APPDATa folder or (for backward compatibility) the HKEY_CURRENT_USER key. When saving to a folder, you should recreate the standard tree of directories:


 CSIDL_APPDATA\[User Name]\Application Data\[Company Name]\[Product Name]
as shown in Figure 10. a call to SHGetFolderPath returns the actual path to CSIDL_APPDATA.
Figure 10 The Directory Tree for Application Data
      Figure 10 The Directory Tree for Application Data

      Machine-specific user data such as the preferred screen resolution is nonroaming, and must be saved in the CSIDL_LOCAL_ APPDATa folder. For backward compatibility, this information can be stored in the registry under HKEY_LOCAL_MACHINE. Finally, nonuser application data such as log files should be stored in the CSIDL_COMMON_APPDATa folder.

Services and Network

      The Active Directory Services Interface (ADSI) in Windows 2000 Server is designed to provide seamless integration across a network. a Windows 2000 Server-based application must publish its directory services (if any) through ADSI. If this is done, client applications don't need to know which computers offer the service or how to bind to an instance of it. All they have to do is simply query ADSI for the service.

    Integrating with Active Directory has many benefits for user applications. First, it provides a secure and extensible infrastructure though a service-centric, rather than server-centric, approach. This makes information and services easily accessible regardless of the network layout. Second, it provides a deployment framework that can be used to efficiently install client components to many workstations.

    A Windows 2000-based application cannot be dependent on the Windows Internet naming service (WINS), so migrating a program that uses WINS to Windows 2000 means implementing DNS names. This shift will take time, so applications must still support WINS. However, Windows 2000-based compliant applications must not rely on it for name resolution, and must support both DNS and NetBIOS names.

Side-by-side Component Sharing

      There are pluses and minuses to Windows-based applications sharing their code. It reduces memory requirements, but also makes applications dependent upon each other. You may remember the problems caused by the shared file ComCtl32.dll that shipped with Internet Explorer 3.0 and replaced the version distributed with Windows 95. Using its new controls and functions made your application dependent upon Internet Explorer 3.0.

    Windows 2000 and Windows 98 Second Edition introduce side-by-side sharing, which enables different programs to use different versions of the same component (COM objects or Win32 DLLs). Side-by-side components are specific to an application and aren't globally shared across the system.

    If you want your program to use its own version of a Win32 DLL, just make sure that the DLL is installed in the same directory as the program. Using multiple versions of a COM object on a single system requires a bit more work. First, register their location using relative paths. This enables the operating system to search for the files. Second, use a different GUID if different versions need specific registry settings (for example, a different threading model), and don't remove the GUID subtree when uninstalling one version of the component.

    Both COM objects and DLLs can manage shared data through memory-mapped files and registry keys, or they can use global objects such as synchronization structures or named pipes. If you plan to maintain different versions of the component and shared data could be a problem, you need to assign per-version names.

    Another noteworthy feature of Windows 2000 is the System File Protection service, a module that runs in the background and checks whether someone replaced any of the system files (DLL, OCX, SYS, and so forth). If so, the original file is promptly restored. Only OS upgrades and service packs are allowed to replace system files.

Tips for Great Programming

      The logo guidelines touch on some of the new features in Windows 2000, but not all. Let's look at some features it doesn't mention that can help you build great applications. In the November and December 1997 issues of MSJ, Matt Pietrek provided an early preview of Windows 2000 from a programmer's perspective. I'll update that information here and discuss how to use the new features in accordance with the logo guidelines.

    What follow are ten suggestions for attractive, friendly, and cutting-edge applications. My focus is on programming features rather than operating system components like Microsoft Transaction Server, Microsoft Message Queue Server, Internet Information Server, Internet Explorer, and Active Desktop.

Create a Fashionable UI and Exploit New Functions

      When it comes to user interfaces, it's hard to come up with a set of rules and requirements that is flexible enough to be useful while remaining consistent. For this reason, the Windows 2000 logo guidelines offer only general suggestions about consistency. Here, I'll go a bit further and talk about some of the features you can use to improve your applications. I recommend that you endow your apps with these.

  • The latest Windows controls: rebars, flat and multi-color toolbars, menu bars, dropdown buttons, tooltips, docking windows, Outlook®-style taskbars, and bitmapped menus.
  • Wizard-like dialogs.
  • a customization console where users can configure menus, toolbars, views, colors, and other elements of your application.
  • An HTML-based help system.
      The ability to build bitmapped menus has been around for a long time, but was little-used until recently. Bitmapped menus are just owner-draw menus, and have been available since Windows 3.1. Menu bars and bitmap menus were covered by Paul DiLascia in the December 1998 and January 1999 issues, respectively.

    With Windows 2000 and Windows 98, wizards underwent a restyling in terms of graphic design and layout. The new guidelines are known as the Wizard 97 specification. You should use wizards with restraint; it would be a mistake to replace all your application's dialogs with wizards. But wizards are often a better choice when a dialog is cluttered with controls and buttons. Here are some guidelines for determining whether to employ wizards.

  • A wizard works best for tasks that are neither trivial nor too complex.
  • The wizard should make choices for the user wherever possible so that the task can be accomplished in less than four steps.
  • A wizard is a good choice for tasks performed infrequently.
  • There should be a way for expert users to skip the wizard.
      So, for example, a wizard is a good choice for creating or editing special documents based on nonstandard templates, but you shouldn't require using even a two-step wizard to perform a common task like saving a document.

    The goal of the Wizard 97 specification is to make the design of wizards more consistent. If you're an MFC developer, you may be familiar with the new standards ahead because MFC 6.0 includes built-in support for new-style wizards. For more details, see the article in the October 1998 MSJ by Paul DiLascia, "Visual C++ 6.0 Brings Home a Full Bag of Tricks and Treats for MFC Developers."

    Windows 2000 brings a number of new or improved technologies plus a large number of APIs, many of which are long-awaited. Figure 11 presents my personal top ten list of the most useful of them. Notice that there's no mention of features like common controls, custom-draw, and APIs such as WinInet that were available with previous versions of Windows.

Figure 12 Taskbar Toolbar
      Figure 12 Taskbar Toolbar

      Finally, if you're writing a suite of applications or you want to expose your commands at the shell level, then consider using taskbar toolbars (aka desk bands) to group functionality. Figure 12 shows a slightly modified version of the sample available with the Platform SDK under samples\winui\shell\bandobjs.

Wrap Up the Web in Your Apps

      Many applications can benefit from being Web-enabled. Here are some hints for doing it.

    Implement an auto-update feature through an Open Software Distribution (OSD) interface or a simple hyperlink to a specific URL, as Windows Update does.

    It's a good idea to add a menu item to point to your company's most important links on the Web.

    If applicable, you can embed a WebBrowser control and make available some useful documents from the Web. In other words, treat the Web as a disk partition that is shared by all users of your application.

    Next, allow users to send documents created in the application through email.

    OSD uses XML and the channel technology to give you a straightforward way to distribute and update software packages via subscriptions.

    I strongly recommend that you start thinking of the Web as a global storage device for your documents. Now Windows Explorer works in much the same way as Internet Explorer. So why not apply this principle inside your applications? Microsoft is doing this; it is one of the key changes in Microsoft Office 2000. As part of this shift, you should consider saving documents in Web-compliant formats such as XML with XSL stylesheets.

Exploit the New Open/Save File Dialogs

      As Figure 13 shows, the Open/Save File dialog is radically different in Windows 2000. The most interesting aspect is the places bar with links to commonly used folders. There's a policy that lets you modify the places bar and the look of the dialog on a per-user basis, but this feature wasn't yet available at the time of writing, so check the Platform SDK for details.

Figure 13 The Open File Dialog
      Figure 13 The Open File Dialog

      Any new or existing application that calls GetOpenFileName automatically inherits the new dialog template. It disappears, though, as soon as you hook the dialog. There are a few other noteworthy innovations in the OPENFILENAME structure. First, you can ask the function to return the selected file object(s) as monikers instead of strings. If you use the OFN_USEMONIKERS flag, the new array of IMoniker pointers, called rgpMonikers, is filled instead of lpstrFile.

    Windows 2000 also lets you decide whether the dialog should automatically store a reference to the selected file in the folder of most recently used documents. It's little-known, but each time you call GetOpenFileName to select a file, a link to that file is automatically stored in the Recent folder, provided there's a program registered to handle the document. To prevent this, just specify the OFN_DONTADDTORECENT flag.

    As you've probably noticed, both Windows 98 and Windows 2000 boast resizable Open File dialogs. At this time, the system makes the adjustments, and you can't prevent them. But what about customized dialogs? To enable sizing, even if you're using hooks or your own templates, just turn on the OFN_ENABLESIZING bit.

    Another new flag warrants a few words. OFN_ENABLEINCLUDENOTIFY works in conjunction with a hook procedure. The documentation claims that this flag lets you be informed about items that will appear in the folder's view. That's correct, but the feature is less appealing than it initially may sound. Once you turn the flag on and define the hook procedure, it starts receiving a CDN_ INCLUDEITEM notify message. The following pseudocode shows how to handle it:


 LPOFNOTIFYEX lpon = (LPOFNOTIFYEX)lParam;
 •
 •
 •
 case WM_NOTIFY:
   switch(lpon->hdr.code) {
      case CDN_INCLUDEITEM:
         return MustInclude(lpon);
 •
 •
 •
   }
The MustInclude pseudo-function returns True or False depending on whether the item is to be included in the view. To let you identify the item, OFNOTIFYEX includes a pointer to the folder's IShellFolder interface and the PIDL of the item. So far, it appears to be a powerful feature that lets you filter the content of a view and decide which file objects a given user can see.

    Unfortunately, the dialog box always ignores your return value if the item has the SFGAO_FILESYSTEM and SFGAO_FILESYSANCESTOR attributes. In other words, if you're dealing with normal files and directories, or folders that are the parent of a file system folder (such as My Computer), by design the OFN_ENABLEINCLUDENOTIFY style doesn't apply. It works only with namespace extensions. a namespace extension, when designed to appear in the common dialog browser, usually obtains a ICommDlgBrowser pointer and calls the IncludeObject method to get the permission to show any of its items. The caller of the common dialog can now, to some extent, control the content shown by a namespace extension.

Figure 14 New Listview Styles
      Figure 14 New Listview Styles

      Sometimes you may want your file common dialogs to exploit features that actually aren't part of the exposed API. For example, Figure 14 shows a dialog that has been enriched with new listview styles such as grid, full row selection, and hot tracking.

    The figure, however, is unable to demonstrate two characteristics that are even more important. The items in the folder view cannot be renamed or deleted. I get the handle of the control, change the listview style, and turn off the LVS_EDITLABELS bit to disable renaming, which may be invoked through either long mouse clicks or the F2 key.

    Preventing deletion is a bit more complicated. You need a thread-wide keyboard hook to intercept the Delete key. Notice that hooking WM_KEYUP in the listview procedure doesn't work since the common dialog installs its own hook. Hooks follow the LIFO rule, so your hook gets called first. To prevent deletion, simply return a nonzero value in the hook procedure.

Figure 15 a Sample App's Context Menu
      Figure 15 a Sample App's Context Menu

      The user, however, still will be able to delete files using the context menu. Both Rename and Delete menu items are still available. Those items are automatically added for file objects that expose the SFGAO_CANRENAME and SFGAO_CANDELETE flags to the shell. Since there's no documented way to turn them off, a workaround is needed. By subclassing the listview's parent window, you can get the context menu handle through the WM_INITMENUPOPUP message. Rename and Delete, when present, are always in the same position. You can figure out their menu IDs and make all the changes. Figure 15 shows the context menu displayed by the sample application for file folders and namespace extensions. Figure 16 presents the source code.

Add Icons and Infotips to Your Folders

       Figure 13 depicts some folders with nonstandard icons. If this doesn't sound particularly exciting to you, consider that they are normal file folders and don't have a namespace extension behind the curtain to provide infotips and icons. To make the directory tree of your applications more intuitive, first put the desired icon file in the folder. Make sure to employ an ICO file that includes both small and large icons. You also can use any executable that includes icons if you properly set the IconIndex entry.

    Next, create an ASCII file and call it desktop.ini. The contents of the file will be something like this:


 [.ShellClassInfo]
 IconFile=[iconfile.ico]
 IconIndex=0
 InfoTip=[tooltip text]
      Restart the shell or force Explorer to refresh its cached icons. The icon for the folder will change, and when the mouse hovers on the icon a tooltip will appear. This feature has been built into the shell since version 4.71 (Windows 95 or Windows NT 4.0 with Internet Explorer 4.0 and Active Desktop), but until Windows 2000 there were problems with it. Under Windows 9x, there was a problem with not all folder views displaying the custom icon correctly.

    Windows 2000 adds another interesting feature: you can make your files unusable from the shell. By adding a NoOpen registry key under the document's ProgID (say, txtfile for TXTs), you can cause the shell to display an error message when the user double-clicks on a file of this type. (This feature was not yet operational at the time of this writing.)

Add an Object Model and Host a Scripting Engine

      Unless your application is a small utility, you should consider giving it an object model. This means that the application's functionality must be organized in a hierarchical structure and made available to external callers through COM Automation objects. In Visual Basic, you can accomplish this using classes. In Visual C++, you can create an MFC-based application that exports its object model through ATL components.

    In addition, I suggest that you consider hosting a scripting engine. This will let your users write script files to automate tasks or create macros to code completely new functions. You can find this same functionality in Microsoft Word and Developer Studio®.

    When it comes to hosting a scripting engine, there are two basic options: the VBa SDK and the ActiveX® Scripting engine. The former is embedded in Microsoft Word, while Developer Studio includes the latter. The VBa SDK (see http://msdn.microsoft.com/vba) is much richer and includes an IDE, but is not free of charge. The ActiveX Scripting engine doesn't include visual toolkits, but it supports Visual Basic Scripting Edition (VBScript) and JScript® and is free. The easiest way to host VBScript or JScript is by means of the Microsoft Script control, which is a wrapper for the necessary COM interfaces.

Get Along with Dynamic HTML

      There are elements in the Windows 2000 user interface that take advantage of Dynamic HTML—for example, the new Search dialog shown in Figure 17. You might want to take a moment and think about adding a bit of Dynamic HTML to your own applications.

Figure 17 Windows 2000 DHTML-based Search
      Figure 17 Windows 2000 DHTML-based Search

      MFC 6.0 provides a CHtmlView class that is a wrapper for the WebBrowser component. The next question is how to avoid distributing the HTML pages and their graphic content separately. a method of the class called LoadFromResource lets you address an HTML page that's embedded in the application's resources. In fact, Visual C++ 6.0 considers HTML pages to be native resource types like bitmaps and icons. This means that an HTML page can be treated like a dialog template.

    And there's more. To embed an entire HTML application in the resources, you need a special protocol to refer to images, scriptlets, and whatever else is stored as a local file. This protocol is called res://. I covered this topic in great detail in the January 1999 installment of Cutting Edge in Microsoft Internet Developer. By combining the res:// protocol with the features of CHtmlView, you can create HTML-based applications that you write using pure MFC and still distribute only executables.

Fuse Your Application to the System Shell

      The title of this section might remind loyal MSJ readers of an article by Jeffrey Richter that appeared in the April 1996 issue. I think that the suggestions he made, which I'll summarize briefly, are still valid.

  • Register your program to handle the documents it creates at the shell level.
  • Create an entry in the shell's New menu for your documents.
  • Add whatever custom verbs are needed to the context menu of your documents.
  • If needed, define a System directory for the files shared by your applications and make it visible to the shell. This way, the necessary DLLs will be reliably found.
  • Consider using shell extensions to add even more functionality to documents.
  • Consider using namespace extensions to get serious about folder customization.
      I'll add another suggestion: try to use ShellExecute or ShellExecuteEx wherever possible to create new processes. The typical way to create processes in Win32 is through CreateProcess, which offers control over process priority, activation state, security context, environment information, startup information, debug mode, and so on. ShellExecuteEx is less powerful, but has a more flexible interface.

    There are two main reasons for using ShellExecuteEx instead of CreateProcess. First, it ensures that system policies are respected, and this is required by the Windows 2000 logo guidelines. Second, it lets you run programs via documents that the program is registered to handle. For example, you can shell execute an HTML file to launch the default browser. Also, ShellExecuteEx offers shell verb support and the ability to hook (IShellExecuteHook) while running. Unless you need the advanced features of CreateProcess, you should choose ShellExecuteEx. Also, note that you can create an enhanced version of ShellExecuteEx to work around limitations such as process class priority or startup information.

Speech-enable Your Dialogs

      It's sometimes useful—and not that hard—to give your applications leave to speak. There are a few commonsense rules to keep in mind. While hardware support is no longer an issue (a 16-bit sound card and a microphone are sufficient), many users are uncomfortable using speech capability. So the first rule is that speech capability should be optional. Second, avoid making extensive use of recorded WAV files; use synthesized voices instead. Third, avoid mixing WAV files and engine-spoken sentences; they are still too different.

    The biggest question is how to use speech capability in a general-purpose application. There are two main variants for generic Windows-based programs. One thing you can do is recognize what the user is saying and map conventional words to ordinary menu commands. Another use is a voice-based help system. This is accomplished via a text-to-speech engine that takes strings or ASCII files as input and outputs them through the speakers.

    The Microsoft Speech SDK 4.0 provides a high-level programming interface to play text and recognize words (C++ classes, COM objects, ActiveX controls). If you need more control, there are also interfaces like DirectTextToSpeech and DirectSpeechRecognition.

Provide Robust Version Checking

      Unless you're so lucky or clever that your application works unchanged on any Windows platform, you should be concerned with version checking. The continuous release of patches, bug fixes, and service packs makes it hard to ascertain what system software your application is running on. This is a problem if applications or components are only guaranteed and supported under a very specific software environment with a certain service pack and product suites installed.

    As mentioned earlier, Windows 2000 makes robust version checking easier. The new API function VerifyVersionInfo was designed with a different approach than you've seen. Instead of returning the current configuration, it gives you a Boolean answer to queries using a mechanism that can be described as Query-By-Example. You fill out an OSVERSIONINFOEX structure, specify a comparison operator (equal, greater than, and the like), and get a direct yes or no answer.

Employ New Technologies

      There are noteworthy new technologies throughout Windows 2000. I'd like to mention three of them: Windows Scripting Host, Browser Helper Objects (BHO), and MMC. You won't be able to use these in every project, of course, but they're good to know about in case you can use them.

    Windows Scripting Host is a little executable that hosts the ActiveX Scripting engine. It exposes objects that manage shortcuts, the registry, and popup messages, and create any available COM Automation component. It's useful as the processor of more modern batch files. Once your application has its own object model, Windows Scripting Host can handle your objects seamlessly. For more information, refer to my book, Windows Scripting Host Programmer's Reference (Wrox Press, 1999).

    BHOs are COM inproc servers used to attach your code to any instance of Windows Explorer or Internet Explorer. If you need to hook or customize the browser, have a look at the January/February 1999 issue of MSDN News.

    Finally, MMC is a generic framework to host management applications or dialogs. I see it as a more sophisticated version of the Control Panel. The counterpart of the Control Panel applets are called MMC snap-ins. These are COM objects that are generally used in Windows 2000 to edit complex application settings offline. MMC snap-ins are required only for server applications, but I suggest that you consider them for customizing client-only modules.

Summary

      Evolution is not revolution, and Windows 2000 is just an evolutionary step forward. Still, it offers a broad array of interesting features. The Windows 2000 logo program is the official guide for using these features in your software, but sometimes it's too general to be of practical use. In this article I've tried to augment its suggestions to help you use Windows 2000 to its fullest.


For related information see:
http://msdn.microsoft.com/winlogo/.
Also check http://msdn.microsoft.com for daily updates on developer programs, resources and events.


From the August 1999 issue of Microsoft Systems Journal.