Nancy Winnick Cluts
Microsoft Developer Network Technology Group
May 1996
Now that you've written your application for Microsoft® Windows® 95, you'd probably like to get the "Designed for Microsoft Windows 95" logo. One of the requirements for the Windows 95 logo is that your application must run under Microsoft Windows NT®. Any features that are not supported in your application under Windows NT must fail gracefully. This means that you should, at the very least, put up a dialog box informing the user that the functionality is not supported instead of making the function call and letting the chips fall where they may. (For complete information on the Windows 95 logo requirements, see http://www.microsoft.com/windows/thirdparty/.) Since Windows NT version 4.0 has the updated shell, you probably figure that your application will run with no problems. Chances are it will. This article is based on a presentation created by Noel Nyman, Windows NT 32-bit Applications Test Lead, and it contains some tips for testing your Windows 95 application under Windows NT version 4.0. Many thanks to Noel for the screen shots and source material.
We all write bug-free code, right? Well, maybe sometimes, when the moon is full or you're suffering from the latest flu, some errant bug might just sneak into your code. Or you might not think of all of the things that you need to test in order to ensure that your code never fails. This is true of all code whether the target is an application or an operating system.
The Systems group here at Microsoft tests loads of applications with Microsoft® Windows®, applications written by Microsoft and by third parties. Doing this helps us find bugs in Windows and helps us make sure that as many applications as possible run on Windows. Third-party application testing is especially useful because the code is written outside of the context of our walls; third-party developers code differently and use the API in ways that we have not yet even dreamed of.
The bottom line is this: The more applications that run under Windows, the more valuable Windows is to our customers. As a result, we test applications to help vendors make as many applications as possible work under Windows.
The issues discussed in this article are meant to illustrate problems you might encounter and scenarios you may want to test when running your Windows 95 application under Windows NT® version 4.0. I have provided screen shots to clarify these issues. Bear in mind that the use of these images is not meant to reflect badly on the coding practices used for any particular application. Rather, they illustrate problems you might encounter but that have not yet appeared. Where possible, the name of the application used in the image has been removed. I'm not trying to point fingers—I just want to help you to not make the same mistakes.
One frequent problem in software design is caused by the necessity to support several different versions of the same operating system. If you check the version and misinterpret the information, your code may fail or may produce unexpected results, such as showing the wrong dialog box or adding incorrect items to a context menu. The application below has not checked for the operating system version correctly and detects Windows NT as the operating system. It assumes that TAPI is not available because TAPI was not supported in Windows NT 3.51. In version 4.0 of Windows NT, however, TAPI is supported.
Figure 1. This application misinterpreted information received from GetVersion.
The application below also misinterpreted the information returned by GetVersion and assumes it's running under Windows 95, making specific calls into Kernel32.dll that are unavailable in the Windows NT version of the DLL.
Figure 2. The wrong operating system has been detected.
You can solve this problem by using the GetVersion or GetVersionEx function to check the version of the operating system. These functions can be used to determine many interesting pieces of information including the build number, the major version (for example, 3), the minor version (for example, 1), whether the machine is running Win32s®, and whether the operating system is Windows 95.
Sounds great, doesn't it? The problem occurs when a developer wrongly assumes that he can check only certain pieces of this information, such as the build number, to determine which system is running. It is important to check all of the pieces of information culled by the version-checking functions. The article "To SUR With Love" on the Win32 Web site (http://www.microsoft.com/win32dev/) covers this problem in detail.
The application in Figure 3 doesn't recognize that Windows NT version 4.0 is a newer version of the operating system than version 3.51. The application is most likely looking only at the minor version number, 0, and ignoring the major version number, 4.
Figure 3. An application that believes 4 is not greater than 3.51.
This application does not open the common dialog boxes when menu options such as File Save As... are chosen. The application correctly determines that it's running under Windows NT and initializes itself to use the common dialog box template in the style of Windows NT 3.51. It also sees version "4.0," assumes that it means the Windows 95 type of Explorer is available, and sets the OFN_EXPLORER bit. Windows NT checks for the OFN_EXPLORER bit, notices that it is being used with the wrong style template, and does not open a common dialog box.
Figure 4. This application used the wrong combination of common dialog box styles.
This might sound like an obvious tip, so it is meant for someone other than you. In a nutshell, if you have written an MFC-based application, be sure to install the correct version of the MFC DLLs that are used by your application in your setup program. Do not assume that the DLLs are already installed. Depending upon the system, they may not be installed.
The MFC30 DLLs are the most common redistributable files not installed by Windows 95 applications, but they're not the only ones. If your application requires any redistributable DLLs, your installation routine should:
I remember my first computer. It was a VIC 20. It had a cassette tape drive. Then, a few years later, I was able to afford a machine with a hard drive. It held a whopping 40 MB. Wow! Windows NT can support drives larger than—be still my beating heart—4 GB. If your application uses variables to check for free disk space or total disk space that doesn't have the capacity to store numbers that large, you and your user may get incorrect results. Check out what happens in Figures 5, 6, and 7 below when you don't check for a large enough drive.
Figure 5. I've really got lots more space than this.
Figure 6. Who stole my gigs?!
Figure 7. I guess I won't be able to install this program.
A hard drive with more than 8 GB of free space was used with Windows NT 4.0 to install and run several applications (this wasn't my machine, sniffle, sniffle). The three gray boxes above are screen captures from three different installation programs. All reported substantially less than 8 GB of free space. In fact, 25 percent of the applications we tested, including some from Microsoft, underreported free drive space. A few even gave the user a negative value. The user only sees this dialog box during installation and may not even notice a problem. Or the user will be savvy enough to know that she has more space than what is shown and will think that some bozo wrote the installation code.
Figure 8. Tsk, tsk. This application shows an incorrect value for free space.
Figure 8 shows a screen capture from a CMD session using a Windows NT-specific shell extension that replaces the DIR command. This application gives the user incorrect information as part of its operation, not just at install time. Oops!
Everyone enjoys seeing an example of a mistake we've made at Microsoft, so here's an example to make you grin. Check out the Figure 9 below.
Figure 9. An application that doesn't support Unicode running on Windows NT.
The AutoRun.exe application shown in Figure 9, running on the Windows 95 CD-ROM, passes file names as ANSI strings to functions in SHELL32.DLL. This works fine when running under Windows 95; however, under Windows NT, the functions called require Unicode™ strings. As a result, the file name search fails, and Windows NT assumes that the required CD-ROM is no longer available and prompts the user to reinsert the CD-ROM.
Windows NT is a Unicode operating system. If you call a function that returns Unicode and converts the string to ANSI using WideCharToMultiByte, be sure to pass the correct flags. The application shown below got the correct drive names from Windows NT but passed incorrect flags to WideCharToMultiByte, which caused the function to fail. The application then used the residual data already in the buffer for each drive icon name. In this case, the buffer contained the path to the \System32 folder on the local machine.
Figure 10. This application did not use the correct flags with WideCharToMultiByte.
Windows NT supports the NTFS file system (okay, so that was redundant: NTFS == NT File System). NTFS supplies compression (among other things), and this compression should be transparent to applications. Some applications, like the one below, detect NTFS compression and wrongly think they can't create folders and files.
Figure 11. e:\foo is a compressed NTFS folder.
When testing your applications, a good rule of thumb is to test installation and file I/O using the following devices:
Under Windows 95, the functionality of PrinterProperties and DocumentProperties are combined. Under Windows NT, however, these two functions are separate. In the screen shots below, the application called PrinterProperties and the Properties dialog box (Figure 13) appeared instead of the Document Properties dialog box (Figure 12), which the user expected.
Figure 12. The user expected to see this dialog box: Document Properties.
Figure 13. But the Printer Properties dialog box appeared instead.
Some applications assume that printer names will always be short (that is, 16 or 32 characters in length). Under Windows NT, printers can have long names.
This application used GetProfileString to get the printer name. This function returned a string with the printer name, printer port, and other information. The application then parsed the string for the printer name, but stopped after 32 characters. It passed the partial name to the CreateDC function. CreateDC failed and displayed the message box shown in Figure 14. The moral of the story: Use MAX_PATH when you allocate buffers for printer names.
Figure 14. This system has a printer installed with a long file name.
Both Windows NT and Windows 95 support long file names. If your application assumes that short (8.3 format) file names are used, then it will not gracefully deal with the long file names (in other words, your application will fail).
Okay, back on my soapbox. One of the things I love about Windows NT and Windows 95 is the ability to create files and folders with names that contain spaces. This allows me to name a folder "Win32 Web Site Docs" instead of "Win32WebSiteDocs". The first is readable; the second looks like a function name. Unfortunately, some applications (which shall remain nameless here) don't support embedded spaces, and my editor has to rename all of the files I give her. (I stoically refuse to give up my spaces. Aren't you glad you aren't my editor?)
Since Windows NT, and your application, may be used outside the United States, be sure to test foreign language characters and Unicode in file names and folder names, as well as testing using ASCII. In addition to testing for spaces (you will be doing that now, won't you?), test using the characters below that are not legal for DOS file names and folder names but are acceptable under Windows NT. Remember, even if you don't want to support these characters, other applications will, and your user will want to open her other files with your application. Be sure to test using several "." (dots) in file names; more than one is allowed.
Illegal characters in DOS
+ | Plus sign |
, | Comma |
. | Period |
; | Semicolon |
= | Equal sign |
[ ] | Brackets |
Since you gracefully support long file names, Unicode, and spaces (wink, wink), you probably feel like you are home free. Let's talk.
Both Windows 95 and Windows NT save not only the long version of the file name, but also save a short version of the file name. Short file names are not created equal under Windows 95 and Windows NT. Windows 95 uses a consecutive number algorithm when creating short file names, while Windows NT uses a combination of consecutive numbers and random characters when creating short file names. Windows NT does this to speed file searches, not to make it harder on the developer. You'll probably never need to know, or care, what the "real" short file name is. Windows NT takes care of all that. On the other hand, if your application validates short file names based on the Windows 95 algorithm or modifies the directory structure, it may not perform as expected under Windows NT. Table 1 shows you the short file names that are produced by Windows 95 and Windows NT for the same three files.
Table 1. Short File Names Produced by Windows 95 and Windows NT
Long File Name | Windows 95 Short File Name | Windows NT Short File Name |
Maps of Africa.vss | mapsof~1.vss | mapsof~1.vss |
Maps of Europe.vss | mapsof~2.vss | mapsof~2.vss |
Maps of England.vss | mapsof~3.vss | macebf~1.vss |
Don't assume that all system executables are stored in the \System directory. Windows NT may store system executables in different locations from Windows 95 or use different names for them. You should supply the file name to the CreateProcess function to launch executables. The file will run regardless of its location so long as the file is located in a directory that is included in the system path.
In the example below, the application has hard-coded the path to WinHelp.exe in the %windir% folder. Under Windows NT, that's the 16-bit Help engine. But this is a 32-bit application and all of the application's .hlp files are version 2.0 (the 32-bit version), which the 16-bit version of WinHelp can't open. The error dialog box pops up, and a minimized WinHelp appears on the task bar each time the user chooses a Help option or clicks a Help button.
Figure 15. Hard-coded path to WinHelp causes the File Open command to fail.
Windows 95 and Windows NT do not use the same registry keys for all objects in the system, such as printers. The application below was written for Windows 95 and looks for the default printer by enumerating the subkeys of the HKEY_LOCAL_MACHINE:System\CurrentControlSet\Control\Print\Printers key.
Figure 16. This application is using the registry key to enumerate printers.
This works fine for Windows 95, but under Windows NT this information is stored in a different part of the registry. Instead, use GetProfileString to find the name of the default printer, and use EnumPrinters to get information about the installed printers. These functions work on both platforms.
Windows NT and Windows 95 use 32-bit handles; however, under Windows 95, the high 16 bits are zero. Windows NT 3.51 accepted a handle with all zeroes in the high 16 bits by using that for a "wild card" match. That's why some code that passes only the low 16-bits of a handle still succeeds on version 3.51. Windows NT 4.0 enforces the full 32-bit rule. (We all must correct our bad habits sometimes!) Under Windows NT, the high 16 bits are usually nonzero. The application below is assuming that the high 16 bits are zero, so it strips off the high 16 bits and uses only the low 16 bits as the handle to the dialog box. The dialog box appears, but when the application subsequently uses the 16-bit version it created to send messages to the dialog box, the messages do not get to the dialog box. As a result, the dialog box never gets its Destroy message and remains forever.
Figure 17. You can click the button, but the dialog box will not go away.
Although the example in Figure 18 comes from a 16-bit application, 32-bit applications might also attempt direct hardware access. Windows NT will display an error dialog box, and your application may fail.
Figure 18. This application attempted to directly access the hard disk.
The moral of the story: Don't use private 16-bit drivers. Even if they appear to function in test situations, they're likely to fail in the more complex environments your users will create. Play by the rules, and no one gets hurt.
Under Windows 95 and Windows NT, you cannot assume that all functions support all of the same parameters. This is because of different functionality of the operating systems. For example, the refresh rate parameter of ChangeDisplaySettings is ignored by Windows 95, but is supported by Windows NT. Using an inappropriate refresh rate value when setting the display settings won't matter on Windows 95. But, it may cause your display to go into the "let's-create-some-modern-art" mode on Windows NT. This problem can be avoided by checking for the operating system and using the appropriate parameters. Or, better yet, make sure all of your parameters are valid, even if one of the operating systems doesn't use all of them. What's the best way to make sure everything is valid? I did mention testing, didn't I?
This problem occurs on Windows 95 as well as Windows NT. Some splash screens appear with a border on the right and lower margins when the machine is using large fonts. If this screen is just for display purposes, there's no harm done. It just looks funny. Figure 19 shows the screen using small fonts, and Figure 20 shows the same screen using large fonts. The gray background shows at the right and lower margins on Figure 20.
Figure 19. System using small fonts
Figure 20. System using large fonts
The problem occurs when the bitmap is a static control on a dialog box. The dialog box resizes along with the font size, even if there are no text controls in it, and the static bitmap does not resize. This makes the dialog box background color become visible. This is ugly, but it doesn't change the functionality of the dialog box.
Some applications, however, can run into functional problems. In the simulated screen shot above, the splash screen is used as an index to a catalog of products. The user can click on one of the language names to get a short description of the product. The user closes the dialog box by clicking on the words "exit (TRUE)." When the machine is using large fonts, the dialog box resizes and the "hot spots" associated with the mouse clicks move along with it. The bitmap, however, does not resize, and the hot spots no longer coincide with the language names or the exit text. In other words, the application doesn't work as expected, and the user is challenged trying to figure out how to get rid of the dialog box. The developer could have avoided this particular problem by creating a static window with SS_BITMAP and SS_CENTERIMAGE flags instead of using a dialog box window.
If you've created text windows that are too small to hold it, you may also see clipping of large font text in any dialog box. The bottom line is this: Test applications at different font sizes.
Your user may not have some rights or permissions your program needs to install or execute an application. Document any special rights your user needs (such as Administrator and Backup Operator). The error message below appeared when the user attempted to copy a file on a machine where he had only guest rights (that is, no ability to write to the disk).
Figure 21. Make sure you have permissions set correctly.