c# How to use the new Version Helper API

牧云@^-^@ 提交于 2020-12-05 06:22:13

问题


Since OSVersion is now not reliable since Windows 10 has been released (this function reports Windows 8 for Windows 10), I'm attempting to use the new Version Helper API functions in my C# application. Here they are.

I apologize if this is simply a issue with my DLL import, but here is my attempt to pull in these new methods to detect the OS correctly.

[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern bool IsWindows7OrGreater();

[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern bool IsWindows8OrGreater();

[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern bool IsWindows8Point1OrGreater();

[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern bool IsWindows10OrGreater();

Whenever I'm calling these methods, I'm getting:

Exception is: EntryPointNotFoundException - Unable to find an entry point named 'IsWindows7OrGreater' in DLL 'kernel32.dll'.

Am I doing something wrong? Anyone have any ideas? Thanks for any help!

EDIT: Please see the accepted answer and take a look at this code project for a good start on porting these methods over to C#.


回答1:


Unfortunately, it's a little more complicated than that. The "functions" are actually macros defined in VersionHelpers.h.

If you think of it, that's the only way to do it - they can't add functions to older Windows versions retroactively.

You have to port the macros over to C#.




回答2:


Your code must have a Manifest that contains

  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
    <application>
        <!-- A list of all Windows versions that this application is designed to work with. Windows will automatically select the most compatible environment.--> 
        <!-- 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}"/>
        <!-- If your application is designed to work with Windows 7, uncomment the following supportedOS node-->
      <!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>-->

    </application>
  </compatibility>

This tells the OS your code supports the newer OS

Use the following class

using System;
using System.Runtime.InteropServices;

namespace VersionHelper
{
    public static class VersionHelper
    {
        const byte VER_EQUAL = 1;
        const byte VER_GREATER = 2;
        const byte VER_GREATER_EQUAL = 3;
        const byte VER_LESS = 4;
        const byte VER_LESS_EQUAL = 5;
        const byte VER_AND = 6;
        const byte VER_OR = 7;

        const byte VER_CONDITION_MASK = 7;
        const byte VER_NUM_BITS_PER_CONDITION_MASK = 3;

        //
        // RtlVerifyVersionInfo() type mask bits
        //

        const uint VER_MINORVERSION = 0x0000001;
        const uint VER_MAJORVERSION = 0x0000002;
        const uint VER_BUILDNUMBER = 0x0000004;
        const uint VER_PLATFORMID = 0x0000008;
        const uint VER_SERVICEPACKMINOR = 0x0000010;
        const uint VER_SERVICEPACKMAJOR = 0x0000020;
        const uint VER_SUITENAME = 0x0000040;
        const uint VER_PRODUCT_TYPE = 0x0000080;

        // wProductType    
        // Any additional information about the system.This member can be one of the following values.
        const byte VER_NT_DOMAIN_CONTROLLER = 0x0000002;
        const byte VER_NT_SERVER = 0x0000003;
        const byte VER_NT_WORKSTATION = 0x0000001;

        //
        // _WIN32_WINNT version constants
        //
        const ushort _WIN32_WINNT_NT4 = 0x0400;
        const ushort _WIN32_WINNT_WIN2K = 0x0500;
        const ushort _WIN32_WINNT_WINXP = 0x0501;
        const ushort _WIN32_WINNT_WS03 = 0x0502;
        const ushort _WIN32_WINNT_WIN6 = 0x0600;
        const ushort _WIN32_WINNT_VISTA = 0x0600;
        const ushort _WIN32_WINNT_WS08 = 0x0600;
        const ushort _WIN32_WINNT_LONGHORN = 0x0600;
        const ushort _WIN32_WINNT_WIN7 = 0x0601;
        const ushort _WIN32_WINNT_WIN8 = 0x0602;
        const ushort _WIN32_WINNT_WINBLUE = 0x0603;
        const ushort _WIN32_WINNT_WINTHRESHOLD = 0x0A00; /* ABRACADABRA_THRESHOLD*/
        const ushort _WIN32_WINNT_WIN10 = 0x0A00; /* ABRACADABRA_THRESHOLD*/

        const bool FALSE = false;

        static byte LOBYTE(ushort w)
        {
            return ((byte)(w & 0xff));
        }

        static byte HIBYTE(ushort w)
        {
            return ((byte)(w >> 8 & 0xff));
        }


        [DllImport("kernel32.dll")]
        static extern ulong VerSetConditionMask(ulong ConditionMask, uint TypeMask,   byte  Condition );

        [DllImport("kernel32.dll")]
        static extern bool VerifyVersionInfoW(ref OSVERSIONINFOEXW lpVersionInformation, uint dwTypeMask, ulong dwlConditionMask);


        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        struct OSVERSIONINFOEXW
        {
            public int dwOSVersionInfoSize;
            public int dwMajorVersion;
            public int dwMinorVersion;
            public int dwBuildNumber;
            public int dwPlatformId;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
            public string szCSDVersion;
            public UInt16 wServicePackMajor;
            public UInt16 wServicePackMinor;
            public UInt16 wSuiteMask;
            public byte wProductType;
            public byte wReserved;
        }


        public static bool
        IsWindowsVersionOrGreater(ushort wMajorVersion, ushort wMinorVersion, ushort wServicePackMajor)
        {
            var osvi = new OSVERSIONINFOEXW
            {
                dwOSVersionInfoSize = Marshal.SizeOf(typeof(OSVERSIONINFOEXW))
            };

            var dwlConditionMask = VerSetConditionMask(
                VerSetConditionMask(
                VerSetConditionMask(
                    0, VER_MAJORVERSION, VER_GREATER_EQUAL),
                       VER_MINORVERSION, VER_GREATER_EQUAL),
                       VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);

            osvi.dwMajorVersion = wMajorVersion;
            osvi.dwMinorVersion = wMinorVersion;
            osvi.wServicePackMajor = wServicePackMajor;

            return VerifyVersionInfoW(ref osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask) != FALSE;
        }

        public static bool
        IsWindowsXPOrGreater()
        {
            return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 0);
        }

        public static bool
        IsWindowsXPSP1OrGreater()
        {
            return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 1);
        }

        public static bool
        IsWindowsXPSP2OrGreater()
        {
            return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 2);
        }

        public static bool
        IsWindowsXPSP3OrGreater()
        {
            return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 3);
        }

        public static bool
        IsWindowsVistaOrGreater()
        {
            return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), LOBYTE(_WIN32_WINNT_VISTA), 0);
        }

        public static bool
        IsWindowsVistaSP1OrGreater()
        {
            return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), LOBYTE(_WIN32_WINNT_VISTA), 1);
        }

        public static bool
        IsWindowsVistaSP2OrGreater()
        {
            return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), LOBYTE(_WIN32_WINNT_VISTA), 2);
        }

        public static bool
        IsWindows7OrGreater()
        {
            return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN7), LOBYTE(_WIN32_WINNT_WIN7), 0);
        }

        public static bool
        IsWindows7SP1OrGreater()
        {
            return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN7), LOBYTE(_WIN32_WINNT_WIN7), 1);
        }

        public static bool
        IsWindows8OrGreater()
        {
            return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN8), LOBYTE(_WIN32_WINNT_WIN8), 0);
        }

        public static bool
        IsWindows8Point1OrGreater()
        {
            return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINBLUE), LOBYTE(_WIN32_WINNT_WINBLUE), 0);
        }

        public static bool
        IsWindowsThresholdOrGreater()
        {
            return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINTHRESHOLD), LOBYTE(_WIN32_WINNT_WINTHRESHOLD), 0);
        }

        public static bool
        IsWindows10OrGreater()
        {
            return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINTHRESHOLD), LOBYTE(_WIN32_WINNT_WINTHRESHOLD), 0);
        }

        public static bool
        IsWindowsServer()
        {
            var osvi = new OSVERSIONINFOEXW
            {
                dwOSVersionInfoSize = Marshal.SizeOf(typeof(OSVERSIONINFOEXW)),
                wProductType = VER_NT_WORKSTATION
            };

            var dwlConditionMask = VerSetConditionMask(0, VER_PRODUCT_TYPE, VER_EQUAL);

            return !VerifyVersionInfoW( ref osvi, VER_PRODUCT_TYPE, dwlConditionMask);
        }
    }
}



回答3:


VersionHelpers functions are not exported in any dll, but rather defined in VersionHelpers.h file, in order to utilize the functionality in your C# code you can copy the functionality from the header file,

Import those 2 functions:

[DllImport("kernel32.dll")]
static extern ulong VerSetConditionMask(ulong dwlConditionMask, uint dwTypeBitMask, byte dwConditionMask);

[DllImport("kernel32.dll")]
static extern bool VerifyVersionInfo([In] ref OsVersionInfoEx lpVersionInfo, uint dwTypeMask, ulong dwlConditionMask);

Define the following structure:

[StructLayout(LayoutKind.Sequential)]
struct OsVersionInfoEx
{
    public uint OSVersionInfoSize;
    public uint MajorVersion;
    public uint MinorVersion;
    public uint BuildNumber;
    public uint PlatformId;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
    public string CSDVersion;
    public ushort ServicePackMajor;
    public ushort ServicePackMinor;
    public ushort SuiteMask;
    public byte ProductType;
    public byte Reserved;
}

Then use the following function:

static bool IsWindowsVersionOrGreater(uint majorVersion, uint minorVersion, ushort servicePackMajor)
{
    OsVersionInfoEx osvi = new OsVersionInfoEx();
    osvi.OSVersionInfoSize = (uint)Marshal.SizeOf(osvi);
    osvi.MajorVersion = majorVersion;
    osvi.MinorVersion = minorVersion;
    osvi.ServicePackMajor = servicePackMajor;
    // These constants initialized with corresponding definitions in
    // winnt.h (part of Windows SDK)
    const uint VER_MINORVERSION = 0x0000001;
    const uint VER_MAJORVERSION = 0x0000002;
    const uint VER_SERVICEPACKMAJOR = 0x0000020;
    const byte VER_GREATER_EQUAL = 3;
    ulong versionOrGreaterMask = VerSetConditionMask(
       VerSetConditionMask(
           VerSetConditionMask(
               0, VER_MAJORVERSION, VER_GREATER_EQUAL),
           VER_MINORVERSION, VER_GREATER_EQUAL),
       VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
    uint versionOrGreaterTypeMask = VER_MAJORVERSION |VER_MINORVERSION | VER_SERVICEPACKMAJOR;
    return VerifyVersionInfo(ref osvi, versionOrGreaterTypeMask, versionOrGreaterMask);

}




回答4:


There's no need to work with interop/unmanaged code or add a target manifest.

Get Windows OS version with WMI




回答5:


Like already mentioned here and on GetVersionEx and Environment.OSVersion docs, GetVersionEx behavior/availability changed with Windows 8.1. (And for .NET < 5.0, Environment.OSVersion reflects this) The same applies to the VerifyVersionInfo function. If you use .NET 5 or Core, you can just use Environment.OSVersion again and by the way, my colleague working on our Core project, excluded the code that was querying WMI using ManagementObjectSearcher with directives as it did not compile for Core )

An alternate way would be to use the PInvoke from other answers with the Windows SDK counterparts (from ntdll.dll) of these functions

  • RtlGetVersion ( can use either the OSVERSIONINFOW a OSVERSIONINFOEXW structure, which are aliases for non-SDK counterparts. Just be sure to set the dwOSVersionInfoSize before calling it so it knows which one you are referencing. )
  • RtlVerifyVersionInfo

They use the same structs as the functions from kernel23.dll, and can be used the same way (also using the VerSetConditionMask function as substitute for the documented VER_SET_CONDITION macro but only Unicode versions are available, so be sure to annotate the structs with StructLayoutAttribute with Charset set to CharSet.Unicode as C# defaults to ANSI)

So code already posted should give reliable results if the following imports are used instead:

(In case you can't or don't want to use WMI or the registry)

            [DllImport("kernel32.dll")]
            private static extern ulong VerSetConditionMask(ulong dwlConditionMask, uint dwTypeBitMask, byte dwConditionMask);

            [DllImport("ntdll.dll")]
            private static extern uint RtlGetVersion(ref OSVERSIONINFOW lpVersionInformation);

            [DllImport("ntdll.dll")]
            private static extern uint RtlGetVersion(ref OSVERSIONINFOEXW lpVersionInformation);

            [DllImport("ntdll.dll")]
            private static extern bool RtlVerifyVersionInfo([In] ref OSVERSIONINFOEXW lpVersionInformation, uint dwTypeMask, ulong dwlConditionMask);


来源:https://stackoverflow.com/questions/31550444/c-sharp-how-to-use-the-new-version-helper-api

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!