Detecting Windows 11 properly - c++

I'm a developper from the FOSS game 0 A.D. (https://play0ad.com) and I recently realised that our detection of the Windows version was failing for Windows 11, just like it did between Windows 8.1 and Windows 10.
It reports Windows 10.0.22000 which is technically the correct version, but we'd like it to report Windows 11 instead.
Here is the current code we use
https://github.com/0ad/0ad/blob/master/source/lib/sysdep/os/win/wposix/wutsname.cpp#L35
https://github.com/0ad/0ad/blob/master/source/lib/sysdep/os/win/wversion.cpp
https://github.com/0ad/0ad/blob/master/source/lib/sysdep/os/win/wversion.h
We're stuck with the Windows 7 SDK for compatibility reasons.
Easy solution would be to replace
if (osInfo.dwMajorVersion >= 10)
{
stream << "Win" << osInfo.dwMajorVersion;
}
by
if (osInfo.dwMajorVersion >= 10)
{
if (osInfo.dwMinorVersion > 22000)
stream << "Win" << 11;
else
stream << "Win" << osInfo.dwMajorVersion;
}
Is there a more robust/future proof solution.
Thanks in advance!

I'm not sure if this will work, but can you give this code a try? It uses the sysinfoapi.h file. According to the docs, it only works on Windows though.
#include <iostream>
#include <sysinfoapi.h>
void print_os_info()
{
OSVERSIONINFOEX info;
ZeroMemory(&info, sizeof(OSVERSIONINFOEX));
info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
GetVersionEx((LPOSVERSIONINFO)&info);
printf("Windows version: %u.%u\n", info.dwMajorVersion, info.dwMinorVersion);
}
int main()
{
print_os_info();
}
EDIT: Apparently applications not manifested for Windows 8.1 or Windows 10 will return the Windows 8 OS version value (6.2) according the the sysinfoapi.h docs\
EDIT AGAIN: I tried using a $(Filename).exe.manifest file with the contents
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<assemblyIdentity
type="win32"
name="Contoso.ExampleApplication.ExampleBinary"
version="1.2.3.4"
processorArchitecture="x86"
/>
<description>Contoso Example Application</description>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 and Windows 11 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<!-- Windows Vista -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
</application>
</compatibility>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<!--
UAC settings:
- app should run at same integrity level as calling process
- app does not need to manipulate windows belonging to
higher-integrity-level processes
-->
<requestedExecutionLevel
level="asInvoker"
uiAccess="false"
/>
</requestedPrivileges>
</security>
</trustInfo>
</assembly>
Which for me worked and made it print out Windows 10.0 instead of 6.2
Sources: Manifest File, sysinfoapi.h

Except the very weak, atleast for me, solution of considering Windows 10 builds greater than 22000, such as Windows 11, the only solution I found which is actually working is the WMIs Win32_OperatingSystem class - Caption property.
On my dev Windows 10 machine it gives the following string: Microsoft Windows 10 Pro.
On my other dev machine, with Windows 11 installed, the same function gives: Microsoft Windows 11 Pro.
The difference is in the string values - "10" vs "11" - but this is at least something far better than the "build greater than" solution.
C# and C++ work well.

I am able to retrieve the version number properly.
Copying my code below. Using windows.h should work instead of sysinfoapi.h
#include<windows.h>
#include<stdio.h>
int main() {
OSVERSIONINFOEX info;
ZeroMemory(&info, sizeof(OSVERSIONINFOEX));
info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
GetVersionEx((LPOSVERSIONINFO)&info);
printf("Windows version: %u.%u\n", info.dwMajorVersion, info.dwMinorVersion);
}
Alternate way to read version number is to use GetProcAddress. 'GetProcAddress' returns the address of specified DLL function.
#include<iostream>
#include<string>
#include <windows.h>
using namespace std;
void main() {
int osver= 0.0;
NTSTATUS(WINAPI *RtlGetVersion)(LPOSVERSIONINFOEXW);
OSVERSIONINFOEXW osInfo;
*(FARPROC*)&RtlGetVersion = GetProcAddress(GetModuleHandleA("ntdll"), "RtlGetVersion");
if (NULL != RtlGetVersion) {
osInfo.dwOSVersionInfoSize = sizeof(osInfo);
RtlGetVersion(&osInfo);
osver = osInfo.dwMajorVersion;
}
}

Related

MSVC OSVersion returning incorrect version

Running Windows 10 on my development machine. Trying to get the OS version to return a major version of 10. Tried this and also tried environment variables via System::Environment::OSVersion->VersionString. Both are returning Microsoft Windows NT 6.2.9200. Tested executable on a Windows 7 machine and I get 6.1.* (correct).
Running this in Powershell I get the correct result:
PS Z:\> [System.Environment]::OSVersion.Version
Major Minor Build Revision
----- ----- ----- --------
10 0 14393 0
I wrote a test C++ program in Visual Studio using the same Environment variables logic ...
#include <Windows.h>
#include <iostream>
using namespace std;
int main(int argc, char **argv, char** envp) {
printf("Current compiler version is ... %d\n", _MSC_VER);
printf("Current compiler full version is ... %d\n", _MSC_FULL_VER);
printf("Current OSVersion is ... %s\n\n", System::Environment::OSVersion->VersionString);
char** env;
for (env = envp; *env != 0; env++)
{
char* thisEnv = *env;
printf("%s\n", thisEnv);
}
cin.ignore();
cin.ignore();
return 0;
}
Results it (I won't print all environment variables except one)...
Current compiler version is ... 1900
Current compiler full version is ... 190024215
Current OSVersion is ... Microsoft Windows NT 6.2.9200.0
...
OS=Windows_NT
The above test program is configured with the Target Platform as 10.0.14393.0.
Why does it think my OS is Windows 8?
Specs:
Windows 10
Version 10.0.14393 Build 14393
Visual Studio 2015 (C++ project)
Target Platform 10.0.14393.0
Powershell version 5.1
This is by design as the GetVersionEx and VerifyVersionInfo functions have a 'version lie' enabled by default on Windows 8.1 or later for all Win32 desktop applications.
You control the application of the 'version lie' by adding an embedded manifest to your EXE that includes the proper compatibility GUIDs:
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows Vista -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
</application>
</compatibility>
</assembly>
See MSDN and Manifest Madness

Get the Windows version correctly from a dll function call?

Suppose I'm writing a multi-purpose dll which includes a function for getting the OS version:
void get_os_version(DWORD *major, DWORD *minor)
{
OSVERSIONINFOEX osvi;
ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
osvi.dwOsVersionInfoSize = sizeof(OSVERSIONINFOEX);
// deprecated but easier to use for this example's sake
GetVersionEx((OSVERSIONINFO*)&osvi);
*major = osvi.dwMajorVersion;
*minor = osvi.dwMinorVersion;
}
For the Windows version to be retrieved correctly for versions higher than Windows 8, it is required to embed a manifest that specifies the supported platforms (see details here).
So I disable automatic generation of a manifest for my dll file using the /MANIFEST:NO flag when compiling, and instead add the following manifest:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="asInvoker" uiAccess="false"></requestedExecutionLevel>
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<!-- Windows Vista -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
</application>
</compatibility>
</assembly>
, using the mt tool:
mt -manifest GetOsVersion.dll.manifest -outputresource:GetOsVersion.dll;#2
All good and no errors. Now to use the dll, I create a simple App.exe which loads the dll and calls its function:
int _tmain(int argc, _TCHAR* argv[])
{
DWORD major, minor;
get_os_version(&major, &minor);
printf("%d.%d\n", major, minor);
return 0;
}
But when running App.exe on Windows 10, surprise surprise, the output is:
6.2
, which is the version for Windows 8. If I apply the manifest to the App.exe too:
mt -manifest GetOsVersion.dll.manifest -outputresource:App.exe;#1
, the output is the expected one:
10.0
Why is this happening? And can I solve this problem without adding a manifest to the executable?
I don't have control over the applications that will use my library, but I still want to correctly retrieve the OS version.
An alternative method for determining the actual operating system version is documented on the MSDN page "Getting the System Version":
To obtain the full version number for the operating system, call the GetFileVersionInfo function on one of the system DLLs, such as Kernel32.dll, then call VerQueryValue to obtain the \\StringFileInfo\\<lang><codepage>\\ProductVersion subblock of the file version information.
This will work from a DLL regardless of whether the application has a manifest.
(Of course, there is a reason that GetVersionInfo and friends don't return the actual operating system version: programmers have a nasty tendency to misuse this information. You should seriously consider whether or not providing such a function in your DLL is really a good idea.)
Complementing the accepted answer, here is some starter code for anyone else who wants to implement it:
#pragma comment(lib, "version")
static void print_version()
{
DWORD buffer_size = GetFileVersionInfoSize(_T("kernel32.dll"), NULL);
if (buffer_size == 0)
{
// get error from GetLastError()
return;
}
VOID *buffer = malloc(buffer_size);
if (buffer == NULL)
{
// out of memory
return;
}
if (!GetFileVersionInfo(_T("kernel32.dll"), 0, buffer_size, buffer))
{
goto error;
}
VS_FIXEDFILEINFO *version = NULL;
UINT version_len = 0;
if (!VerQueryValue(buffer,
_T("\\"),
(LPVOID*)&version,
&version_len))
{
goto error;
}
_tprintf(_T("Version is: %u.%u\n"),
HIWORD(version->dwProductVersionMS),
LOWORD(version->dwProductVersionMS));
error:
free(buffer);
}
And the output on Windows 10 is:
Version is 10.0
App (executable) manifest MSDN page describes this matter rather explicitly:
The compatibility section of the app (executable) manifest introduced in Windows helps the operating system determine the versions of Windows an app was designed to target.
Compatibility (and other application settings, such as DPI awareness) are completely ignored if manifest is not an executable manifest. This makes sense because otherwise there would be possible manifest conflicts occurring between manifests in different dlls.
Most of the nodes in a manifest applies to the whole process and is only read from the main .exe module:
The compatibility section of the app (executable) manifest introduced in Windows helps the operating system determine the versions of Windows an app was designed to target.
You should use GetProcAddress and CoCreateInstance to check if the feature you need is present, not the Windows version.
With a little work, GetProcAddress can also be used to figure out what version you are on if you really need that information. Look at the minimum OS version of various kernel32 and user32 functions on MSDN...

Error Side by side

First of all, sorry for the length of this message and for my poor English.
I have a application programmed with Visual C++ 2010 and Windows XP application. If I run the application on the same computer where I have installed the Visual Studio, it works without problems but if I run it from another computer with Windows 7 I get an error.
And the information on the display of events is:
And if I run the application on a computer with Windows XP the error is:
And the event display is:
I have checked the versions that I have installed on three computers msvcr100.dll file. On the computer with windows xp and Visual Studio installed are:
On the computer with Windows XP without Visual Studio are:
And on the computer with Windows 7 are:
You can see that the three computers have the same version of the file (10.0.40219). I have checked the dependencies with "Dependecy Walker" program but has not found anything. Also I uninstalled and installed the Visual Studio 2010 x86 Redistributable but I still have the error.
Can I get help finding a solution?
Thank you very much.
UPDATE:
I have run the command sxstrace with the following result:
=================
Begin Activation Context Generation.
Input Parameter:
Flags = 0
ProcessorArchitecture = x86
CultureFallBacks = en-US;en
ManifestPath = C:\KMQQ760\KMQQ.exe
AssemblyDirectory = C:\KMQQ760\
Application Config File =
INFO: Parsing Manifest File C:\KMQQ760\KMQQ.exe.
INFO: Manifest Definition Identity is kmqq.exe,processorArchitecture="x86",type="win32",version="5.1.0.0".
ERROR: Line 1: XML Syntax error.
ERROR: Activation Context generation failed.
End Activation Context Generation.
=================
Begin Activation Context Generation.
Input Parameter:
Flags = 0
ProcessorArchitecture = x86
CultureFallBacks = en-US;en
ManifestPath = C:\Windows\system32\sppsvc.exe
AssemblyDirectory = C:\Windows\system32\
Application Config File =
INFO: Parsing Manifest File C:\Windows\system32\sppsvc.exe.
INFO: Manifest Definition Identity is System.Security.SoftwareLicensing.Server,processorArchitecture="x86",type="win32",version=" 1.0.0.0".
INFO: Activation Context generation succeeded.
End Activation Context Generation.
And the RT_MANIFEST resource of kmqq.exe is:
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"><assemblyIdentity processorArchitecture="x86" version="5.1.0.0" type="win32" name="kmqq.exe"></assemblyIdentity><description>KitchenMaster</description><trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"><security><requestedPrivileges><requestedExecutionLevel level="asInvoker" uiAccess="false"></requestedExecutionLevel></requestedPrivileges></security></trustInfo><application xmlns="urn:schemas-microsoft-com:asm.v3"><windowsSettings><ms_windowsSettings:dpiAware xmlns:ms_windowsSettings="http://schemas.microsoft.com/SMI/2005/WindowsSettings" xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</ms_windowsSettings:dpiAware></windowsSettings></application></assembly>
I do not find file version msvcr100.dll is conflicting. Can it be that the error is other?
Thanks in advance.

MinGW produces exe which when ran on a different system produces "not compatible with the version of Windows"

Computer A runs Windows 7 x64. Computer B runs Windows 7 x86. I am using Eclipse, Ant and MinGW-w64 to compile the file on Computer A. The file runs fine on Computer A, but on Computer B I get the following error:
The version of this file is not compatible with the version of Windows
you're running. Check your computer's system information to see
whether you need an x86 (32-bit) or x64 (64-bit) version of the
program, and then contact the software publisher.
The program is one file, main.cpp
#include <windows.h>
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MessageBox(NULL, "Goodbye, cruel world!", "Note", MB_OK);
return 0;
}
The ant script is as follows:
<?xml version="1.0" encoding="UTF-8" ?>
<project name="winsock" default="build">
<taskdef resource="cpptasks.tasks">
<classpath>
<pathelement location="E:\_dev\windows\MinGW\msys\home\windoze\projects\Winplay\lib\cpptasks.jar"/>
</classpath>
</taskdef>
<taskdef resource="net/sf/antcontrib/antlib.xml">
<classpath>
<pathelement location="E:\_dev\windows\MinGW\msys\home\windoze\projects\Winplay\lib\ant-contrib.jar" />
</classpath>
</taskdef>
<target name="build">
<mkdir dir="build" />
<mkdir dir="build/obj" />
<cc name="g++" objdir="build/obj" debug="${debug}">
<fileset dir="src" includes="*.cpp" />
<compiler name="g++">
<compilerarg value="-std=c++11" />
</compiler>
</cc>
<condition property="debugoption" value="-g -O0" else="-O2">
<isset property="debug" />
</condition>
<fileset dir="build/obj" id="objects" >
<include name="*.o" />
</fileset>
<pathconvert pathsep=" " property="objectslinker" refid="objects" />
<!-- Due to a bug in GppLinker.java in cpptasks.jar, we must exec g++ because GppLinker erroneously uses gcc, which breaks exception handling. -->
<exec command="g++ -std=c++11 -mwindows ${debugoption} -o build/winplay ${objectslinker}" failonerror="true" />
</target>
<target name="clean">
<delete dir="build" />
</target>
</project>
Why would the .exe work on my system, but not a system with the same Windows? Do I need to do something like statically link to the windows definitions that MinGW uses?
You are generating a 64-bit executable, and it's incompatible with any 32-bit version of Windows (notice that the opposite is not true, if you had generated a 32-bit executable, it would be compatible with 64-bits Windows...)
To generate a 32-bit version of your executable, check the "project options" on eclipse. You'll have to have -march=i686 somewhere in the antfile and the project options. It's possible that Eclipse has a checkbox/combobox for it on its interface...

Launch x86 or x64 MSI from MSBuild bootstrapper - newb

I don't have much of a programming background, but I have been using Wix to build very basic installations for several years, usually consisting of a browser shortcut with an icon file (The desktop shortcut simply opens 32-bit Internet Explorer to a specific URL). The clientele that use my installers don't usually know about their system and it appears the time has come where I can create a bootstrapper that will run 1 of the 2 .msi files, one for x86 & one for x64. The problem is that the Wix help documentation out there assumes a certain level of knowledge about programming and/or Wix and I need "for dummies" level of help. Using code snippets from Rob's answers to an earlier post here (similarly titled) and a post on another site, I have this in my .wxs:
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension" xmlns:bal="http://schemas.microsoft.com/wix/BalExtension">
<Bundle Name="Intermountain SecureAccess Desktop Icon Installer" Version="1.0.0.0" Manufacturer="Intermountain Healthcare" UpgradeCode="61b75a8f-67f6-43a1-beb9-1a0be426b5a6">
<BootstrapperApplicationRef Id='WixStandardBootstrapperApplication.HyperlinkLicense' />
<Payload SourceFile="86IHCSAHCO.msi"/>
<Payload SourceFile="64IHCSAHCO.msi"/>
<Payload SourceFile="C:\Program Files (x86)\WiX Toolset v3.7\SDK\Microsoft.Deployment.WindowsInstaller.dll"/>
</BootstrapperApplicationRef>
<Chain>
<PackageGroupRef Id='Netfx4Full' />
<MsiPackage SourceFile="IHCSAHCO.msi" Id="InstallationPackageId" Cache="yes" Visible="no"/>
<MsiPackage InstallCondition='NOT VersionNT64' SourceFile='86IHCSAHCO.msi' />
<MsiPackage InstallCondition='VersionNT64' SourceFile='64IHCSAHCO.msi' />
</Chain>
</Bundle>
</Wix>
The goal is to create a single .msi (or .exe) called IHCSAHCO.msi which contains the 2 msi packages 86IHCSAHCO.msi & 64IHCSAHCO.msi and then simply runs one or the other depending on the environment. When I run the batch file (called Burn.wxs) with this in it...
set WIX_ROOT=%programfiles(x86)%\WiX Toolset v3.7\bin
del /q /f *.wixobj *.msi
call "%WIX_ROOT%\candle.exe" Burn.wxs
call "%WIX_ROOT%\light.exe" Burn.wixobj -sice:ICE38
pause
...here is the error:
Burn.wxs
C:\PATH\Burn.wxs(8) : error CNDL0104 : Not a valid source file; detail: The 'Bundle' start tag on line 3 position 4 does not match the end tag of 'BootstrapperApplicationRef'. Line 8, position 7.
I feel as though I am close and am hoping one of you could take a quick peek at the code and give suggestions. It would be much appreciated...
Thanks!
RHH
Your opening BootstrapperApplicationRef element on line 4 is an empty element - it shouldn't end in />.
What are you using to edit your files? Pasting your file into Notepad++ and turning on XML highlighting identified the problem pretty quickly.