Export (0) Print
Expand All

Microsoft Windows 2000 Application Compatibility

 

Microsoft Corporation

July 2000

Summary: Explains the most common compatibility issues encountered when installing and running applications on Windows 2000. Also describes common issues and changes that will affect the way some applications run on Windows 2000, which should be considered when designing new applications for use on this platform. (18 printed pages)

Contents

Introduction
Setup and Installation
Location Changes and Hard-Coded Paths
Files and Folders
Security
Networking
Memory Management
Shell
Drivers
DirectX
Other
Additional Resources

Introduction

This article explains compatibility issues applications encounter when installing and running on Microsoft® Windows® 2000. Windows 2000 includes changes that move the platform forward towards the goal of making a more useful and reliable platform. These changes affect the way in which some applications run, and should be considered when designing new applications for use on Windows 2000. This article will be updated as we encounter more compatibility issues in the future.

Setup and Installation

Version Checking

The number one application compatibility issue on Windows 2000 is problematic versioning code. Many of the applications we have tested call GetVersionEx and only run if it returns a specific version. Windows 2000 returns a new major version number (5) that is not handled by such programs.

Microsoft has developed a tool that changes the return value from GetVersionEx for specific applications. Using this tool, users can run applications with problematic versioning code. In most cases, applications that run when the major version number is (4) will run on Windows 2000.

New applications should be designed to work whenever the version number is equal to or greater than current versions.

Below is an example of proper versioning code:

BOOL bIsWindowsVersionOK(   DWORD dwWin9xMajor, DWORD dwWin9xMinor,
                            DWORD dwWinNTMajor, DWORD dwWinNTMinor, WORD wWinNTSPMajor )
{
    OSVERSIONINFO           osvi;   
    // Initialize the OSVERSIONINFO structure.
    ZeroMemory( &osvi, sizeof( osvi ) );
    osvi.dwOSVersionInfoSize = sizeof( osvi );    
    GetVersionEx( &osvi );  // Assume this function succeeds.
    // Split code paths for NT and Win9x    
    if( osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS )
    {
        // Check the major version.
        if( osvi.dwMajorVersion > dwWin9xMajor )
            return TRUE;
        else if( osvi.dwMajorVersion == dwWin9xMajor )
        {
            // Check the minor version.
            if( osvi.dwMinorVersion >= dwWin9xMinor )
                return TRUE;
        }
    }
    else if( osvi.dwPlatformId == VER_PLATFORM_WIN32_NT )
    {
        // Check the major version.
        if( osvi.dwMajorVersion > dwWinNTMajor )
            return TRUE;
        else if( osvi.dwMajorVersion == dwWinNTMajor )
        {
            // Check the minor version.
            if( osvi.dwMinorVersion > dwWinNTMinor )
                return TRUE;
            else if( osvi.dwMinorVersion == dwWinNTMinor )
            {
                // Check the service pack.
                DWORD dwServicePack = 0;

                if( osvi.szCSDVersion )
                {
                    _stscanf(   osvi.szCSDVersion,
                                _T("Service Pack %d"),
                                &dwServicePack );
                }
                return ( dwServicePack >= wWinNTSPMajor );
            }
        }
    }
    return FALSE;
}  
//

The Windows 2000 API includes the function VerifyVersionInfo, this function compares a set of operating system version requirements to the corresponding values for the currently running version of the system. When a new version of the operating system is released, your application will still install and run on it. There are actually many more options and ways to use VerifyVersionInfo, but to check if the operating system is new enough you could call using the three flags below, and check the major version, the minor version, and the service pack. You would be saying "My application needs Windows NT® version 4.0, with SP 2, or greater" and then ask VerifyVersionInfo "Is this OS that I'm running on up to that standard?" It would return either a true or a false.

Below is an example of these flags:

VerifyVersionInfo(&osvi, 
      VER_MAJORVERSION |
      VER_MINORVERSION |
      VER_SERVICEPACKMAJOR,
      dwlConditionMask);

VerifyVersionInfo, is only available on Windows 2000 and later versions of the platform.

Below is an example of this versioning code:

BOOL bIsWindowsVersionOK(DWORD dwMajor, DWORD dwMinor, DWORD dwSPMajor )
    {

    OSVERSIONINFOEX osvi;
   ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
   osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
   osvi.dwMajorVersion = dwMajor;
   osvi.dwMinorVersion = dwMinor;
   osvi.wServicePackMajor = dwSPMajor;
   // Set up the condition mask.
   VER_SET_CONDITION( dwlConditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL );
   VER_SET_CONDITION( dwlConditionMask, VER_MINORVERSION, VER_GREATER_EQUAL );
   VER_SET_CONDITION( dwlConditionMask, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL );
   // Perform the test.
   return VerifyVersionInfo(&osvi, 
                             VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR,
                          dwlConditionMask);
     }

Applications must use this technique to meet the certified application specifications for Windows 2000 as outlined in the Application Specification for Windows 2000 Server and the Application Specification for Microsoft Windows 2000 for Desktop Applications.

Using "user" Folders Instead of "common" Folders

Windows NT allows customized Start menus on a per-user basis, but also offers the global Start menu that is common for all users. Applications should always install to the common start menu and/or desktop so that they are available to all users.

When using SHGetFolderPath (explained later), applications sometimes use one folder ID for installation and the other for uninstallation. This causes shortcuts to remain after uninstallation.

Assuming Installation Requires Reboot

Windows 2000 does not require rebooting as often as other versions of the platform when installing software. Applications should handle each platform separately and only reboot when it is really required.

For DirectX applications, be sure to check for DirectX Setup's return value without assuming that a reboot is required. If a reboot is absolutely necessary under Windows 2000, you must acquire the appropriate security privileges before calling ExitWindowsEx.

Shipping System DLLs with Your Application

Applications should not overwrite a system DLL unless the application DLL is a newer version than the system DLL.

Some applications install a system DLL (like DDRAW.DLL, for example) to the application's directory so that it is loaded first (due to the loader's path prioritization). This can create a problem because some versions of system DLLs loaded in the application’s directory are incompatible with newer versions of the operating system. If a newer system DLL in the system folder is actually compatible with the application, it cannot be loaded because it is superceded by the application-installed DLL.

Large Drives

Hard drive size has increased dramatically over the years to the point that applications may not handle large amounts of free space correctly. For example, calling GetDiskFreeSpace, which returns 32 bit values, gives incorrect results when there is more that 2GB of free space.

You need to use GetDiskFreeSpaceEx, which uses a ULARGE_INTEGER_ instead of an INT, to return available free space.

Location Changes and Hard-Coded Paths

The location of many folders and directories changes with each release of Windows. These location changes are the cause of a number of application incompatibilities on Windows 2000.

The Windows functions SHGetFolderPath, GetWindowsDirectory, GetSystemDirectory and GetTempPath should be used to find directories used by Windows. Applications that use hard-coded paths to specific locations, may not work on other versions of Windows.

For example, the My Documents folder was just a folder off the root on Windows 9x, somewhere on C drive, or on D drive. Windows NT® 4.0 created personal folders on a per-user basis, and put each user’s folder deep in the Windows system directory:

%windir%\profiles\user\personal

Windows 2000 has moved it yet again. Instead of the system directory, or even at the root, a default installation of Windows 2000 Professional puts the folder on the system drive at:

\Documents and Settings\user\My Documents

In many enterprise situations, the user’s My Documents folder may even be moved off the local machine to a network share. That makes it available to the user from anywhere in the enterprise using roaming profiles. However, an application that searches for the folder on the local system instead of hard-coding the path, will not be able to find the relocated My Documents unless it uses the Windows API functions.

SHGetFolderPath

To find shell folders such as My Documents, use SHGetFolderPath to get the path. You can use it on Windows 95, Windows 98, or any Windows platform, and it will get you to the right place.

The following code example shows how to use SHGetFolderPath; it is a very straightforward API to use.

            // SHGetFolderPath can work everywhere SHFOLDER is installed.
            HMODULE hModSHFolder = LoadLibrary("shfolder.dll");
            if ( hModSHFolder != NULL ) 
               {
               (*(FARPROC*)&g_pfnSHGetFolderPath = GetProcAddress(hModSHFolder, "SHGetFolderPathA"));
               }
            else
               g_pfnSHGetFolderPath = NULL;
            }

   if (g_pfnSHGetFolderPath != NULL )
      g_pfnSHGetFolderPath(NULL, CSIDL_SYSTEM, NULL, NULL, szSystem32);
   else
      szSystem32[0] = '\0';
   OpenFileName.lpstrInitialDir   = szSystem32;
 

Physical Drive Number

If your application accesses hard drives and volumes in a low-level way (as with virus scanners, for example), you'll need to find the physical drive number, and you are going to have to change the way you find that number. The physical drive number isn't in that symbolic link anymore, anywhere. Instead you will need to use a couple of IOCTLs that are available. The first one:

IOCTL_STORAGE_GET_DEVICE_NUMBER

works for a single drive number. For example, if the drive is a C drive, that will work, or even if you have multiple partitions on a drive. But if you have a multi-volume set, you'll need to use:

IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS

This works on Windows NT 4.0 as well.

Finding the System Directory on Terminal Server

The GetWindowsDirectory function retrieves the path of the Windows directory, and returns a per-user system directory. If you're running on terminal server, you may find that you're not getting the real system directory; you may be getting a system directory that's set up for a particular user. Use the GetSystemWindowsDirectory function to get the actual system directory on terminal server.

The REG_EXPAND_SZ Registry Value Type

First introduced in Windows NT, the REG_EXPAND_SZ registry value type is an expandable data string that contains a variable to be replaced when an application retrieves the value. In this example, the string "%SystemRoot%" will be replaced by the actual location of the directory containing the Windows NT system files:

..........File : REG_EXPAND_SZ : %SystemRoot%\file.exe

In many cases, older applications don't handle this registry value type properly. When an application reads this value type, it must call ExpandEnvironmentStrings before using the value.

Files and Folders

Long File and Printer Names

Long file and printer names have been available since Windows® 95 was released. Windows 2000 requires applications to support them correctly. Older applications may have undetected errors in their long file name support.

For example, an application may use a smaller buffer than it says it does. On older Platforms this may go undetected because typical filenames were short. The paths have tended to get longer, so now instead of the average path being 30 to 40 characters, the average path is 60 to 70 characters. For more information about long filenames, see Making Room for Long Filenames.

Some applications don’t handle the spaces in directory names such as "Documents and Settings." Applications parse through directories, and as soon as they find the word "Documents" they assume they are at the end of the chain that ended with "My Documents." The application fails because it has not really found the correct directory.

Test your long file name support. The Windows 2000 Desktop Applications Test Plan includes many examples of difficult file names, which you can use to make sure you are supporting long file names correctly. You can use the test tools, available at that same location, to create the long paths and file names on your test system, so you don’t even have to enter them by hand. The Test Plan has details about how to effectively test your application for long path, file and printer name support.

File I/O with Non-buffered Files

If you're trying to do some file I/O without using the system-supplied buffers, you're using the FILE_FLAG_NO_BUFFERING flag with CreateFile . In this case you must make sure the buffer you pass to ReadFile and WriteFile is aligned correctly for the device. The alignments are slightly different for Windows 2000, especially in supporting some of the new devices, such as the new Ultra 66 IDE drives. So, you must make sure that you allocate the buffer correctly. The easiest way to do this is to use VirtualAlloc function. This function aligns to memory page boundaries, which tend to work out to volume sector boundaries, so devices will require more alignment than VirtualAlloc provides. VirtualAlloc will always align the buffer on an even boundary; therefore, it will always be aligned correctly for whatever size you'll need for the device doing the file I/O. You have to make sure that your reads and writes are in multiples of the actual sector size of the I/O device. You can get the sector size from GetDiskFreeSpace.

GetFileAttributes

The GetFileAttributes function returns attributes for a specified file or directory.

On Windows 2000, directories residing on CD-ROMs have the READONLY bit set along with other attribute bits. Applications must mask the desired bit instead of simply checking for equality.

The following example shows the correct way to test for the FILE_ATTRIBUTE_DIRECTORY attribute:

if( GetFileAttributes( "H:\Install" ) & FILE_ATTRIBUTE_DIRECTORY )
{
    // This is a directory!!
}

Security

User Permissions

Both power users and administrators should be able to install system wide applications. Corporations may want to have locked-down machines on which only a small number of people have administrator rights. In this environment, system wide applications will be installed by power users rather than by administrators.

All users should be able to install an application for their use, rather than for anyone on the system. If I have a game that I want to run, I should be able to install that game and not let anyone else on the system use it. Remember, however, that a nonpower user is not going to be allowed to write to the Program Files directory; that user will not be allowed to write to HKEY_LOCAL_MACHINE. During your setups, then, you should try to open HKEY_LOCAL_MACHINE and see if you're allowed to write there. If your setup is not being run by a user with write permissions to HKEY_LOCAL_MACHINE, say, "You are not allowed to install this application for all users on this machine; do you want to install this application for personal use?"

Default Server Permissions

Another security issue is default server permissions. If you are installing a server application or service, and you are using what is an unprivileged service account now— in other words, you are not using an account in a local system, and you are not using an account that is set up to be a member of a local administrators group— it is unlikely that your account has the privileges it needs to actually run the service. On Windows 2000, unprivileged users have fewer permissions than those on Windows NT 4.0. For example, unprivileged users cannot write to anywhere on the Windows system directory. If you are running a server application, make sure that it has the right account logged in, so it does have all the necessary permissions to function correctly.

Networking

NetBIOS

NetBIOS has always been set as a default configuration on Windows NT. Starting with Windows 2000, NetBIOS is not set as the default configuration. If your application is calling functions that use NetBIOS on a system without NetBIOS, the functions will return errors. Applications must handle this situation correctly. Applications can use functions that don't depend on NetBIOS, or the application can ask the user to enable NetBIOS.

New Network .inf Files Needed

Network devices, including network drivers, transport drivers, and some network file print providers, need to have new network .inf files for the device, in order to support Windows 2000 Plug and Play. Windows 2000 needs these new files during installation. You may have used this format already, since it is compatible with Windows 98.

Memory Management

Increased Stack Consumption

Windows 2000 requires more stack space than Windows NT 4.0. Applications that are compiled and linked to use small stacks may run out of stack space too quickly. You can find out if you might be doing this in your application by checking for the /STACK-linker option in your link line if you're using that, or the STACKSIZE-.def file that is using the STACKSIZE parameter or the /F option on your compiler. You need to re-examine the stack space required by your application under Windows 2000.

Heap Management

Changes in heap management can expose bugs in the memory management of applications.

Beginning with Windows NT 4.0 Service Pack 4, We changed the heap manager to make it more efficient and faster, especially with multiprocessor machines. Windows 2000 has made some additional changes. We didn't make any changes to the APIs themselves but we have made some subtle changes to the way the blocks are reused, exposing bugs in some applications.

Previously, when you freed a block, that block would go on the unused block list, at the bottom of the list, and eventually it would be reused. Now, we are caching the last blocks that you used; they are effectively going to the top of the list, and when you need another block, you're more likely to get the block back sooner than later. That keeps you on the same page, and speeds up the whole system. This enhancement exposes bugs in applications that try to access memory after it’s been freed. The application will allocate memory, it will use the block, it will free the block, and then it will try to read or write it again. When the application allocates more memory the block gets reused and the data will get corrupted. Because the memory is sitting in a block or a page that you already own, and because you're doing a read or write from a place that is perfectly allowable for you to write, it's not going to cause an access violation.

In order to make better use of the room on a page, Windows 2000 and Windows NT 4.0 Service Pack 4 might move an allocation if you've done a reallocation for a smaller block. Applications that assumed that reallocating to a smaller block would not move the block may corrupt data.

Shell

Super Hidden Files

A new feature we have on Windows 2000 is Super Hidden Files. These are files with both the System and the Hidden attributes. The files are still there but when you are in Windows Explorer, these files are not going to show up. Even if you check Show Hidden Files, you won't see them. There is a new check box on the list of attributes for a folder that will allow the user to see these files that is unchecked by default.

This is not really a compatibility problem for the 32-bit applications. Applications can see these files in the common Open File dialog box, and can open the files without any problem. The command line will still work. If you do a Dir /ASH to see the Super Hidden Files, you will see all the files.

The only compatibility issue here is for 16-bit applications, which may fall into a trap if they make calls through the INT21 on MS-DOS. The INT21 on MS-DOS will only find the hidden service file, if you have actually asked to find hidden files.

Some of the files that you'll find hidden:

MS-DOS system files, such as io.dos and Office Fast Find files.

OPENFILENAME Structure

The OPENFILENAME structure contains information the operating system uses to initialize the system-defined Open or Save As dialog box. After the user closes the dialog box, the system returns information about the user’s selection in this structure.

On Windows 2000, the OPENFILENAME structure has a slightly different behavior for the initial directory. If OpenFile doesn't find any of the files of the type you are looking for, it will default directly to the My Documents folder as a shortcut.

Setting the Foreground Window

In Windows 98 we introduced rules about when an application’s window can become the foreground window. We did this to limit the conditions when a window can pop to the top. You can’t just rely on your application calling SetForegroundWindow to get the foreground window.

To ensure that your application gets the foreground window, observe these rules:

You can take the foreground window if:

  • Your process is already the foreground process.
  • Your process was just started by the foreground process.
  • If the foreground process is currently being debugged.
  • If there is no foreground window at the moment.
  • If your process received the last input.

However, if the foreground timeout lock has happened—the foreground lock hasn't done something in awhile, and appears not to be responding—someone else can take the foreground.

Lastly, if any system menu is active, your application can't grab the foreground.

DS_SHELLFONT

The Dialog Box Style DS_SHELLFONT, indicates that the dialog box should use the system font. The typeface member of the extended dialog box template must be set to MS Shell Dlg. otherwise this style has no effect.

On Windows 2000, if you specify DS_SHELLFONT, you cannot change the font face anymore. We're using Microsoft Shell Dlg 2 as the font face; you can change the size, but you cannot change the face.

Drivers

Hooking Display Drivers

Applications such as remote control applications that try to intercept calls to a display device need to use a new technique. Such applications should use the new Display Driver Management Level (DDML) to mirror that output to a remote device. This will enable multiple display drivers, what these remote control applications are doing anyway. The documentation for DDML is included with the Windows 2000 DDK.

Write-Protected Kernel Mode

Anything that is running in kernel mode will have write-protected regions of the memory. If you have a device driver in which you've used code segments or string segments, and as a shortcut you put something in what is listed as a read-only section—your notes, for example—that's not going to work on Windows 2000. We're not allowing anything in kernel mode to override the protections that are supposed to be there, because this had been a leading cause of crashes.

We've found that many device drivers break this rule for Windows 2000. The system examines each driver, and if it determines that a device driver is targeted at Windows NT 4.0 instead of Windows 2000, it won't enforce this rule. Otherwise, too many device drivers would not work at all. For any device driver that is written for Windows 2000 or upgraded so that it works well on Windows 2000, however, the system will enforce this rule.

DirectX

Support for Task-Switching

Many DirectX applications avoid supporting task switching by disallowing it. This is possible in Windows 9x by intercepting system key press messages. This is also possible in Windows 2000 by using SetWindowsHookEx to install a low-level keyboard hook (WH_KEYBOARD_LL).

New applications should always support task switching. For any surfaces that your application builds up over time, you will need to use SYSTEM MEMORY. This is because your application needs reconstruct any surfaces that are in video memory when a task switch happens. On Windows 2000, a surface can be lost in the process of task switching and unlike Win9x the surface is not guaranteed to be at the same address after the task switch. The DirectX documentation contains information on how to properly design "task-switch-safe" applications.

Other

Opening Another HKEY_CURRENT_USER

Sometimes applications, especially some server applications, need to get information from a different HKEY_CURRENT_USER than the one for the current user. The correct procedure for opening a different HKEY_CURRENT USER is to call RegOpenCurrentUser. This call avoids problems with caching that cause other techniques to get the wrong user.

Accessing Tape Drives

If you have an application that uses a tape drive, you'll need to change the way you access that tape drive. The new Hierarchical Storage Management uses the Removable Storage Manager, looks for files that have not been used in a long time and moves the file to tape. This frees space on the drive without deleting the file.

Removable Storage Manager has control of the tape drive and prevents other applications from using it. Read Development Considerations for Storage Applications in Windows 2000 to see how to get Removable Storage Manager to release the tape drive.

New Input Method on Windows 2000

We're supporting a new input method on Windows 2000. We want to pass some information in the wParam that you would get with the WM_KEYUP and the WM_KEYDOWN messages. To do that, we need for you to pass that wParam untouched to TranslateMessage. If you don't do that, users are not going get the full effect of this new input method.

Checking for Bit Flags

We're finding applications that are checking for bit flags and trying to use some kind of equality operator on them instead of actually checking for the presence or absence of a particular bit. Obviously, we're going to be adding flags to Windows 2000 and to versions after Windows 2000. So you need to make sure you check for the bits and not for equality. Here is how to check a bit flag:

if (fItemState & ODS_FOCUS)

Order of Messages

We’ve warned developers for a long time now not to use the order of messages or rely on a particular order of messages received by an application However, we're finding some applications have relied on this order of messages, particularly in some multithreaded applications. For example, one thread might shut down and post a message to the main message loop, and another thread would shut down, and it would also post a message. The application would rely on the order of the posted messages to be the order that those threads shut down. We've made changes to the way the thread scheduler operates. Now the messages may not be in the same order that the threads were shut down, and all kinds of bad things can happen if the applications assume that they are. If you need to rely on the order of messages, especially across threads, you need to add your own synchronization.

Multiple Monitors

Starting with Windows 98, Windows has the ability to handle multiple monitors. Windows 2000 is the NT-based platform with that capability. Applications must be able to handle negative screen coordinates and very large screen coordinates correctly. Make sure, if you're controlling the positioning of your windows, that you're testing your applications on multiple monitors to ensure that the application handles negative screen coordinates and very large screen coordinates.

Generic Thunking

Another way a lot of applications have fallen into a capability hole is in the use of thunks. Windows 9x implemented something called a flat thunk. It allowed a 16-bit application to call into 32-bit components and applications, and it also allowed a 32-bit application to call directly into a 16-bit component or application. This last capability is not supported on Windows 2000, specifically the ability for a 32-bit application to call directly into a 16-bit application. Windows 2000 has implemented generic thunking. Generic thunking will allow a 16-bit application to call into a 32-bit component, and it will also allow a 16-bit application to initiate a call into a 32-bit component, and then be called back from that 32-bit component. The ability to just have 32-bit code that calls a 16-bit component is not working and is not supported. You really only initiate your calls from 16-bit into 32-bit, not the other way around. Another thing to keep in mind about thunking is that even the underlying process model differs between Windows 9x and Windows NT. Generic thunkings will show you some differences. The easiest thing to do here is to port the 16-bit component in the 32-bit components.

Using the Win9x SMAP_LS APIs

The Win9x version of KERNEL32 facilitated porting of old DOS programs to Windows by allowing execution of 16-bit code in a 32-bit application via the SMAP_LS APIs. These APIs are not (and probably never will be) supported on the Windows NT platform due to its pure 32-bit architecture. On NT, 16-bit applications execute in the virtual DOS machine (NTVDM.EXE), which runs the DOS and/or Win3.1 kernel in a "sandbox" to retain NT's stability. Since they are completely segregated from the NT kernel, it is not possible for the same process to run 16- and 32-bit code at the same time.

Undocumented Registry Keys

Applications sometimes retrieve information about the OS and/or installed software by looking in registry keys. Most of the time, Microsoft does not guarantee that certain registry keys will be present on all platforms, and so it is dangerous to rely on registry specific information. If information about the OS is not available through an API, chances are it is not supported on all platforms.

Using Privileged Instructions for I/O or Performance Profiling

Many Intel x86 instructions cannot execute in Ring 3 (user mode) on Windows NT, including cli, sti, in, out, and any instructions that access the cr registers. Applications written for the Win9x platform sometimes use these instructions to profile the CD-ROM drive or the processor itself. These instructions cause the NT kernel to terminate the process.

Additional Resources

Application Specification for Windows 2000 Server

Application Specification for Microsoft Windows 2000 for Desktop Applications

Microsoft Windows 2000: Check Hardware and Software Compatibility

Show:
© 2014 Microsoft