5

I am trying to find the build version of Windows Server 2016 machines, for example RS1 or RS3. There was an API to do this - GetVersionEx() - but is now deprecated.

MSDN says to use Version Helper Functions instead.

I want the build version, for ex: 1607 for RS1.

Is there an API to get this?

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
anon
  • 367
  • 1
  • 4
  • 18
  • Use the version helper functions – David Heffernan Nov 30 '17 at 19:30
  • Is there a way to get the version or not? If not why is that not clearly stated anywhere? Like I said I don't want the helper version stuff. How am I suppose to differentiate between the WS 2016 RS1 and RS3 image. – anon Nov 30 '17 at 19:33
  • See [Operating System Version](https://msdn.microsoft.com/en-us/library/windows/desktop/ms724832(v=vs.85).aspx) and [Version Helper functions](https://msdn.microsoft.com/en-us/library/windows/desktop/dn424972(v=vs.85).aspx). – Richard Chambers Nov 30 '17 at 19:34
  • Already looked at that. IsWindowsServer() doesn't solve my problem. – anon Nov 30 '17 at 19:35
  • From [Targeting your application for Windows](https://msdn.microsoft.com/en-us/library/windows/desktop/dn481241(v=vs.85).aspx) there is this function [VerifyVersionInfo function](https://msdn.microsoft.com/en-us/library/windows/desktop/ms725492(v=vs.85).aspx) though in "Windows 10, the VerifyVersionInfo function has also been deprecated." – Richard Chambers Nov 30 '17 at 19:43
  • The version helper functions dont return the actual version number, like `GetVersion/Ex()` does. They compare the version number to a requested version and return the result of the comparison. To learn the exact version number, you would have to make several comparisons, which is tedious. – Remy Lebeau Nov 30 '17 at 20:53
  • 4
    If you need to discover the exact version number, you can use [`RtlGetVersion()`](https://msdn.microsoft.com/en-us/library/windows/hardware/ff561910.aspx), [`NetServerGetInfo()`](https://msdn.microsoft.com/en-us/library/windows/desktop/aa370624.aspx) or [`NetWkstaGetInfo()`](https://msdn.microsoft.com/en-us/library/windows/desktop/aa370663.aspx), which are not deprecated or subject to manifestation (yet?). Or, [do what MSDN suggests](https://msdn.microsoft.com/en-us/library/windows/desktop/ms724429.aspx). – Remy Lebeau Nov 30 '17 at 20:55
  • 2
    This seems like an X/Y problem. What do you want the version number for? – Mike Vine Nov 30 '17 at 20:57
  • 1
    RtlGetVersion works for me. Loaded the ntdll.dll and got the RtlGetVersion. %HMODULE ntDll = GetModuleHandleW(L"ntdll.dll"); rtlGetVersion getVersion = (rtlGetVersion)GetProcAddress(ntDll, "RtlGetVersion"); % – anon Dec 01 '17 at 22:43

1 Answers1

9

Option 0: (per RbMm) Use [RtlGetVersion] from the driver development kit.

Option 1: [Updated] Grab the version number of a system DLL like kernel32.dll. MSDN used to bless this approach, saying:

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\\ProductVersion subblock of the file version information. [From an Internet Archive snapshot of MSDN circa 2017]

That would look something like this:

// Quick hack without error checking.
#include <cassert>
#include <iomanip>
#include <iostream>
#include <vector>
#include <Windows.h>

int main() {
  const auto system = L"kernel32.dll";
  DWORD dummy;
  const auto cbInfo =
      ::GetFileVersionInfoSizeExW(FILE_VER_GET_NEUTRAL, system, &dummy);
  std::vector<char> buffer(cbInfo);
  ::GetFileVersionInfoExW(FILE_VER_GET_NEUTRAL, system, dummy,
                          buffer.size(), &buffer[0]);
  void *p = nullptr;
  UINT size = 0;
  ::VerQueryValueW(buffer.data(), L"\\", &p, &size);
  assert(size >= sizeof(VS_FIXEDFILEINFO));
  assert(p != nullptr);
  auto pFixed = static_cast<const VS_FIXEDFILEINFO *>(p);
  std::cout << HIWORD(pFixed->dwFileVersionMS) << '.'
            << LOWORD(pFixed->dwFileVersionMS) << '.'
            << HIWORD(pFixed->dwFileVersionLS) << '.'
            << LOWORD(pFixed->dwFileVersionLS) << '\n';

  return 0;
}

Note that the original MSDN link now redirects to a newer documentation set that doesn't mention this approach. I suppose that means this is no longer a supported technique, and, presumably, all the compatibility hacks for older code might prevent an application from getting the actual answer.

Option 2: Query the registry, specifically:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion

which has values for CurrentMajorVersionNumber, CurrentMinorVersionNumber, and CurrentBuildNumber.

I can't find official documentation for these values, so this may not be MSDN-approved or future-proof.

Option 3: Use GetProductInfo if available and fall back to GetVersionInfo if it's not.

Adrian McCarthy
  • 45,555
  • 16
  • 123
  • 175
  • why not use option 0 - call `RtlGetVersion` or undocumented `RtlGetNtVersionNumbers`. anyway this more simply and fast compare this way – RbMm Nov 30 '17 at 22:19
  • @RbMm So `RtlGetVersion` does not lie like `GetVersionEx` if you don't have the latest `Compatibility` entries in manifest? – zett42 Nov 30 '17 at 22:25
  • 3
    @zett42 - no, `RtlGetVersion` until always return true windows version, and not look for manifest – RbMm Nov 30 '17 at 22:30
  • @zett42 - however the latest build windows number, where i look this is *15063* (and here RtlGetVersion still read correct values from PEB), not look more resent builds. interesting [link](https://github.com/reswitched/newlib/blob/master/winsup/cygwin/wincap.cc#L138), but no more resent build under hand now – RbMm Nov 30 '17 at 22:47
  • @RbMm I think RtlGetVersion is affected by the compatibility tab settings on a .exe. – Anders Nov 30 '17 at 23:22
  • @Anders - you can look binary code of `RtlGetVersion` (under debugger or disassembler) - it enough simply - unconditionally read this values from process `PEB` . however i not view the latest build, but on 15063 stil true version – RbMm Nov 30 '17 at 23:25
  • 1
    @RbMm And who fills the PEB (You can mess with the PE file to set some of these values)? Anyway, looking at the code is pointless because those functions get hooked by aclayers.dll. – Anders Nov 30 '17 at 23:27
  • @Anders yes :) you was correct. the `RtlGetVersion` is hooked by *aclayer.dll* and return "compatible" version. but `RtlGetNtVersionNumbers` not hooked and return correct version number. in the process PEB also real values – RbMm Nov 30 '17 at 23:34
  • @RbMm But you can hex-edit the PE file so that those PEB fields have whatever number you want. I'm sure it is only a matter of time until RtlGetNtVersionNumbers is hooked as well. – Anders Nov 30 '17 at 23:38
  • @Anders - no, *PEB* this is not part of pe file but semi-documented structure in memory (process environment block) - allocated by kernel in process creation time. think here always must be correct values, which need for internal system work. are `RtlGetNtVersionNumbers` will be hooked - don't know. may be yes. interesting that before win10 this function simply return values hard-coded in *ntdll.dll*. now (win 10) it read it from peb – RbMm Nov 30 '17 at 23:42
  • @RbMM I know perfectly well what the PEB is. The PEB **can** be filled with values from the PE file if you tweak it correctly. – Anders Nov 30 '17 at 23:46
  • @Anders - what you mean ? that ntdll or shim under some case can read major/minor values from exe optionalheader and write it to peb ? – RbMm Nov 30 '17 at 23:49
  • @RbMm IMAGE_LOAD_CONFIG_DIRECTORY::CSDVersion (if not zero) is written to the PEB etc. I assume it is ntdll that does it when you create the process. The major/minor version PE fields are somewhere else and not documented AFAIK. – Anders Nov 30 '17 at 23:51
  • @Anders - dont know. even if use not 0 *MajorVersion*, *MinorVersion*, *CSDVersion* i view under peb real os values. however may be i not set some flags/etc. not research this early, but until not view visible effect from play with `IMAGE_LOAD_CONFIG_DIRECTORY` in this direction. however this already some offtopic for original question - the `IMAGE_LOAD_CONFIG_DIRECTORY` control who build of exe. if it patched.. but any code can be patched. – RbMm Dec 01 '17 at 00:04
  • 1
    " MSDN blesses this approach" I don't see anything on the linked page that discusses using the build number of kernel32.dll as being "blessed" or otherwise endorsed. Maybe it was previously discussed, but not as of this writing. I've had cases where the build number of kernel32.dll was different from what's reported via winver.exe. – Charles Oppermann Dec 02 '19 at 16:19
  • 1
    @CharlesOppermann: The MSDN link is now redirected to Microsoft's newer documentation set, and the newer version doesn't have it. I'll update the answer with the relevant bit as captured by the Wayback Machine. – Adrian McCarthy Dec 02 '19 at 20:54
  • Great there's `RtlGetVersion`! Requiring installing DDK just for one function may be overkill, though. Can you integrate the answer pointing to [this](https://gist.github.com/myfreeer/2179898828fe8bc062bb51722a80514f) gist that has just the useful declarations? – ceztko Jan 16 '21 at 20:50