Skip to content
⌘ NSIS Forum Archive

Windows 8.1 detection

64 posts

Lenge#

Windows 8.1 detection

Hello world,

just to be sure: What exactly is required to make installers created by NSIS 2.46 detect newer Windows versions such as 8/8.1 or 2012/2012R2?

I used to include an updated WinVer.nsh and thought that was enough, but now I came across http://forums.winamp.com/showthread.php?p=2949897 and http://nsis.sourceforge.net/Using_!packhdr, which made me unsure. What's the story behind that compatibility header stuff and which exact code do I need to put into my scripts to make them successfully detect Win8 and above?

Also, will there be a 2.47 update to "officially" include support for newer Windows versions "out of the box" until the final NSIS 3 is released?
Anders#
Why do you care, Win8 and 8.1 should be the same for any desktop app.

To detect Win8.0 you can just copy the Win8 line from WinVer.nsh in NSIS 3.

Windows lies and reports the wrong version when calling GetVersion[Ex] so you need a manifest that declares 8.1 support if you need to know about 8.1. For 2.4x that means !packhdr.

There are a couple of other ways to check. You can check the registry or get the dllversion of kernel32.dll.

I don't think there are any plans for a 2.47 update but the current 3.0 alpha is pretty stable...
LoRd_MuldeR#
Yes, Windows 8.1 broke the GetVersion[Ex] API and will still identify itself as Windows 8.0 - unless you add a special Manifest (UUID) to your executable that "ensures" Windows 8.1 compatibility. But you obviously cannot add such Manifest for future Windows versions (because the UUID of future versions is not known yet), so even with the proper Manifest, your application is going to break in future Windows versions! This was a completely braindead decision by Microsoft! Only to make some completely broken applications work under Windows 8.1, they made GetVersion[Ex] API useless - instead of just having those completely broken applications fixed 👎

Anyway, here is an implementation of GetVersion() that will work correctly on Windows 8.1 as well as on all older versions (at least since Windows 2000) - and it should also continue to work on future Windows versions, as long as they don't break even more Win32 API's:

http://pastie.org/private/2lnfacwakl8opqpnhl2w3q
Lenge#
Originally Posted by Anders View Post
Why do you care, Win8 and 8.1 should be the same for any desktop app.
We need to distinguish due to corporate policies.
Originally Posted by Anders View Post
I don't think there are any plans for a 2.47 update but the current 3.0 alpha is pretty stable...
We'll probably go for 3.x once a non-beta version has been released. But if that is not going to happen too soon, another 2.4x update would be very welcome. See, 3.x is not even beta yet...
Lenge#
Originally Posted by LoRd_MuldeR View Post
Yes, Windows 8.1 broke the GetVersion[Ex] API and will still identify itself as Windows 8.0 [...] you obviously cannot add such Manifest for future Windows versions [...]
So GetVersionEx has become useless as it does no longer reliably deliver the real Windows version. Too bad! 😠

Does this also hold for the newer GetProductInfo API? The MSDN page does not mention anything about it, and I have no machine at hand to try.

Doe anybody know (or would be so kind and try)?
stass#
Lenge
System::Call "kernel32.dll::GetVersion() i .r0"
IntOp $4 $0 & 0xFF ;main ver
IntOp $5 $0 & 0xFF00 ;sub ver
IntOp $5 $5 >> 8
Push $5
Push $4
MessageBox MB_OK "ver : $4.$5 "
or

ClearErrors
ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" "CurrentVersion"
MessageBox MB_OK "ver : $R0 "
or

;http://nsis.sourceforge.net/WMI_header
OutFile "OperatingSystemInfo.exe"
!include MUI2.nsh
!include WMI.nsh
!include LogicLib.nsh
!insertmacro MUI_LANGUAGE "English"

Section
${WMIGetInfo} root\CIMV2 Win32_OperatingSystem Caption callback_Function
${WMIGetInfo} root\CIMV2 Win32_OperatingSystem Version callback_Function
${WMIGetInfo} root\CIMV2 Win32_OperatingSystem CSDVersion callback_Function
${WMIGetInfo} root\CIMV2 Win32_OperatingSystem ServicePackMajorVersion callback_Function
${WMIGetInfo} root\CIMV2 Win32_OperatingSystem ServicePackMinorVersion callback_Function
SectionEnd

Function callback_Function
#$R0 = result number, $R1 = total results, $R2 = result name
System::Call "user32::OemToChar(t R2, t. r2)"
detailprint "$2"
FunctionEnd
or

WinVer.nsh in nsis-3.0a2
<version> can be replaced with the following values:
;
; 95
; 98
; ME
;
; NT4
; 2000
; XP
; 2003
; Vista
; 2008
; 7
; 2008R2
; 8
; 2012
; 8.1
; 2012R2
Afrow UK#
That registry method is the simplest way to correctly get 6.3 on Windows 8.1 but it isn't the recommended way. The WMI method probably also works but I would avoid WMI whenever possible. The other methods will return 6.2 (i.e. ${If} ${IsWin8.1} will never be true).

You need to follow the instructions on http://nsis.sourceforge.net/Using_!packhdr
!define RequestExecutionLevel user
!include Packhdr.nsh
Stu
Anders#
Just to make this clear, using WinVer.nsh from NSISW v3.0 will not work for Win8.1 because v3.0 adds the required 8.1 supported OS GUID to the manifest when you use RequestExecutionLevel...
LoRd_MuldeR#edited
OS GUID + GetVersion[Ex] doesn't do solve the issue at all!

As explained before, adding the "proper" OS GUID to the application's Manifest doesn't solve the underlying problem at all. They have changed the API to return "the latest OS version supported by the calling application" (according to Manifest), instead of what the actual OS version is! So adding the GUID is just a temporary workaround to get the GetVersion[Ex] API working correctly on Windows 8.1 - but it's almost certainly going to break on any future Windows! That's because you cannot know the GUID's of future Windows versions now.

So, when building an installer today, you might be able to get GetVersion[Ex] to report Windows 8.1 correctly by adding the GUID, yes! But a future Windows 8.2 or Windows 9.0 (whatever they will call it) is going to identify itself still as Windows 8.1 - because the "new" OS GUID, which you could impossibly know at the time when building your installer, is missing in the Manifest. So Windows 8.2/9.0 thinks your application requires Windows 8.1 and thus will identify itself like that. But IMO it is very important to detect also future Windows versions! For example, when I need to ensure that we are running specifically on Windows 8.1, my installer would get fooled on Windows 8.2 😠

On the other hand, the code I posted above does not rely on any OS GUID's in the Manifest, it will (hopefully) continue to give the correct result in future Windows versions and it doesn't use any undocumented Registry hacks or unreliable DLL version checks...


[UPDATE]

Here is a new StdUtils plug-in version with GetRealOsVersion() function:


LoRd_MuldeR#
Small update:


Added the VerifyOSVersion() function, for convenience. See the docs for details!
Afrow UK#
I thought this was fairly obvious but why would you be trying to detect a future version of Windows before it has been released anyway? Once the new Windows OS is available, you can add the compatibility GUID and then test your application still works with any changes as necessary (e.g. OS detection changes). Microsoft will not publish/confirm the GUID or Windows version until it has gone gold anyway.

This seems a non-issue to me. We only have this small issue because official NSIS 2.46 has not been updated since 2009. NSIS 3.0 is the answer.

Edit: Hypothetically, lets say they released 8.2. As far as your application is now concerned, it is still running on 8.1. What is the problem with this? You should not have written any code specifically for 8.2 because it has only just been released. Therefore the answer is just to add the new GUID and then update your code to deal with changes in 8.2, and finally release the new version of your software.

Stu
LoRd_MuldeR#
This doesn't quite hit the point. The point is that the GetVersion() API has been changed from an API that tells you the actual version of the installed operating system to an API that just returns the latest OS version that your application is compatible with (according to its Manfest) - which probably has been done to "satisfy" some horribly broken programs. This means that GetVersion() is pretty much a NOP function now, because you already know which version(s) you have written into the Manifest at compile time - no need to ask the Win32 API again.

And of course I do need to reliably detect "future" versions too! Detetcing older versions is only one side of the medal. We need both, as that's how any sane GetVersion() has to work! For example, in Debug logs I want to put a FAT WARNING, if the user runs the application on a Windows version I had never tested before. Having to "repair" my installer for every new Windows version in order to workaround API restrictions is the most horrible idea I have ever heard! What a maintenance nightmare and the most braindead decision by Microsoft ever.

Consequently, I recommend that the code I have suggested above (or a similar solution) gets incorporated into NSIS 3.0, so we can have a reliable working Windows version detection "out of the box" again. That code has been successfully committed into the Qt framework already in order to fix their version detection, by the way. Don't see why NSIS wouldn't want that too, as it's a pretty simple/clean solution.

If the version detection in NSIS remains "broken", I (and probably other dev's too) will be forced to stick with an external plug-in 😐
Anders#
NSIS follows the official MS recommended way of doing things and I don't know if we should add a hack like this to the exehead. At this point we don't even know if GetVersion in 8.2 is going to lie when you have the 8.1 manifest.

I have already written some code for the system plugin that uses VerifyVersion but I have not decided what to do with it yet. The only valid reason I see for doing this is for logging/debugging, a installer should not use this information for its normal logic and you should just accept the lies that Windows tells you because it will probably be more compatible that way.

Is it insane/annoying/silly that MS decided to lie to everyone because of a few bad applications..yes but there are basically no changes for Desktop apps since 8.0 so it is not really a big problem.
LoRd_MuldeR#edited
Originally Posted by Anders View Post
NSIS follows the official MS recommended way of doing things and I don't know if we should add a hack like this to the exehead. At this point we don't even know if GetVersion in 8.2 is going to lie when you have the 8.1 manifest.
You are right, we don't know for sure. We can only make an educated guess at this point. But since they have just introduced this new "mechanism" with Windows 8.1, I highly doubt they're going to revert it with the next version. The intention seems clear: Tell applications what they want to hear (most likely), so customers won't bother our support team with compatibility issues...

Originally Posted by Anders View Post
I have already written some code for the system plugin that uses VerifyVersion but I have not decided what to do with it yet. The only valid reason I see for doing this is for logging/debugging, a installer should not use this information for its normal logic and you should just accept the lies that Windows tells you because it will probably be more compatible that way.
I'm not so sure about "more compatible". If I write an installer and, e.g., make it refuse to install on future/untested Windows versions, I probably do this for a reason! And if it doesn't work in the way that any sane person expects, I will blame NSIS for the "wrong" behavior. And for applications that are really broken with respect to the version detection Windows had (and still has) the "compatibility mode" since Windows XP. No reason for another "compatibility" layer, which you cannot even turn off and which interferes with "proper" applications.

Furthermore, I think the solution ("hack") based on VerifyVersion() doesn't have any drawbacks, provided that Microsoft doesn't make VerifyVersion() go rampage in future versions. If GetVersion() works as expected - as is the case in older Windows versions and might also apply to some hypothetical future versions - the additional VerifyVersion() checks don't do anything. It might be a tiny performance overhead, yes. But honestly, if querying the Windows version is a performance bottleneck of your program/installer, then your problem is elsewhere.

Originally Posted by Anders View Post
Is it insane/annoying/silly that MS decided to lie to everyone because of a few bad applications..yes but there are basically no changes for Desktop apps since 8.0 so it is not really a big problem.
But if they stick with the current behavior, it might become a "big problem" in the (near) future.
Anders#
Originally Posted by LoRd_MuldeR View Post
You are right, we don't know for sure. We can only make an educated guess at this point. But since they have just introduced this new "mechanism" with Windows 8.1, I highly doubt they're going to revert it with the next version. The intention seems clear: Tell applications what they want to hear (most likely), so customers won't bother our support team with compatibility issues...
I did not mean revert, I was thinking that the 8.1 guid could be a switch that unlocks GetVersion for all future releases.


Originally Posted by LoRd_MuldeR View Post
But if they stick with the current behavior, it might become a "big problem" in the (near) future.
Desktop is dead (Has anything useful been added since Win7?) and they want to force WinRT on everyone.
LoRd_MuldeR#
Originally Posted by Anders View Post
I did not mean revert, I was thinking that the 8.1 guid could be a switch that unlocks GetVersion for all future releases.
That would make even less sense. But yes, doesn't mean it couldn't happen.

Originally Posted by Anders View Post
Desktop is dead (Has anything useful been added since Win7?) and they want to force WinRT on everyone.
I don't think that is yet decided 😉

Actually "Windows RT", the one that runs on ARM and that is limited to the "Metro" UI, is dead, because it was a complete failure on the Tablet market - while the "normal" Windows 8 with "Metro" and Desktop is still around. Surely, Windows 8 is all about the new "Metro" UI, because they eagerly wanted to push Windows on Tablet computers. They even forced the "Metro" UI on Desktop computers, in an attempt to get as many developers as possible making "Metro" apps. Still, many Desktop users are unhappy with Windows 8 and will stick with Windows 7 (just like they did with Windows XP during the "Vista era"), which is not very surprising. But most important Windows 8 is completely out of the question for companies to install on their Desktop workstations - they all are skipping Windows 8. Even Microsoft now admits that Windows 8 didn't bring the intended success. So in the next "major" version they need to deliver something for Desktop again. Not only to address all the Windows 7 Desktop users, but to deliver something that has a realistic chance to be adopted by companies.
Lenge#
I definitely support the opinion that there must be a reliable way to detect the exact true Windows version that my application is actually running on, without any compatibility lies or guesses. The 8.1 change to GetVersionEx renders it useless for this purpose, so I welcome any addition to NSIS that enables a "real" version detection.

With respect to MuldeR's approach to check a (potentially false) version using VerifyVersion , I'd like to ask how VerifyVersion behaves if the app was run in compatibility mode? Even before the 8.1 change, GetVersionEx was telling lies about the Windows version if the app was run in compatibility mode (instead of the "real" Windows version, it reported the version that was set in the compatibility options). So, how does the VerifyVersion approach behave in this respect?
LoRd_MuldeR#
Doesn't look like it can be fooled by "compatibility mode", but feel free to play around with it yourself:
WinVer.2014-02-17.zip

Anders#
Originally Posted by LoRd_MuldeR View Post

Actually "Windows RT", the one that runs on ARM and that is limited to the "Metro" UI, is dead, because it was a complete failure on the Tablet market ...
WinRT=The new COM-like API at the bottom of the "Metro stack", WindowsRT=Windows on ARM (WOA)

I think 8.2 comes out in April but probably no big changes for Desktop apps, not even charms support?
Anders#
There is also shlwapi::IsOS but somebody would have to debug it on 8.1 to figure out the value to use for ISOS_81ORGREATER...
LoRd_MuldeR#
Originally Posted by Anders View Post
I think 8.2 comes out in April but probably no big changes for Desktop apps, not even charms support?
Windows 8.1 and 8.2 are more like Service Packs to Windows 8.0, not really a new Windows.

Still there have been some tiny Desktop improvements in 8.1, like options for booting directly to the Desktop, having your Desktop wallpaper on the Start screen and having Desktop programs displayed before Metro apps on the Start screen. AFAIK, in 8.2 it will be possible to pin Metro apps to the taskbar. And for 9.0 there are rumors about a Startmenu and Metro apps in "windowed" mode. Just rumors, of course.

Originally Posted by Anders View Post
There is also shlwapi::IsOS but somebody would have to debug it on 8.1 to figure out the value to use for ISOS_81ORGREATER...
I can't see any mention of Windows 8 in the docs for that function:


But even Windows Vista and Windows 7 are explicitly not supported:
Values are not provided for Windows Vista and Windows 7. To determine whether either of those operating systems are present, use VerifyVersionInfo.
Anders#
Originally Posted by LoRd_MuldeR View Post
Windows 8.1 and 8.2 are more like Service Packs to Windows 8.0, not really a new Windows.

Still there have been some tiny Desktop improvements in 8.1, like options for booting directly to the Desktop, having your Desktop wallpaper on the Start screen and having Desktop programs displayed before Metro apps on the Start screen. AFAIK, in 8.2 (or maybe it's called "8.1 Update 1") it will be possible to pin Metro apps to the taskbar.
But there are no useful API changes, all those other things are explorer.exe changes.

IsOS has checks for Vista+, 7+ and 8+ but they are not documented. This is one of those functions that MS did not really want to document but they were forced to document some of it, probably only the parts used by IE and the active desktop bits. The actual implementation is in shcore.dll IIRC if someone wants to find the 8.1 value...
LoRd_MuldeR#
Originally Posted by Anders View Post
IsOS has checks for Vista+, 7+ and 8+ but they are not documented. This is one of those functions that MS did not really want to document but they were forced to document some of it, probably only the parts used by IE and the active desktop bits. The actual implementation is in shcore.dll IIRC if someone wants to find the 8.1 value...
Interesting. Though I don't see what would be the advantage in using the isOS() function. Especially because relying on undocumented behavior isn't very safe, the function was not even exported by name or declared in a public header before Windows Vista (could only be used via GetProcAddress with some magic number) and - most important - with VerifyVersionInfo() we have a more powerful/flexible alternative that also is officially documented. Finally, even if we did figure out the value to check for Windows 8.1 with isOS(), it would still not be able to detect any future version (unless we update it with every release), leading to the same dilemma as using Manifest GUID's.

Also, the isOS() function has some rather strange behavior:
OS_WIN2000ORGREATER   7  The program is running on Windows 2000 [or later].
OS_XPORGREATER 18 Always returns FALSE.
So we can check for Win2k+, but the check for WinXP+ is going to fail always 😕
Anders#
Originally Posted by LoRd_MuldeR View Post
So we can check for Win2k+, but the check for WinXP+ is going to fail always 😕
Guess what? The XP check works fine and the docs are wrong.

I know this cannot be used for future detection but it can probably help with detecting 8.1 if that is all you need and you don't want to mess with the manifest.

Here is some code I wrote a while ago but never tested:
!include LogicLib.nsh
push $0
push $9
System::Call '*(i148,i,i,i,i,&w128,i,i)p.r0'
!if "${NSIS_PTR_SIZE}" <= 4
System::Call 'kernel32::GetVersionExA(pr0)i.r9' ; OSVERSIONINFOA for Win95
System::Call '*$0(i,i.r1,i.r2)'
System::Call 'kernel32::VerifyVersionInfoW(pr0,i 3,l 20)i.r9'
${If} $9 == 0 ; GetVersionEx lied (Must use string test, if VerifyVersionInfoW does not exist the string will be "error" and that converts to 0 for number testing)
!endif
StrCpy $1 3 ; NT4.SP4 has VerifyVersionInfo
${Do}
IntOp $1 $1 + 1
System::Call '*$0(i276,ir1,i0)'
System::Call 'kernel32::VerifyVersionInfoW(pr0,i 2,l 16)i.r9'
${LoopWhile} $9 = 0
StrCpy $2 -1
${Do}
IntOp $2 $2 + 1
System::Call '*$0(i,i,ir2)'
System::Call 'kernel32::VerifyVersionInfoW(pr0,i 3,l 20)i.r9'
${LoopWhile} $9 = 0
!if ${NSIS_PTR_SIZE} <= 4
${EndIf}
!endif
System::Free $0
pop $9
pop $0
DetailPrint WinVer=$1.$2
Lenge#
Originally Posted by LoRd_MuldeR View Post
Doesn't look like it can be fooled by "compatibility mode"
I came across this article that says that, if your app includes an 8.1 manifest, it can no longer be run in compatibility mode (see 6th paragraph of the "Update" section).

If your version test application is made with NSIS 3.x (is it?), it probably does include an 8.1 manifest, and so the compatibility mode might not be in effect.

Originally Posted by LoRd_MuldeR View Post
but feel free to play around with it yourself
I will, as soon as I have according machines at hand. Currently I have only access to XP machines. In the meantime, it would be great if somebody could verfiy if an application with an explicit 8.1 manifest can be run in compatibility mode or not.
LoRd_MuldeR#
Originally Posted by Lenge View Post
If your version test application is made with NSIS 3.x (is it?), it probably does include an 8.1 manifest, and so the compatibility mode might not be in effect.
Nope. My test app was built with Unicode NSIS v2.46.5 🙂

Originally Posted by Lenge View Post
I will, as soon as I have according machines at hand. Currently I have only access to XP machines. In the meantime, it would be great if somebody could verfiy if an application with an explicit 8.1 manifest can be run in compatibility mode or not.
Seems like they can. I have a simple C++ app for testing my get_real_os_version() function. Without Windows 8.1 GUID in the Manifest, the result of GetVersionEx() is wrong and we need to fix it via VerifyVersionInfo(). By adding the GUID, we get the correct value right away. But I can still enable the "compatible mode", which will still make GetVersionEx() return a wrong value - but again VerifyVersionInfo() will fix it.

BTW: You can get an evaluation edition of Windows 8.1 Enterprise for free and install it in VirtualBox/VMWarePlayer. That's what I do 😉
Anders#
Originally Posted by Lenge View Post
I came across this article that says that, if your app includes an 8.1 manifest, it can no longer be run in compatibility mode (see 6th paragraph of the "Update" section).
When you have a Supported OS Guid in your manifest you will not get automatic compatibility fixes for <= that version of Windows. Changes in 8.1 are listed here.

The compatibility mode you can select in the properties tab are additional shims added on top of this. The fact that VerifyVersionInfo is not affected by the changes you make here is a serious bug in Windows (VerifyVersionInfo was added in some NT4 service pack so it is a pretty old function)
LoRd_MuldeR#
Originally Posted by Anders View Post
The compatibility mode you can select in the properties tab are additional shims added on top of this. The fact that VerifyVersionInfo is not affected by the changes you make here is a serious bug in Windows (VerifyVersionInfo was added in some NT4 service pack so it is a pretty old function)
Well, it only means that "compatibility mode" doesn't resamble the behavior of the older Windows versions accurately - and it doesn't have to. If you think of "compatibility mode" as something that has the goal to get old/broken applications working under up-to-date Windows (rather than something that has the goal to emulate the older Windows version as accurate as possible), then it makes sense that GetVersionEx() will return the older Windows version (as selected in "compatibilty mode" options) and - at the same time - VerifyVersionInfo() accepts everything up to the actual Windows version. Restricting VerifyVersionInfo() to only accept what would have been accepted by the older Windows version obviously doesn't aid the golad to improve compatibilty. After all it's "compatibility mode", not "emulator mode".
Anders#
Originally Posted by LoRd_MuldeR View Post
Restricting VerifyVersionInfo() to only accept what would have been accepted by the older Windows version obviously doesn't aid the golad to improve compatibilty.
Well, I don't agree with this at all.

Imagine this program:
if (IsVistaOrLater())
DoVistaThing();
else
DoXPThing();
With this code, what happens when you choose a compatibility option in the .exe properties depends on the implementation of the IsVistaOrLater() function in this program. If GetVersion[Ex] was used then the program will fall back to XP behavior like the user requested but if VerifyVersionInfo was used then the users choice is ignored. You could argue that this is not a compatibility issue since the program knows about Vista but perhaps DoVistaThing() stopped working in 7 and you need to fall back to XP behavior for the program to work at all.

Imagine another scenario: A program uses VerifyVersionInfo but one of the 3rd party DLLs it depends on uses GetVersion, the combined program logic will now be out of sync!

VerifyVersionInfo was invented because people wrote broken comparison code like:
if (major >= 5 && minor >= 1)
DoXPThing();
else
MessageBox("Not supported on this version of Windows");
(This is broken on Vista and would think it is < XP but works correctly again on 7)
IMHO VerifyVersionInfo should not act like some backdoor that ignores the requested compatibility mode and should just report the same results as GetVersion.


I also don't understand your compatibility != emulation argument. Emulation is exactly what it does, when the program is started the loader replaces standard Windows functions with a different function that is designed to work like it did in the older OS ( http://blogs.technet.com/b/askperf/a...new-stuff.aspx ). A couple of the shims even have Emulate in their name ( http://technet.microsoft.com/en-us/l...=ws.10%29.aspx )

A real world scenario where the version lie is not even about compatibility but the desire to emulate older behavior is/was MSN messenger. On 7 if you want it to use the tray icon mode you have to emulate a OS that does not have the new taskbar api.
Lenge#
Originally Posted by Anders View Post
Well, I don't agree with this at all. [...]
I respectfully disagree. I fully agree with MuldeR in this respect. As a developer, I definitely need a reliable way to detect the exact true Windows version that my application is running on, regardless of whatever manifest or compatibility options have been set.

What I do not need is a vendor that tries to tell me that I wouldn't want to know (or am not even allowed to know). Of course, we're all trying to make our apps as compatible with different versions of Windows as possible. But if I decide to query the real version, I do that for a reason, and I want to be sure that the result is correct.

However, I have no objections against having separate functions for "GetWindowsVersion" and "GetRealWindowsVersion", where the second one is not affected by whatever compatibility setting.