Better Version Checking - Windows 7 Training Kit for Developers

Version Checking – Whitepaper
Version Checking
Lab version:
1.0.0
Last updated:
May 12, 2009
Version Checking – Whitepaper
CONTENTS
INTRODUCTION........................................................................................................................................... 3
WORKING AROUND THE PROBLEM ........................................................................................................ 3
Compatibility Mode .................................................................................................................................. 4
Application Compatibility Toolkit.............................................................................................................. 5
SOLUTIONS ................................................................................................................................................. 7
Better Version Checking............................................................................................................................ 7
Checking for Features ............................................................................................................................... 9
List of Tools ............................................................................................................................................. 11
Additional Resources .............................................................................................................................. 11
Version Checking – Whitepaper
Introduction
The most common application compatibility issue that users as well as developers face is when an
application fails upon checking the operating system version. A lot can go wrong when version checking
is misused. A user might experience a “silent fail” where the application simply fails to load and nothing
happens. Or, a user might see a dialog box indicating something to the effect of “you must be running
Microsoft Windows® XP or later” when in fact, the computer is running Windows 7. Many other
consequences to poor version checking can inconvenience users as well.
Applications fail due to version checking for two reasons:

A flaw (bug) in the version checking code, which fails if the minor version is decreased, even if
the major version is increased (for example, changing versions from 5.1 to 6.0) or if the
expected service pack (SP) is not installed, even if you're running a newer operating system (for
example, changing versions from Windows XP SP 2 to Windows Vista® SP 1)

An intentional blocking that prevents the application from running on operating system versions
not tested by its developers (we recommend that you do not block applications from running on
future operating systems)
When an application runs on an "incompatible" (due to poor version checking) version of Windows, it
will generally display an error message, but it may also exit silently or behave erratically. Often, if we
work around the version checking, the application will run well. End-users and IT professionals may
apply a fix to let the application think it is running on an older version of Windows.
The following sections provide information about how to work around version checking compatibility
issues, and how to check the operating system version. We also suggest the best overall approach:
checking for operating system features instead of checking for the operating system version.
Working Around the Problem
Windows provides two mechanisms to work around the version checking problem:

Compatibility mode: Designed for end users, compatibility mode is an easy way to work around
compatibility issues. When enabled, it applies a set of compatibility fixes that provide a runtime
environment more compatible with applications written for older versions of Windows. One of
those fixes is the "version lie," which makes the version query functions return the operating
system version the user chose in the Compatibility tab of the Properties dialog box instead of
the actual Windows version.

Application Compatibility Toolkit (ACT): A set of application compatibility tools for IT
professionals and developers, ACT provides the means to detect and debug a wide array of
Version Checking – Whitepaper
compatibility issues so that developers can fix them, and provides the means to work around
compatibility issues by applying compatibility fixes from a list, including the “version lie” fix. ACT
provides finer-grained control over what fixes are applied.
Compatibility Mode
To enable compatibility mode:
1.
2.
3.
4.
Right-click the executable or shortcut to the executable.
Click Properties.
Click the Compatibility tab.
Enable Run this program in compatibility mode for: and select the operating system version
you think the application should be able to run on.
Some applications consist of several executables. You may need to apply this fix to each one.
5. Click OK to close the dialog box.
6. Run the application.
Note: Note: Compatibility mode does not normally affect version checking performed by
managed (.NET Framework) applications via Environment.OSVersion or by calling Win32
functions (for example, GetVersionEx) via P/Invoke. However, this fix might still help if the
application is a mix of native and managed code. If the application performs the version
checking in a native call, then this fix will apply.
Version Checking – Whitepaper
Application Compatibility Toolkit
After installing ACT, run the Compatibility Administrator from the Start menu:
1. Look at the tree view on the left. If there is no "Custom Databases" node, click New on the
toolbar.
2. Right-click the New Database
3. Click Rename and give a name to the compatibility database.
4. Right-click the database you have just renamed, point to Create New and click Application Fix.
The Create new Application Fix dialog box appears:
5. Fill in the details.
6. Click Next.
Version Checking – Whitepaper
7. Under Operating System Modes, choose None.
8. From Select additional compatibility modes:, select WinXPSP2VersionLie.
9. Click Next.
10. If your application requires additional fixes, select them here.
11. Click Next.
Version Checking – Whitepaper
12. Select the conditions by which Windows should identify the executable.
13. Click Finish.
14. Click the Save toolbar button to save the compatibility database to a file (with an .sdb
extension).
You can install this file on target computers using the Windows SDBinst tool.
15. Windows comes with a compatibility database that contains fixes for many programs; you may
look at these by expanding the System Database/Applications node. Clicking on sub-nodes will
reveal the fixes applied to the application.
Solutions
Better Version Checking
Identifying the current operating system is not the best way to determine whether a particular
operating system feature is present. However, if you can’t design your application to check for specific
feature availability and the only way to ensure compatibility is through version checking, then please
consider the following.
For native applications, you will need to ensure your application's logic will work with newer versions of
Windows. Please DO NOT BLOCK on version change! The following is a Win32 code example that uses
GetVersionEx. If the major version is greater than 5 (Windows Vista, Windows® Server 2008 R2 and
Windows 7), the check passes. If it equals 5, then the minor version should be 1 or greater (Windows XP
or Windows Server 2003).
C++
#include <windows.h>
Version Checking – Whitepaper
#include <stdio.h>
void main()
{
OSVERSIONINFO osvi;
BOOL bIsWindowsXPorLater;
ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&osvi);
bIsWindowsXPorLater =
( (osvi.dwMajorVersion > 5) ||
( (osvi.dwMajorVersion == 5) && (osvi.dwMinorVersion >= 1) ));
if(bIsWindowsXPorLater)
printf("The system meets the requirements.\n");
else printf("The system does not meet the requirements.\n");
}
The following code example uses VerifyVersionInfo to check the operating system version against
minimal requirements (Windows XP SP2):
C++
#include <windows.h>
BOOL Is_WinXP_SP2_or_Later ()
{
OSVERSIONINFOEX osvi;
DWORDLONG dwlConditionMask = 0;
int op=VER_GREATER_EQUAL;
// Initialize the OSVERSIONINFOEX structure.
ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
osvi.dwMajorVersion = 5;
osvi.dwMinorVersion = 1;
osvi.wServicePackMajor = 2;
osvi.wServicePackMinor = 0;
// Initialize the condition mask.
VER_SET_CONDITION( dwlConditionMask, VER_MAJORVERSION, op );
VER_SET_CONDITION( dwlConditionMask, VER_MINORVERSION, op );
VER_SET_CONDITION( dwlConditionMask, VER_SERVICEPACKMAJOR, op );
Version Checking – Whitepaper
VER_SET_CONDITION( dwlConditionMask, VER_SERVICEPACKMINOR, op );
// Perform the test.
return VerifyVersionInfo(
&osvi,
VER_MAJORVERSION | VER_MINORVERSION |
VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
dwlConditionMask);
}
For .NET Framework developers, use the ==, !=, <=, <, >, >= operators of the Version object returned by
Environment.OSVersion.Version:
C#
// This code checks if the OS is at least Windows XP
if (Environment.OSVersion.Version < new Version(5, 1))
{
MessageBox.Show("Windows XP or later required.",
"Incompatible Operating System", MessageBoxButtons.OK,
MessageBoxIcon.Error);
return;
}
Visual Basic
' This code checks if the OS is at least Windows XP
If Environment.OSVersion.Version <> New Version(5, 1) Then
Dim style As MsgBoxStyle = MsgBoxStyle.OkOnly Or MsgBoxStyle.Critical
MsgBox("Windows XP required.", style, "Incompatible Operating System")
Return
End If
Checking for Features
As mentioned previously, checking the operating system version is not the best way to confirm that a
specific operating system feature is available. This is because the operating system may have had new
features added in a redistributable DLL. Rather than using GetVersionEx to determine the operating
system platform or version number, it is more effective to test for the presence of the feature itself. For
example, we plan to make the Direct2D and DirectWrite APIs and the Ribbon API available in
Windows Vista, so there is no need to block your application from using these APIs.
If possible, your application should still run if the feature is unavailable, though with reduced
functionality or performance.
For Win32, use the following technique:
Version Checking – Whitepaper

Use LoadLibrary() to load a library which is not yet loaded into your application. If you are
interested in a new function of a DLL which is already loaded (for example, kernel32.dll), then
call GetModuleHandle() to obtain the module handle. If either of these functions return NULL,
then this indicates an error.

Use GetProcAddress() to obtain a function pointer. If GetProcAddress() returns NULL, then the
function may not exist. Cast the pointer to a function pointer of an appropriate prototype.
Some functions, although they exist may actually be stubs that return a "not implemented"
error. Be such to check for such errors.
The following example demonstrates this technique:
C++
// define function pointer type
typedef BOOL (WINAPI *SetWaitableTimerExProc)(
__in HANDLE hTimer,
__in const LARGE_INTEGER *lpDueTime,
__in LONG lPeriod,
__in PTIMERAPCROUTINE pfnCompletionRoutine,
__in LPVOID lpArgToCompletionRoutine,
__in PREASON_CONTEXT WakeContext,
__in ULONG TolerableDelay
);
LARGE_INTEGER liDueTime;
liDueTime.QuadPart = 0;
nt period = 1000;
unsigned int tolerance = 1000;
HANDLE hTimer = // Get timer handle
REASON_CONTEXT reasonContext = {0};
reasonContext.Version = 0;
reasonContext.Flags = POWER_REQUEST_CONTEXT_SIMPLE_STRING;
reasonContext.Reason.SimpleReasonString = L"MyTimer";
// Get module handle to a module which is already loaded
HMODULE hKernel32Module = GetModuleHandle(_T("kernel32.dll"));
if (hKernel32Module == NULL)
return FALSE;
// Get Address of function
SetWaitableTimerExProc pFnSetWaitableTimerEx =
(SetWaitableTimerExProc) ::GetProcAddress(hKernel32Module,
"SetWaitableTimerEx");
// Check if the function exists
if (pFnSetWaitableTimerEx == NULL)
Version Checking – Whitepaper
return FALSE;
// Call function
if (!pFnSetWaitableTimerEx(hTimer, &liDueTime, period, NULL, NULL,
&reasonContext, tolerance)
{ // handle error }
Alternatively, you may use DLL delayed loading and call functions in a __try...__except block. (For more
information, see Linker Support for Delay-Loaded DLLs.)
For COM APIs, handle errors returned by CoCreateInstance and QueryInterface.
.NET framework applications that call Win32 APIs via P/Invoke should handle
EntryPointNotFoundException and DllNotFoundException exceptions.
List of Tools
Microsoft Application Compatibility Toolkit
Additional Resources

Application Compatibility Cookbook: http://msdn.microsoft.com/en-us/library/bb963893.aspx

Version Lie and Managed Applications:
http://blogs.msdn.com/cjacks/archive/2007/09/10/version-lie-shims-and-managed-code-onwindows-vista.aspx