Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Application Manifest Settings and OS Versioning

System Information

Application Manifest Settings and OS Versioning

by  Glenn9999  Posted    (Edited  )
Reference: faq102-3232
(Edited 11/19/2015 - evidently Microsoft pushed through the application compatibility manifest change in Windows 10 [link http://www.tek-tips.com/viewthread.cfm?qid=1750734]as indicated in the thread introducing this FAQ.[/link] As I wrote there: The "problem" I think that's coming from this is that Microsoft has designed to make versioning irrelevant in their OSes. At least from an API standpoint. The major thing that seems to indicate version is the application compatibility manifest (as noted, GetVersionEx will return the correct answer if 8.1 compatibility is indicated there).

The Application Compatibility Manifest
One thing that people will find of the newer Microsoft operating systems is that Microsoft has left the "versioning" of the OS to the application at hand as opposed to the operating system itself. More or less that means the programmer decides the "version" of the program through the application compatibility manifest. It probably was preferable anyway, because the programmer can dictate what OS the program *should* run on through his own testing and then Microsoft can apply appropriate settings to the program on the fly when it's run.

Those who have run into the UAC requirement will recognize an application manifest. I'll post an example below and then explain it:
Code:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <assemblyIdentity type="win32" name="App" version="3.1.0.0" processorArchitecture="*"/>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" publicKeyToken="6595b64144ccf1df" language="*" processorArchitecture="*"/>
    </dependentAssembly>
  </dependency>
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
    <security>
      <requestedPrivileges>
        <requestedExecutionLevel level="asInvoker" uiAccess="false"/>
        </requestedPrivileges>
    </security>
  </trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
    <application>
      <!--The ID below indicates application support for Windows Vista -->
      <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
      <!--The ID below indicates application support for Windows 7 -->
      <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
      <!--The ID below indicates application support for Windows 8 -->
      <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/> 
      <!--The ID below indicates application support for Windows 8.1 -->
      <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/> 
      <!--The ID below indicates application support for Windows 10 --> 
      <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
    </application>
  </compatibility></assembly>

I'm sure there is more to specify here than what I'll go through, but I'll be sure to go through the major highlights of the XML file.

1. The first notable part is the name="Microsoft.Windows.Common-Controls" version="6.0.0.0" line. This enables themed controls in your program, which will make it consistent with what the user sets as a theme in the OS and will make your program look more modern. As well, you will be able to create procedures within the final Delphi unit that will force your controls (forms, edit boxes, labels) to OS specific fonts. Notably, for those of you who have that in your Delphi, this is what is done within TXPManifest (it actually is an empty component that only serves to include a manifest with this alone).

2. The next notable line of concern is the <requestedExecutionLevel level="asInvoker" uiAccess="false"/> line. This is the UAC setting line. As indicated, [link https://msdn.microsoft.com/en-us/library/bb384691.aspx]here are the settings[/link]:
asInvoker: The application will run with the same permissions as the process that started it. The application can be elevated to a higher permission level by selecting Run as Administrator.

highestAvailable: The application will run with the highest permission level that it can. If the user who starts the application is a member of the Administrators group, this option is the same as requireAdministrator. If the highest available permission level is higher than the level of the opening process, the system will prompt for credentials.

requireAdministrator: The application will run with administrator permissions. The user who starts the application must be a member of the Administrators group. If the opening process is not running with administrative permissions, the system will prompt for credentials.

Most programs should be fine with this set to "asInvoker", but if you find that your program doesn't work unless you use "Run as Administrator", you will want to set this option within the manifest. This will make it so it will always prompt and elevate to administrator rights.

3. The last line is of concern <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">. The section below this indicates application compatibility. This was instituted as of Windows Vista, so all the lines that should be of interest as of this writing are present, each specifically marked. While good to include as default in most cases, the idea here is to only include the OSes upon which the application has been tested. For example, if I test my program on Windows 7, it is usually best to have that as the maximum line. If it is run on Windows 8.1 or Windows 10, then those OSes should default the program to Windows 7 compatibility. Consequently, it should return version numbers indicating so - the maximum of 6.2 (logically) unless the 8.1 or 10 compatibility line is present.

Note: If you get any of the editing wrong, running the program will produce an error about "side-by-side configuration". So if you see that mentioned, start looking at your manifest for the cause (it is case-sensitive).

The Application Compatibility Manifest - Putting It In Your Program
Now that the manifest is prepared, the question of including it into your program is pertinent. This is done by including it as a resource. To do that, we need a resource file.

Code:
1 24 Win8Theme.manifest

My manifest is saved as "Win8Theme.manifest" in the project directory. I save the above as Win8Theme.RC. (This was my first thing I did under Windows 8.1 if you're wondering about the naming) Then to build it, as I do all projects that require resources, with a BAT file. Other Delphis may enable inclusion of the RC file as part of the project so this will be unnecessary in that case. My main interest with the BAT file is that I can change the manifest easily and test it with the new settings I use.

Code:
"C:\Delphi3\Delphi 3\BIN\BRCC32.EXE" Win8Theme.RC
"C:\Delphi3\Delphi 3\BIN\DCC32.EXE" Win8Theme.pas
"C:\Delphi3\Delphi 3\BIN\DCC32.EXE" Project1.dpr
pause

Now to include the manifest in a source file. This is my Win8Theme.pas:

Code:
{$D- $L-}
unit Win8Theme;

interface
  uses commctrl, forms, sysutils;

implementation

// this line will include the manifest resource
{$R Win8Theme.res}

initialization
  // this call is necessary for the newer "theme" controls.  Note YMMV on Common Controls 6.0 
  // ("theme controls") and you may require other corrective changes in your programs for 
  // those to work
  InitCommonControls;
end.

Now if that compiles successfully, all you need to do in your program to get the manifest into it is include Win8Theme as a unit and provide the proper DCU and RES file.

Version Helper APIs
As the API changes in Windows, they update other things and deprecate others. This happens to be the case with GetVersionEx and anything associated with it (most Delphi internal version variables?).

Anyway, one will discover this deprecation in GetVersionEx easily once you notice that it [link https://msdn.microsoft.com/en-us/library/windows/desktop/dn302074(v=vs.85).aspx]fails to properly bring back the right version in Windows 8.1[/link] - returning 6.2 instead of 6.3. Note for the moment that using application compatibility settings in the manifest will produce the correct answer, as the link notes. But the link points you towards [link https://msdn.microsoft.com/en-us/library/windows/desktop/dn424972%28v=vs.85%29.aspx]the version helper apis[/link]. While seemingly straight forward, they are really macros you will find when you open up the H file. That said, my rough translation is below. Using this will enable you to get a reliable OS version for testing purposes. The short boolean functions should demonstrate how to properly call the main functions of concern.

Code:
unit newversiontests;
  // compiled / written by Glenn9999 on tek-tips.com on 06/06/2015
interface
  uses windows, sysutils;
   const
     VER_SERVICEPACKMAJOR = $0000010;
     VER_MAJORVERSION     = $0000002;
     VER_MINORVERSION     = $0000001;
     VER_NT_WORKSTATION   = 1;
     VER_PRODUCT_TYPE = $80;

     VER_EQUAL                       = 1;
     VER_GREATER                     = 2;
     VER_GREATER_EQUAL               = 3;
     VER_LESS                        = 4;
     VER_LESS_EQUAL                  = 5;
     VER_AND                         = 6;
     VER_OR                          = 7;
     WIN32_WINNT_NT4 = $0400;
     WIN32_WINNT_WIN2K = $0500;
     WIN32_WINNT_WINXP = $0501;
     WIN32_WINNT_WS03 = $0502;
     WIN32_WINNT_VISTA = $0600;
     WIN32_WINNT_WS08 = $0600;
     WIN32_WINNT_LONGHORN = $0600;
     WIN32_WINNT_WIN7 = $0601;
     WIN32_WINNT_WIN8 = $0602;
     WIN32_WINNT_WINBLUE = $0603;
     WIN32_WINNT_WIN10 = $0A00;

     kernel32 = 'kernel32.dll';
   type
     OSVERSIONINFOEX = record
       dwOSVersionInfoSize: DWord;
       dwMajorVersion: DWord;
       dwMinorVersion: DWord;
       dwBuildNumber: DWord;
       dwPlatformID: DWord;
       szCSDVersion: array[1..128] of char;
       wServicePackMajor: Word;
       wServicePackMinor: Word;
       wSuiteMask: Word;
       wProductType: Byte;
       wReserved: Byte;
     end;

   function IsWindowsXPOrGreater: Bool; stdcall;
   function IsWindowsXPSP1OrGreater: Bool; stdcall;
   function IsWindowsXPSP2OrGreater: Bool; stdcall;
   function IsWindowsXPSP3OrGreater: Bool; stdcall;
   function IsWindowsVistaOrGreater: Bool; stdcall;
   function IsWindowsVistaSP1OrGreater: Bool; stdcall;
   function IsWindowsVistaSP2OrGreater: Bool; stdcall;
   function IsWindows7OrGreater: Bool; stdcall;
   function IsWindows7SP1OrGreater: Bool; stdcall;
   function IsWindows8OrGreater: Bool; stdcall;
   function IsWindows8Point1OrGreater: Bool; stdcall;
   function IsWindows10OrGreater: Bool; stdcall;
   function IsWindowsServer: Bool; stdcall;

   function IsWindowsVersionOrGreater(wMajorVersion, wMinorVersion,
            wServicePackMajor: Word): Bool; stdcall;

implementation

function VerifyVersionInfo(var LPOSVERSIONINFOEX : OSVERSIONINFOEX;
           dwTypeMask: DWORD;dwlConditionMask: int64): BOOL; stdcall;
           external kernel32 name 'VerifyVersionInfoA';

function VerSetConditionMask(dwlConditionMask: int64;dwTypeBitMask: DWORD;
           dwConditionMask: Byte): int64; stdcall; external kernel32;

function IsWindowsVersionOrGreater;
  var
    osvi: OSVersionInfoEX;
    condmask: Int64;
  begin
    FillChar(osvi, sizeof(osvi), 0);
    osvi.dwOSVersionInfoSize := SizeOf(osvi);
    FillChar(condmask, 8, 0);
    condmask := VerSetConditionMask(condmask, VER_MAJORVERSION, VER_GREATER_EQUAL);
    condmask := VerSetConditionMask(condmask, VER_MINORVERSION, VER_GREATER_EQUAL);
    condmask := VerSetConditionMask(condmask, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
    osvi.dwMajorVersion := wMajorVersion;
   	osvi.dwMinorVersion := wMinorVersion;
  	osvi.wServicePackMajor := wServicePackMajor;
    Result := VerifyVersionInfo(osvi, VER_MAJORVERSION or VER_MINORVERSION or VER_SERVICEPACKMAJOR,
            condmask) <> false;
  end;

function IsWindowsXPOrGreater;
  begin
    Result := IsWindowsVersionOrGreater(HiByte(WIN32_WINNT_WINXP), LOBYTE(WIN32_WINNT_WINXP),0);
  end;

function IsWindowsXPSP1OrGreater;
  begin
    Result := IsWindowsVersionOrGreater(HiByte(WIN32_WINNT_WINXP), LOBYTE(WIN32_WINNT_WINXP),1);
  end;

function IsWindowsXPSP2OrGreater;
  begin
      Result := IsWindowsVersionOrGreater(HiByte(WIN32_WINNT_WINXP), LOBYTE(WIN32_WINNT_WINXP),2);
  end;

function IsWindowsXPSP3OrGreater;
  begin
    Result := IsWindowsVersionOrGreater(HiByte(WIN32_WINNT_WINXP), LOBYTE(WIN32_WINNT_WINXP),3);
  end;

function IsWindowsVistaOrGreater;
  begin
    Result := IsWindowsVersionOrGreater(HIBYTE(WIN32_WINNT_VISTA), LOBYTE(WIN32_WINNT_VISTA), 0);
  end;

function IsWindowsVistaSP1OrGreater;
  begin
    Result := IsWindowsVersionOrGreater(HIBYTE(WIN32_WINNT_VISTA), LOBYTE(WIN32_WINNT_VISTA), 1);
  end;

function IsWindowsVistaSP2OrGreater;
  begin
    Result := IsWindowsVersionOrGreater(HIBYTE(WIN32_WINNT_VISTA), LOBYTE(WIN32_WINNT_VISTA), 2);
  end;

function IsWindows7OrGreater;
  begin
    Result := IsWindowsVersionOrGreater(HIBYTE(WIN32_WINNT_WIN7), LOBYTE(WIN32_WINNT_WIN7), 0);
  end;

function IsWindows7SP1OrGreater;
  begin
    Result := IsWindowsVersionOrGreater(HIBYTE(WIN32_WINNT_WIN7), LOBYTE(WIN32_WINNT_WIN7), 0);
  end;

function IsWindows8OrGreater;
  begin
    Result := IsWindowsVersionOrGreater(HIBYTE(WIN32_WINNT_WIN8), LOBYTE(WIN32_WINNT_WIN8), 0);
  end;

function IsWindows8Point1OrGreater;
  begin
    Result := IsWindowsVersionOrGreater(HIBYTE(WIN32_WINNT_WINBLUE), LOBYTE(WIN32_WINNT_WINBLUE), 0);
  end;

function IsWindows10OrGreater;
  begin
    Result := IsWindowsVersionOrGreater(HIBYTE(WIN32_WINNT_WIN10), LOBYTE(WIN32_WINNT_WIN10), 0);
  end;

function IsWindowsServer;
  var
    osvi: OSVersionInfoEX;
    condmask: Int64;
  begin
    FillChar(osvi, sizeof(osvi), 0);
    osvi.dwOSVersionInfoSize := SizeOf(osvi);
    FillChar(condmask, 8, 0);
    condmask := VerSetConditionMask(condmask, VER_PRODUCT_TYPE, VER_EQUAL );
    osvi.wProductType := VER_NT_WORKSTATION;
    Result := not VerifyVersionInfo(osvi, VER_PRODUCT_TYPE, condmask) <> false;
  end;

end.
Register to rate this FAQ  : BAD 1 2 3 4 5 6 7 8 9 10 GOOD
Please Note: 1 is Bad, 10 is Good :-)

Part and Inventory Search

Back
Top