27

My objective is to detect Windows 10 in my code which has to work cross-platform as well as across different versions of Windows (atleast 7 & up). Windows provides IsWindows10OrGreater() to tackle this problem, but there's another issue with it, this function isn't present in previous windows versions.

You'll find countless blogs and SO questions regarding this as well as the manifest madness where functions like this and getversion and others return some different version rather than the correct one.

For example on my machine - the method IsWindows10OrGreater() doesn't compile(I would've to install Win10 SDK), and IsWindowsVersionOrGreater() reports 6 as major version.

So is there a sane multi-version way I could solve this problem?

IInspectable
  • 46,945
  • 8
  • 85
  • 181
hg_git
  • 2,884
  • 6
  • 24
  • 43
  • 2
    Any reason you aren't installing the Windows 10 SDK then? – IInspectable Apr 11 '16 at 08:26
  • @IInspectable my code is part of library that would work with different versions of windows. I've separate logic for win10 and rest windows versions. – hg_git Apr 11 '16 at 08:31
  • 1
    Uhm... no, you don't. Just define `_WIN32_WINNT`/`WINVER` to set the minimum supported target OS. See [Using the Windows Headers](https://msdn.microsoft.com/en-us/library/windows/desktop/aa383745.aspx) for details. You can use the Windows 10 SDK to target Windows Vista. – IInspectable Apr 11 '16 at 08:33
  • @IInspectable man I do have separate logic since 10 changed a few things under the hood. There's no choice and I'm not targeting previous versions. I've a code which will work with different logic acc to windows versions - either 10 or previous versions. – hg_git Apr 11 '16 at 08:48
  • 1
    So? Just get yourself the Windows 10 SDK and move on. Why do you believe, that this is not a solution? – IInspectable Apr 11 '16 at 08:50
  • 2
    @IInspectable my code would be shared among different applications, some of them(developers) might not add manifest, and many VS 2015 installations do not include win10 sdk by default - the way I found out about this problem. As I said, this isn't one time code, its part of a library which would be shared among different applications, some of them would be compiled under windows 7 or 8 or 10 and I've to detect 10 in order to separate my logic. – hg_git Apr 11 '16 at 08:54
  • Are you writing a library that you distribute as source and/or object code, and need a way to verify the OS version at runtime? In that case you should update your question to include the missing information. None of what you wrote even hints towards that, which makes it a completely different question. The consensus on **that** question would be to retrieve the version information of a well-known binary (e.g. kernel32.dll). – IInspectable Apr 11 '16 at 08:58
  • Do you mean this doesn't compile? `BOOL IsWindows10OrGreater() {return IsWinVersionOrGreater(0x0A00, 0);}` – Barmak Shemirani Apr 11 '16 at 09:01
  • 2
    @Barmak: It will compile, but to return the desired result, the binary needs the respective manifest entry that says: "Now look, I know that Windows 10 exists, and am well prepared for it." – IInspectable Apr 11 '16 at 09:02
  • @IInspectable edited the question. I'm writing it as another header file that will be included in desired project. – hg_git Apr 11 '16 at 09:02
  • @BarmakShemirani compiles but returns windows 8 by default on usual windows 10 installations. – hg_git Apr 11 '16 at 09:03

3 Answers3

59

The most straight-forward way to retrieve the true OS version is to call RtlGetVersion. It is what GetVersionEx and VerifyVersionInfo call, but doesn't employ the compatibility shims.

You can either use the DDK (by #including <ntddk.h> and linking against NtosKrnl.lib from kernel mode, or ntdll.lib from user mode), or use runtime dynamic linking as in the following snippet:

typedef LONG NTSTATUS, *PNTSTATUS;
#define STATUS_SUCCESS (0x00000000)

typedef NTSTATUS (WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW);

RTL_OSVERSIONINFOW GetRealOSVersion() {
    HMODULE hMod = ::GetModuleHandleW(L"ntdll.dll");
    if (hMod) {
        RtlGetVersionPtr fxPtr = (RtlGetVersionPtr)::GetProcAddress(hMod, "RtlGetVersion");
        if (fxPtr != nullptr) {
            RTL_OSVERSIONINFOW rovi = { 0 };
            rovi.dwOSVersionInfoSize = sizeof(rovi);
            if ( STATUS_SUCCESS == fxPtr(&rovi) ) {
                return rovi;
            }
        }
    }
    RTL_OSVERSIONINFOW rovi = { 0 };
    return rovi;
}

In case you need additional information you can pass an RTL_OSVERSIONINFOEXW structure in place of the RTL_OSVERSIONINFOW structure (properly assigning the dwOSVersionInfoSize member).

This returns the expected result on Windows 10, even when there is no manifest attached.


As an aside, it is commonly accepted as a better solution to provide different implementations based on available features rather than OS versions.
IInspectable
  • 46,945
  • 8
  • 85
  • 181
  • 3
    Er... how to detect features support? – Cu2S Apr 12 '16 at 06:25
  • Depends on the feature. Which feature you want to detect? – Sunius Apr 12 '16 at 08:10
  • 4
    @Cu2S: It depends on the feature. If a feature was introduced as a new export (e.g. [GetDpiForMonitor](https://msdn.microsoft.com/en-us/library/windows/desktop/dn280510.aspx)) you can use runtime dynamic linking as in the code above. If a feature was added as a COM interface, you can call `QueryInterface` as usual. If a feature was introduced as a new parameter/flag to a function, use it and observe the return value. – IInspectable Apr 12 '16 at 08:10
  • 1
    and linking against **ntdll.lib** but not *NtosKrnl.lib* . and *doesn't employ the compatibility shims* not true - if we select in file properties compatible tab - run in compatible mode `RtlGetVersion` will be affected by this – RbMm Apr 16 '18 at 21:40
  • @RbMm: The [documentation](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/nf-wdm-rtlgetversion) disagrees with you. Now if you *actively* choose to run an application in compatibility mode, obviously you do want the application to see different OS version. That's not the same as the compatibility shim introduced for `GetVersionEx` and friends. – IInspectable Apr 16 '18 at 21:43
  • so simply wrong documentation. if we build for user mode - need use `ntdll.lib` ofcourse – RbMm Apr 16 '18 at 21:44
  • also look for this - [RtlGetVersion](https://msdn.microsoft.com/en-us/library/mt723418.aspx) - funny, yes ? – RbMm Apr 16 '18 at 21:46
  • 1
    Another "bug" in MS documentation: I've found out that `RTL_OSVERSIONINFOEXW` is available in `winnt.h`, so no need to get the DDK or to declare that structure manually! – gog May 17 '19 at 07:53
1

You can read real build number from the registry, and then infer Windows version from it. Your application does not need to have a manifest for this work: on my machine, it correctly detects OS build number as 10586. For example:

#include <Windows.h>
#include <sstream>

struct HKeyHolder
{
private:
    HKEY m_Key;

public:
    HKeyHolder() :
        m_Key(nullptr)
    {
    }

    HKeyHolder(const HKeyHolder&) = delete;
    HKeyHolder& operator=(const HKeyHolder&) = delete;

    ~HKeyHolder()
    {
        if (m_Key != nullptr)
            RegCloseKey(m_Key);
    }

    operator HKEY() const
    {
        return m_Key;
    }

    HKEY* operator&()
    {
        return &m_Key;
    }
};

bool IsRunningOnWindows10()
{
    HKeyHolder currentVersion;
    if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, LR"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)", 0, KEY_QUERY_VALUE, &currentVersion) != ERROR_SUCCESS)
        return false;

    DWORD valueType;
    BYTE buffer[256];
    DWORD bufferSize = 256;

    if (RegQueryValueExW(currentVersion, L"CurrentBuild", nullptr, &valueType, buffer, &bufferSize) != ERROR_SUCCESS)
        return false;

    if (valueType != REG_SZ)
        return false;

    int version;
    std::wistringstream versionStream(reinterpret_cast<wchar_t*>(buffer));
    versionStream >> version;

    return version > 9800;
}
Sunius
  • 2,789
  • 18
  • 30
  • 11
    The registry is not a public programming interface. Unless this key is officially documented, your code relies on an implementation detail, that can change at any time, without prior notice. If this is an official key, please add a link to the documentation. – IInspectable Apr 12 '16 at 08:12
0

IsWindows10OrGreater() from VersionHelpers.h

Check the notes at Version Helper functions on MSDN

File VersionHelpers.h is shipped with Windows 10 SDK, but it will work in previous versions, too. Just copy it to your development environment.

It's just a header-only defined small lib, which uses VerSetConditionMask and VerifyVersionInfoW functions, both available in WinAPI since Windows 2000.

Upd If you can not include manifest file with your source code, you can use simple hack: just get a version of any system dll, for example, kernel32.dll using GetFileVersionInfo function.

vladon
  • 8,158
  • 2
  • 47
  • 91
  • according to MSDN , the minimum supporting client is windows 10 itself. the app will fail to load on previous versions – David Haim Apr 11 '16 at 08:30
  • 3
    Oh boy... *"Just copy it to your development environment"*... Seriously? Besides, `IsWindows10OrGreater()` is **not** declared in the Windows 8.1 SDK (not really a surprise). – IInspectable Apr 11 '16 at 08:31
  • `IsWindows10OrGreater()` reports wrong windows version if manifest informations are not recognized. reports 6 as major instead of 10 on my machine with windows 10. – hg_git Apr 11 '16 at 08:33
  • @hg_git "returns false unless the application contains a manifest that includes a compatibility section that contains the GUID that designates Windows 10", did you add a manifest file? – vladon Apr 11 '16 at 08:34
  • @IInspectable ok, just get it from Windows 10 SDK and copy. Look inside it: it uses `VerSetConditionMask` and `VerifyVersionInfoW`, both available in Windows API since Windows 2000. – vladon Apr 11 '16 at 08:36
  • Recommending to *"copy it to your development environment"* is a recipe for disaster. Are you going to tell every single one of your 250 developers on your team to do that? What about SDK updates? Who is going to remember to remove or copy this file back? Where's the official compatibility statement from the vendor? Is this even legal? – IInspectable Apr 11 '16 at 08:47
  • @vladon my code would be shared among different applications, some of them might not add manifest, and many VS 2015 installations do not include win10 sdk by default - the way I found out about this problem. – hg_git Apr 11 '16 at 08:51
  • @hg_git Just include it with your source code, and that's all – vladon Apr 11 '16 at 09:03
  • @IInspectable It is not a recipe for disaster. MS wrote this on the link: "This file ***can be used*** with other Microsoft Visual Studio releases to implement the same functionality for Windows versions prior to Windows 8.1." (my emphasis) – vladon Apr 11 '16 at 09:04
  • @vladon I just cannot include windows 10 manifest file with my source code. Then what's the point for compiling for different windows versions if my code is forcing it to detect as windows 10? – hg_git Apr 11 '16 at 09:05
  • @hg_git "and many VS 2015 installations do not include win10 sdk by default" - ALL VS 2015 installations do not include even C++ compiler by default, and how you deal with it? – vladon Apr 11 '16 at 09:05
  • @vladon if someone is including my source code within his/her c++ application, I could probably assume he/she would have the c++ compiler. – hg_git Apr 11 '16 at 09:07
  • @hg_git I've added an update, you can check file version of system dll. – vladon Apr 11 '16 at 09:10
  • @hg_git "I could probably assume he/she would have the c++ compiler", but to compile an app which is using Windows API calls, one must have Windows SDK (for include files) – vladon Apr 11 '16 at 09:13
  • @vladon man this has become a mess. I'll try to clear. I have a library, assume it as a source file which has separate logic for windows 10 & previous windows versions. I need to make sure that it compiles on previous windows versions and not just on windows 10. So version helper function is bad here since it requires win 10 as min requirements. And getversion and getversionex function are deprecated and returns wrong result unless the code is manifested with windows version information. Now manifest file isn't required so many users don't provide it. That's it. – hg_git Apr 11 '16 at 09:13
  • @vladon windows include files would be installed with compiler, **windows 10 sdk** might not be. – hg_git Apr 11 '16 at 09:14
  • 1
    @hg_git You search for a problems where they did not exist. Just copy the file from Windows SDK or add a requirement to install Windows 10 SDK to use your source code. That's all. – vladon Apr 11 '16 at 09:24
  • 3
    @vladon: The function does not work as expected by itself. You also have to provide a manifest, which you cannot enforce when writing library code. – IInspectable Apr 11 '16 at 09:26
  • 6
    You don't "just copy" a header. Don't you consider reproducible build environments? If it needs manual setup, it's not a solution. No one else will be able to build unless they follow some custom instructions. You can't do this and have it work on any developer's system or on your CI systems. Also, you polluted your build environment by making it non-standard; now you might introduce assumptions into your code which rely on your special custom setup. Keep it simple, and don't make such rash changes. – Roger Leigh Apr 13 '16 at 14:27
  • https://learn.microsoft.com/en-us/windows/win32/api/versionhelpers/nf-versionhelpers-iswindows10orgreater: "Applications not manifested for Windows 10 return false, even if the current operating system version is Windows 10. To manifest your applications for Windows 10, see Targeting your application for Windows." – Solomon Ucko Feb 19 '22 at 15:56