Exercise 1: Understanding Version Checking

In this excerice, you will learn how to make applications that check for the minimum operating system version work with later operating system versions. The workaround succeeds by "lying" about the true operating system version installed. Next you will fix the application code to work with other OS version. Last you will change the code to check for feature availability rather than specific version OS.

Task 1 – Review and Compile the Broken Application

  1. Navigate to the folder where the VersionCheck solution is located.
  2. Open the VersionCheck solution in Visual Studio 2008.
  3. Make sure that the build configuration is Debug/x86:

  4. In Solution Explorer:
    1. Right-click the VersionCheckNativeBroken project
    2. Select Set as Startup Project
  5. Navigate to the _tWinMain function (top of the file) located in VersionCheckNativeBroken.cpp.
  6. Notice how GetVersionEx queries the operating system version .
  7. Notice how the comparison will let the application run only if the operating system version is 5.1 (Microsoft Windows XP). Otherwise, it will open a message box.
  8. Compile and run the application.
  9. Notice that this message box appears:

Task 2 – Use Compatibility Mode to Lie About Version

  1. Navigate to the folder where the application's .exe file is located. It should be located in: <solution_dir>\VersionCheckNativeBroken\Release.
  2. Right-click the VersionCheckNativeBroken.exe and select Properties.
  3. Click the Compatibility tab:

  4. Select the checkbox Run this program in compatibility mode for.
  5. From the drop-down menu, select “Windows XP (Service Pack 3).”
  6. Click OK.
  7. Run the application. You will notice that the application now launches.
  8. Follow steps 1-6, but this time, clear the compatibility mode checkbox. This removes the compatibility mode for the next task.

    Note:
    Help

    Please note that this is not the preferred solution and it is highly recommended to modify the application to correctly check for Operating System and NOT fail the application.

Task 3 – Modify the Application to Correctly Check for Minimum Operating System Version

  1. Navigate to the VersionCheckNativeBroken.cpp file.
  2. Add the following function forward declaration above _tWinMain:BOOL TestMinimumOSRequirement();

  3. Navigate to the _tWinMain function and replace the version checking code with a call to TestMinimumOSRequirement(). The end result should be:// Program entry point
    int APIENTRY _tWinMain(HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPTSTR lpCmdLine,
    int nCmdShow)
    {
    g_hInst = hInstance;

    // Instead of using GetVersion/GetVersionEx, use
    // VerifyVersionInfo to test for
    // minimum OS requirements
    if (!TestMinimumOSRequirement())
    {
    MessageBox(NULL,
    _T("Windows XP SP2 or later is required."),
    _T("Wrong OS version."), MB_OK | MB_ICONERROR);
    return 1;
    }

    // Initialize common controls (for progress bar)
    InitCommonControls();
    // Show the dialog with a dialog template resource
    IDD_VERSIONCHECKNATIVE_DIALOG
    DialogBox(hInstance,
    MAKEINTRESOURCE(IDD_VERSIONCHECKNATIVE_DIALOG), 0,
    DlgMainProc);
    return 0;
    }

  4. Add the TestMinimumOSRequirement function with the following code:

    // Check minimum OS requirement
    // This function returns TRUE if OS is XP SP2 or later // (for example Windows Vista, Windows 7)
    BOOL TestMinimumOSRequirement()
    {
    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; // Windows XP
    osvi.wServicePackMajor = 2; // Service Pack 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 );
    VER_SET_CONDITION( dwlConditionMask, VER_SERVICEPACKMINOR, op );

    // Perform the test.
    return VerifyVersionInfo(
    &osvi,
    VER_MAJORVERSION | VER_MINORVERSION |
    VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR,
    dwlConditionMask);
    }

  5. Compile and run the application. You will notice that the application runs correctly on Windows 7.

Task 4 – Check for a Feature Instead of Checking for Version

In this task, you will learn how to check for the presence of a Windows feature instead of relying on the operating system version to check for a feature's presence. This allows your application to run and use the feature even if the feature has been added to an earlier operating system. Microsoft plans to add two Windows 7 features to Windows Vista in the future: Direct2D/DirectWrite and Ribbon.

  1. Navigate to the CTimer::Set function in Timer.cpp.
  2. Notice that the CTimer::Set function currently calls SetWaitableTimer, which has been available since at least Windows 2000.
  3. Uncomment the commented lines of code in this function.
  4. Uncomment the commented lines of code at the beginning of the file.

    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
    );

  5. Notice that we used GetModuleHandle() to obtain the kernel32.dll handle. Kernel32.dll is always loaded into the process. If you are using a module that may not be loaded, use LoadLibrary() to obtain the module handle.
  6. Notice that we used GetProcAddress() to obtain the function address and then cast the return value to the function pointer (SetWaitableTimerExProc).
  7. If GetProcAddress() returns NULL, it means that the SetWaitableTimerEx function could not be found in kernel32.dll; we must use the SetWaitableTimer function instead. This ensures that the application can still function (with reduced features) on a Windows version which does not contain this feature.
  8. Compile and run the application.
  9. Notice that the application prints whether it is using the new coalescing timer API (SetWaitableTimerEx) or not:

    Note:
    Watch Out

    Mixing logic into an application’s presentation layer, such as we’re showing here, is a poor software engineering practice. It’s difficult to test, mixes concerns, and breaks a number of other solid design principles.

    We’re breaking this principle and showing our logic mixed in the window message loop in order to keep our examples concise and clear. In real applications, separate out this logic into separate classes, preferably by using a Model View Presenter (MVP) or similar approach. We encourage you to explore MVP and its brethren as design principles that can help you write more flexible, maintainable, and testable applications.