Skip to content
⌘ NSIS Forum Archive

Windows 8.1 detection

64 posts

Anders#
Originally Posted by Lenge View Post
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 you are saying does not make any sense to me. The reason the compatibility shims exist in the first place is so people can apply them on broken applications. If you are asking for a way around that then you are just going to end up causing extra pain for end users down the road unless you write perfect bug free software.

For diagnostics, sure, include kernel32 version, registry version or a GetRealWindowsVersion based on VerifyVersionInfo but for decisions made at run-time I don't think it is wise to fight whatever shims someone has applied to your process.

Before 8.1, things in the manifest only unlocked new features and it is sad that MS has decided to change that. I still think it is a good idea to wait for 8.2 to see if the 8.1 guid is enough to unlock GetVersion before we start thinking about ways to bypass everything...


Originally Posted by Lenge View Post
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.
When you say vendor, do you mean NSIS or MS?

As a user I feel that it is my machine and if I want to put your application in compatibility mode then that should be my decision and there should not be anything you can do about it. Sadly because the version functions are implemented differently, Win8.1 leaves us in nowhere land where all of this depends on which function the developer chose to use.
LoRd_MuldeR#
Originally Posted by Anders View Post
Well, I don't agree with this at all.

Imagine this program:

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:

(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.
Well, I think we can agree that for any possible behavior of the "compatibility mode" we could probably find a program that (still) won't behave as intended with the "compatibility mode" enabled. I think it's just not possible to fix any broken program out there with just a single "compatibility mode" switch - simply because we cannot know what the programmer had originally in mind or what bugs are present in the program. The actual "compatibility mode", as it exists today, has probably been designed to hack around the most common issues.
Lenge#
Originally Posted by Anders View Post
When you say vendor, do you mean NSIS or MS?
I mean Microsoft (the OS vendor that controls the APIs). NSIS is more like a partner that helps me work around some of their OS design decisions. After all, the entire NSIS concept is a way to avoid the "official standard" (which is MSI).

Originally Posted by Anders View Post
As a user I feel that it is my machine and if I want to put your application in compatibility mode then that should be my decision and there should not be anything you can do about it.
Good point, yet still:

1.) While I agree that "GetWindowsVersion" should be the "normal" method to use in most cases, I still sometimes need "GetRealWindowsVersion". And when I do so, I know what I'm doing and why I'm doing that. (And, I promise, it's not for the purpose of insulting the user or making the application break. More of the opposite.) So if there is no cleanly defined "GetRealWindowsVersion" API, I'm forced to use some hack, which in turn is more likely to cause problems than any clean solution.

2.) In the case of Windows 8.1, you (as a user) just cannot decide to not run an application in compatibility mode. Microsoft has already decided that you must run every app in Win8 compatibility mode, unless the app has an explicit 8.1 manifest (which you as a user cannot influence).
LoRd_MuldeR#
Just for the notes: I had a chance to look at the "Windows 10" preview today. The NT version is 6.4 and the build number is 9841. The workaround I used to get the real Windows version under Windows 8.1 still works in Windows 10. Anyway, I have put up an new build here, so we also get the correct "friendly name" on Windows 10.

Anders#
Public service announcement:

Win10 TP 10041 now also lies to the VerifyVersionInfo function unless you have the Windows 10 GUID in the manifest.

With no GUIDs in the manifest it tells you that you are on 6.2 and on 6.3 if you have the Win8.1 GUID...

The ManifestSupportedOS instruction in NSIS v3 supports GUIDs as parameters:
ManifestSupportedOS Win7 Win8 Win8.1 {8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}
(The latest beta supports Win10 as a parameter so you don't need a guid until Windows 11)
LoRd_MuldeR#
Originally Posted by Anders View Post
Win10 TP 10041 now also lies to the VerifyVersionInfo function unless you have the Windows 10 GUID in the manifest.
I can confirm this. So they brought it to yet another level of idiocy 🙄 🙄 🙄

Also, at one point, "Windows 10" changed from identifying itself as NT v6.4 to NT v10.0, following the motto "hey, let's establish the most stupid versioning scheme ever, and once everybody has gotten used to this insane mess and implemented the required workarounds, let's break everybody's code once again by making the versioning sane again". Sorry, but is Satya Nadella secretly payed by Apple to piss off the Windows developer community as much as possible? Makes me even less confident that Windows has a future. In the past every "borked" Windows release was followed by a "good" one. But after the Windows 8 disaster, we got the Windows 8 disaster part two (aka Windows 8.1). And with "Windows 10", which was supposed to make it all so much better again, things are just getting worse and worse...

Anyway, that's enough grumbling for now. Here is an updated version of StdUtils, in order to take account for the Windows 10 madness:


(The new solution doesn't depend on some obscure GUID that's guaranteed to break with the next Windows version, but uses GetFileVersionInfo() directly on kernel32.dll. Yes, it's ugly, but it works. If, in the next Windows version - provided that there will be a next Windows version - they break GetFileVersionInfo() too or they ship a kernel32.dll with "wrong" version number in its resource section, I'll just throw my Windows PC out of the window)

Have a nice Sunday 😎
Anders#
Originally Posted by LoRd_MuldeR View Post
The new solution doesn't depend on some obscure GUID that's guaranteed to break with the next Windows version
The GUID is guaranteed to give you forward compatibility for the exact versions you specify, that's the whole point of them in the first place. (You can only test on versions you know the GUID for so your app should not break in the future because it still thinks it's on the older version of Windows)
LoRd_MuldeR#
Originally Posted by Anders View Post
The GUID is guaranteed to give you forward compatibility for the exact versions you specify, that's the whole point of them in the first place. (You can only test on versions you know the GUID for so your app should not break in the future because it still thinks it's on the older version of Windows)
By definition, we now need to include the specific GUID for Windows version "xyz" in order to make Windows version "xyz" identify itself faithfully. Any Windows, for which the specific GUID is not included in the program's Manifest, will lie to us and give invalid result. But it's impossible to know and include the GUID's for any Windows version that will exist in the future - they simply are not known yet!

But the whole purpose of a version getter API is to report the exact system version that we running on - including any future version that may not even have existed by the time we compiled the program! With that GUID nonsense, even if I include all the GUID's that existed by the time I compiled my program, the API still will only report those Windows versions correctly that existed by the time I compiled my program. Any future Windows version would lie to my program! It would report the Windows version my program was compiled for, not the "true" Windows version we are running on. But it's especially interesting/important to get the correct Windows version in this case!

An API that just reports an information that I already know at compile time - the highest Windows version for which I have included the corresponding GUID - effectively is a NOP(*). Which makes the "official" version getter API of Windows totally pointless now 🧟




(*) Yes, it's not entirely a NOP, because I can still distinguish those Windows version for which I explicitly have included the corresponding GUID's. But that's clearly not sufficient. And it's certainly not what you'd expect from a proper version getter API! It's a crippled version getter API that you cannot rely on!
Anders#
Originally Posted by LoRd_MuldeR View Post
...and give invalid result.
It is not a invalid result, it is just that your app is running in compatibility mode. If you include the GUID for Windows 10 today in 2015 then your app is supposed to work without changes on Windows 11 assuming you are not doing undocumented stuff. It is of course impossible to test on Windows 11 now so your app is just supposed to pretend that it is still on 10 which is why the API lies.

Your are not really supposed to check the version number anyway, you are supposed to check if a feature exists before using it or whatever fallback mode you might have. Granted, it sucks for logs etc but MS has decided that compatibility for crappy programs is more important than keeping developers happy...
th_mi#
Originally Posted by LoRd_MuldeR View Post
(The new solution doesn't depend on some obscure GUID that's guaranteed to break with the next Windows version, but uses GetFileVersionInfo() directly on kernel32.dll. Yes, it's ugly, but it works. If, in the next Windows version - provided that there will be a next Windows version - they break GetFileVersionInfo() too or they ship a kernel32.dll with "wrong" version number in its resource section, I'll just throw my Windows PC out of the window)
I use this way to identify the OS Version since Win8/8.1. In case they asl obreak this way I'll do the Windows API way. Until today every Windows Version brings new API commands. A new version has a few new APIs, it is also a try to use LoadLibrary/GetProceAddress to find out what API exist on what OS...

But I agree it is getting more and more strange at all...
LoRd_MuldeR#
Originally Posted by Anders View Post
It is not a invalid result, it is just that your app is running in compatibility mode. If you include the GUID for Windows 10 today in 2015 then your app is supposed to work without changes on Windows 11 assuming you are not doing undocumented stuff. It is of course impossible to test on Windows 11 now so your app is just supposed to pretend that it is still on 10 which is why the API lies.
IMO, if my application uses only documented functions and only relies on documented behavior, there will be no need for any "compatibility mode" at all! It will continue to work perfectly fine in future Windows versions without "compatibility" hacks or workarounds – provided that M$ doesn't intentionally break the documented behavior, of course. There would be some use case for "compatibility mode", if a broken application relies on undocumented behavior (or even bugs) that existed in earlier Windows versions, but not in the current one. But in this case we should rather fix the broken application than implementing workarounds that only encourage people to rely on undocumented stuff.

Actually, the "enforced compatibility mode" has great potential to breaking things. If, e.g., my application relies on the correct documented behavior of GetVersionEx(), it will stop working in Windows 8.1. So, following the M$ method, I will need to "fix" my application by adding the required GUID. But on Windows 10 it will break, once again. And I will need to "fix" my application, once again. For every new Windows version I will have to "fix" my application – again and again! Without all the "compatibility" mess and if they do not mindlessly break existing documented behavior, the very same binary that was originally compiled for Windows XP could still be working perfectly fine in Windows 10.

BTW: If I want to accurately emulate the behavior of Windows N on Windows N+1, there is a solution known for decades - virtualization.

Originally Posted by Anders View Post
Your are not really supposed to check the version number anyway, you are supposed to check if a feature exists before using it or whatever fallback mode you might have. Granted, it sucks for logs etc but MS has decided that compatibility for crappy programs is more important than keeping developers happy...
The operating system provides services to the application. So the job of the OS is to correctly implement these services. If and how the application makes use of these services - the OS doesn't need to care! I decide what I do with the version number in my program. Microsoft's job is to give me the correct number – not more, not less. If the application does something stupid, it's the problem of this application. And it can only be properly fixed inside that particular application. Microsoft's sphere of influence must end at the application boundary.

Really, you can't punish every application out there, just because a few applications do stupid things. If at all, the "compatibility" hacks must be optional (disabled by default!), so they can be enabled if and only if they are actually needed and/or helpful. Heck, if they think users are too dumb for enabling compat mode themselves, they could have implemented a Blacklist for known "broken" applications.

Originally Posted by th_mi View Post
I use this way to identify the OS Version since Win8/8.1. In case they asl obreak this way I'll do the Windows API way. Until today every Windows Version brings new API commands. A new version has a few new APIs, it is also a try to use LoadLibrary/GetProceAddress to find out what API exist on what OS...
I have been experimenting with this method too. The problem here is: You can only distinguish Windows versions that already existed by the time when you program was compiled. You can not detect future versions, as you don't know which new API's they are going to add or which NT version the next Windows will have. From this point of view, your solution has the same limitation as the "GUID" workaround.
Afrow UK#
I'm still a little confused as to what the issue is here. If you need to detect that you are running Windows 10, add the GUID. Otherwise on Windows 10 as far as your app knows it is running on Windows 8.1. Why is this such a big issue?

I can see this being an issue for logging or reporting purposes but surely it's not difficult to add the GUID and send out an updated version of your app? That is the only case that there is an issue and it is a very minor one.

Also I'm not sure how this "punishes" the application by giving it the false version. As I've said before, the app will not support newer Windows 10 features until you add them anyway - at which point you can add the GUID...

Stu
DrO#
as for not working with newer OS features, most cases you need to update to support the newer OS so then adding the guid at that point is a non-issue imho.

tbh the argument that comes across is almost like "why should I have to keep updating things". and with the compatibility issues, MS can also insert a shim to correct / revert things if needed (often without you knowing about it in the first place) so that's a bit of a non-issue as well.

sure it's not great if the OS changes things but it's not just an issue specific to Windows and any developed system can 'break' things or change the rules (which am sure are not changed unless there's a reason to do so).

either way, I can why for some cases it's not great about this change, then again just magically expecting things to work like before is a bit naive and I don't mind the guid aspect as it's not like things with Windows haven't changed / been broken before and any change in OS means having to do some sort of update if you really need to know the OS version anyway (as how can you magically know in advance the name for the OS?).

if anything, a reliance on the OS version for identification is an old way of thinking about things (yes I know it'd be needed to gauge what OS a crash report comes from) but the more web-like way of querying for functionality than assuming it on the OS is better imho (and is something that has definitely been needed since XP such as for SP2 vs SP3 additions).
LoRd_MuldeR#edited
Originally Posted by Afrow UK View Post
I'm still a little confused as to what the issue is here. If you need to detect that you are running Windows 10, add the GUID. Otherwise on Windows 10 as far as your app knows it is running on Windows 8.1. Why is this such a big issue?
If you are only interested to get the correct result on Windows versions that existed by the time when you compiled your program, the GUID method can work for you. It's an extra burden for the developer, but works. But as soon as you want to ensure that your program will still get correct result on future Windows versions – and I mean without having to modify/rebuild the program – the GUID method can not work.

Now you can say: "I don't care about future OS versions" or "getting the correct version isn't all that important for me". But for some people it is! Suppose I have shipped a program 6 months ago and today I get a bug report from a customer. If the log file says "Windows 8.1" this could mean he really is on "Windows 8.1", but it could also means he is on "Windows 10" (because the info in the log is wrong). Of course I need to know the exact system specification to try to replicate his problem! So I will have to ask him what OS he really uses. Now suppose he is located on the opposite side of the globe. Means I will have to wait (at least) until the next workday to get an answer. Thank you M$!

---

Also there is another very important point: Very often you are writing library code! This means you can not influence the Manifest of the program, which will contain your code, at all! And, in my experience, most program files don't contain any of these compatibility GUID's – programmers barely know that such thing exists. So, for library code, you have to assume that no particular GUID will be included in the program. Therefore, you won't even be able to distinguish those Windows versions that exited by the time you created your code !!!

Also I'm not sure how this "punishes" the application by giving it the false version.
They have intentionally crippled the version getter API (make it report the wrong result), because they think a few programs used it to do something that Microsoft didn't "intend". Who do they think they are to tell programmers what they are supposed to do? Anyway, instead of just "blacklisting" the few programs that were effected by this "problem", they crippled the entire version API, for all applications.

As I've said before, the app will not support newer Windows 10 features until you add them anyway - at which point you can add the GUID...
The program now needs to be modified in order to "support" Windows 10 (i.e. get the correct results under Windows 10), because M$ has intentionally broken the documented behavior of the Win32 API. And the program will have to be modified – again and again – for every new Windows version. If, on the other hand, they hadn't done this act of mental deficiency, any program compiled for Windows XP that only uses documented functions and only relies on documented behavior, would probably still work correctly on Windows 10 today.

---

I will not try to further convince you that it's important to have a reliable method to detect the exact OS version that we are running on – current and future. But I surely need this functionality. And it appears a lot of other people do too. So whoever finds the GetRealOSVersion and GetRealOSName functions useful may use them – now on Windows 10 too. The rest of the world can just happily ignore them 😉
Lenge#
As already stated, I agree with LoRd_MuldeR on this topic in any and all possible ways. If I need to determine the exact real version that my app is running on a regular basis, I do so for a reason, and I need a way to do so that is not influenced by some compatibility hack that Microsoft created for certain OS versions.

So I am glad that that the GetRealOSVersion function exists, and I hope that it'll be updated if Microsoft should break their own documented APIs again.
LoRd_MuldeR#
Originally Posted by Lenge View Post
So I am glad that that the GetRealOSVersion function exists, and I hope that it'll be updated if Microsoft should break their own documented APIs again.
As long as GetRealOSVersion needs to be updated for each new Windows version, it's not really better than the GUID workaround. Okay, GetRealOSVersion can work inside libraries too, which is impossible for the GUID method (by design). But the actual plan is to find something that will continue to work in future Windows version without modification. So far, Microsoft is leading 1:0, but we'll see 😉
Lenge#
BTW: Now that GetRealOSVersion, GetRealOSName, and VerifyOSVersion work correctly again on all (yet-released) Windows versions (thanks again!), what is the current state of GetRealOSBuildNo and VerifyOSBuildNo? I guess that is not as easy to fix, right?
LoRd_MuldeR#
Originally Posted by Lenge View Post
BTW: Now that GetRealOSVersion, GetRealOSName, and VerifyOSVersion work correctly again on all (yet-released) Windows versions (thanks again!), what is the current state of GetRealOSBuildNo and VerifyOSBuildNo?
Internally, all these functions are just "wrappers" around the very same "core" function. And this "core" function reveals the real OS version by first retrieving the "fake" version via GetVersionEx() and then refining the result via an iterative algorithm based on VerifyVersionInfo(). And now it additionally compares the detected version with the file version of KERNEL32.DLL in order to "repair" the result on Windows 10 too. This all works the exactly same way for the "major" and "minor" NT versions as well as for the "build" number. It's simply different parts of the same intermediate result. However, I wonder what will happen when they exceed build #65535, because that's the highest number that can be represented as a 16-Bit value - and the part of the KERNEL32.DLL version that they use for the build number is only 16-Bit in size!

BTW: GetRealOSName naturally cannot give you the correct name of future Windows version, because this name is not even decided yet. Still it should return "unknown" instead of returning an invalid Windows version, like GetVersionEx() would do - and knowing that you are actually running on some unknown OS version (by the time your product was released) can already be a valuable information, I think.
Lenge#
Ok, thanks for the detailed information. This is exactly what I was looking for: So, on a future OS, I will still receive the correct major/minor version and build number regardless of any compatibility mode that might be in effect (at least until MS breaks things again). Of course the OS name cannot be known in advance, so "unknown" is a good choice.
Afrow UK#
You could read version information from HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion (SetRegView 64) if that is sufficient as a last resort.

Edit: Or WMI of course...
wmic os get caption

Stu
LoRd_MuldeR#
Here is a new version of StdUtils, which contains an improved GetRealOSVersion function:


This one no longer relies on investigating the file version of KERNEL32.DLL to detect Windows 10+. Does not rely on WMI (COM) or dubious Registry hacks either. Instead we call the "native" RtlGetVersion() function directly, instead of the "crippled" equivalent.
Anders#
FYI RtlGetVersion is affected by the compatibility tab so it might not report the real version.
LoRd_MuldeR#
Originally Posted by Anders View Post
FYI RtlGetVersion is affected by the compatibility tab so it might not report the real version.
In my test, "compatibility mode" was unable to spoil the result. You can test with:
RealOSVersion.2016-05-14.zip
Anders#
If it is not affected, why do you bother looping with VerifyVersionInfo?

Anyway, I have seen RtlGetVersion on Vista SP2 return 5.1.2600 if you set compatibility to XP (don't remember if AcLayers was involved though).

On Windows 8 RtlGetVersion returns the correct value but then a hook overwrites parts of the result:
call dword ptr [eax+54h] ds:002b:0056dfe4={ntdll!RtlGetVersion (77bf8b69)}
mov edi,eax
test edi,edi
js AcLayers!NS_WinXPSP3VersionLie::APIHook_RtlGetVersion+0x91 (5fc07c04)
cmp dword ptr [esi],11Ch
mov dword ptr [esi+4],5
mov dword ptr [esi+8],1
mov dword ptr [esi+0Ch],0A28h
mov dword ptr [esi+10h],2
and it got hooked because GetProcAddress is actually AcLayers!NS_WRPDllRegister::APIHook_GetProcAddress.



System::Call '*(&l4,i,i,i,i,&w128,i,i)p.r0'
System::Call 'KERNEL32::LoadLibrary(t "NTDLL")p.s'
System::Call 'KERNEL32::GetProcAddress(ps, m "RtlGetVersion")p.r9'
IntFmt $9 "0x%p" $9
System::Call 'KERNEL32::GetModuleHandleEx(i4,pr9,*p0r8)'
System::Call 'KERNEL32::GetModuleFileName(pr8,t.r7,i999)'
DetailPrint "RtlGetVersion=$9 is in $7"
System::Call '::$9(pr0)'
System::Call '*$0(i,i.r1,i.r2)'
System::Free $0
DetailPrint $1.$2 
LoRd_MuldeR#
Originally Posted by Anders View Post
If it is not affected, why do you bother looping with VerifyVersionInfo?
Because it is still required under some circumstances.

Windows 10 without compat mode:


Windows 10 with Win9x compat mode:


Windows 10 with WinXP compat mode:


In the last case even RtlGetVersion() lies, but RtlVerifyVersionInfo can reveal the expected result.

RealOSVersion.2016-05-15.zip
Anders#
RtlVerifyVersionInfo probably helps because its internal call to RtlGetVersion is not hooked, this might change in the future of course because MS will discover that people are doing this. Our only hope is that it is a layering violation for NTDLL to call back up to the shim dll's so they might let this one slide, fingers crossed...
th_mi#
Originally Posted by Anders View Post
RtlVerifyVersionInfo probably helps because its internal call to RtlGetVersion is not hooked, this might change in the future of course because MS will discover that people are doing this.
No RtlGetVersion is not an "internal" function. It is a well documented part of the Windows Driver Support routines:
The RtlVerifyVersionInfo routine compares a specified set of operating system version requirements to the corresponding attributes of the currently running version of the operating system.

The RtlGetVersion routine returns version information about the currently running operating system.


From what I remember this functions did not changed since eons. Currently I also do not expect this functions will be changed, Microsoft will surely not making the driver development more difficuilt. And since most drivers are capable to be used on more than one OS this functions are very essential for driver development.
Anders#
Originally Posted by th_mi View Post
No RtlGetVersion is not an "internal" function. It is a well documented part of the Windows Driver Support routines:
Where did I say that RtlGetVersion is an internal function? The RtlVerifyVersionInfo implementation calls RtlGetVersion to get the version info it uses when comparing the fields. Because this call happens inside ntdll it is not affected by the compatibility hooking that is forced on you when calling RtlGetVersion in your own code.

Originally Posted by th_mi View Post
Microsoft will surely not making the driver development more difficuilt. And since most drivers are capable to be used on more than one OS this functions are very essential for driver development.
While some functions exist both in kernel and user mode they don't have to share the same implementation. It would be possible for ntdll!RtlGetVersion to lie without affecting drivers! RtlGetVersion is only documented for driver use, the user mode version is technically undocumented and could even go away in the future.
Anders#
And as a cruel joke, here I'm breaking your GetRealOSVersion detection:




😁

@th_mi: Oh look, the user-mode implementation is not the same as the kernel implementation!