diff --git a/CHANGES.md b/CHANGES.md index 8f650c128e..3a8380af0c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,11 +2,12 @@ NOTE: as of JNA 4.0, JNA is now dual-licensed under LGPL and AL 2.0 (see LICENSE NOTE: JNI native support is typically incompatible between minor versions, and almost always incompatible between major versions. -Next release (5.2.1) +Next release (5.3.0) ==================== Features -------- +* [#1050](https://github.com/java-native-access/jna/pull/1050): Add `c.s.j.p.win32.VersionHelpers` and supporting functions - [@dbwiddis](https://github.com/dbwiddis). Bug Fixes --------- diff --git a/contrib/platform/src/com/sun/jna/platform/win32/Kernel32.java b/contrib/platform/src/com/sun/jna/platform/win32/Kernel32.java index 6efed8300b..e012b1349e 100644 --- a/contrib/platform/src/com/sun/jna/platform/win32/Kernel32.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/Kernel32.java @@ -1413,6 +1413,73 @@ boolean CreateProcessW(String lpApplicationName, char[] lpCommandLine, */ boolean GetVersionEx(OSVERSIONINFOEX lpVersionInfo); + /** + * Compares a set of operating system version requirements to the + * corresponding values for the currently running version of the system. + * This function is subject to manifest-based behavior. + * + * @param lpVersionInformation + * A pointer to an {@link WinNT#OSVERSIONINFOEX} structure + * containing the operating system version requirements to + * compare. The {@code dwTypeMask} parameter indicates the + * members of this structure that contain information to compare. + *
+ * You must set the {@code dwOSVersionInfoSize} member of this + * structure to {@code sizeof(OSVERSIONINFOEX)}. You must also + * specify valid data for the members indicated by + * {@code dwTypeMask}. The function ignores structure members for + * which the corresponding {@code dwTypeMask} bit is not set. + * @param dwTypeMask + * A mask that indicates the members of the + * {@link WinNT#OSVERSIONINFOEX} structure to be tested. + * @param dwlConditionMask + * The type of comparison to be used for each + * {@code lpVersionInfo} member being compared. To build this + * value, call the {@link #VerSetConditionMask} function once for + * each {@link WinNT#OSVERSIONINFOEX} member being compared. + * @return If the currently running operating system satisfies the specified + * requirements, the return value is a nonzero value. + *
+ * If the current system does not satisfy the requirements, the + * return value is zero and {@link #GetLastError()} returns + * {@link WinError#ERROR_OLD_WIN_VERSION}. + *
+ * If the function fails, the return value is zero and + * {@link #GetLastError()} returns an error code other than + * {@link WinError#ERROR_OLD_WIN_VERSION}. + */ + boolean VerifyVersionInfoW(OSVERSIONINFOEX lpVersionInformation, int dwTypeMask, long dwlConditionMask); + + /** + * Sets the bits of a 64-bit value to indicate the comparison operator to + * use for a specified operating system version attribute. This function is + * used to build the {@code dwlConditionMask} parameter of the + * {@link #VerifyVersionInfo} function. + * + * @param conditionMask + * A value to be passed as the {@code dwlConditionMask} parameter + * of the {@link #VerifyVersionInfo} function. The function + * stores the comparison information in the bits of this + * variable. + *
+ * Before the first call to {@link #VerSetConditionMask}, + * initialize this variable to zero. For subsequent calls, pass + * in the variable used in the previous call. + * @param typeMask + * A mask that indicates the member of the + * {@link WinNT#OSVERSIONINFOEX} structure whose comparison + * operator is being set. This value corresponds to one of the + * bits specified in the {@code dwTypeMask} parameter for the + * {@link #VerifyVersionInfo} function. + * @param condition + * The operator to be used for the comparison. The + * {@link #VerifyVersionInfo} function uses this operator to + * compare a specified attribute value to the corresponding value + * for the currently running system. + * @return The function returns the condition mask value. + */ + long VerSetConditionMask(long conditionMask, int typeMask, byte condition); + /** * The GetSystemInfo function returns information about the current system. * diff --git a/contrib/platform/src/com/sun/jna/platform/win32/VersionHelpers.java b/contrib/platform/src/com/sun/jna/platform/win32/VersionHelpers.java new file mode 100644 index 0000000000..ae5b9aba27 --- /dev/null +++ b/contrib/platform/src/com/sun/jna/platform/win32/VersionHelpers.java @@ -0,0 +1,218 @@ +/* Copyright (c) 2019 Daniel Widdis, All Rights Reserved + * + * The contents of this file is dual-licensed under 2 + * alternative Open Source/Free licenses: LGPL 2.1 or later and + * Apache License 2.0. (starting with JNA version 4.0.0). + * + * You can freely decide which license you want to apply to + * the project. + * + * You may obtain a copy of the LGPL License at: + * + * http://www.gnu.org/licenses/licenses.html + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "LGPL2.1". + * + * You may obtain a copy of the Apache License at: + * + * http://www.apache.org/licenses/ + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "AL2.0". + */ +package com.sun.jna.platform.win32; + +import com.sun.jna.platform.win32.WinDef.DWORD; +import com.sun.jna.platform.win32.WinDef.WORD; +import com.sun.jna.platform.win32.WinNT.OSVERSIONINFOEX; + +/** + * The following functions can be used to determine the current operating system + * version or identify whether it is a Windows or Windows Server release. These + * functions provide simple tests that use the VerifyVersionInfo function and + * the recommended greater than or equal to comparisons that are proven as a + * robust means to determine the operating system version. + */ +public class VersionHelpers { + /* + * Code in this class is an attempt to faithfully port the inline macros in + * the versionhelpers.h header file of the Windows 10 SDK. + */ + + /** + * This function is useful in confirming a version of Windows Server that + * doesn't share a version number with a client release. You should only use + * this function if the other provided version helper functions do not fit + * your scenario. + * + * @param wMajorVersion + * The major version to test + * @param wMinorVersion + * The minor version to test + * @param wServicePackMajor + * The service pack to test + * @return True if the current OS version matches, or is greater than, the + * provided version information. + */ + public static boolean IsWindowsVersionOrGreater(int wMajorVersion, int wMinorVersion, int wServicePackMajor) { + OSVERSIONINFOEX osvi = new OSVERSIONINFOEX(); + osvi.dwOSVersionInfoSize = new DWORD(osvi.size()); + osvi.dwMajorVersion = new DWORD(wMajorVersion); + osvi.dwMinorVersion = new DWORD(wMinorVersion); + osvi.wServicePackMajor = new WORD(wServicePackMajor); + + long dwlConditionMask = 0; + dwlConditionMask = Kernel32.INSTANCE.VerSetConditionMask(dwlConditionMask, WinNT.VER_MAJORVERSION, + (byte) WinNT.VER_GREATER_EQUAL); + dwlConditionMask = Kernel32.INSTANCE.VerSetConditionMask(dwlConditionMask, WinNT.VER_MINORVERSION, + (byte) WinNT.VER_GREATER_EQUAL); + dwlConditionMask = Kernel32.INSTANCE.VerSetConditionMask(dwlConditionMask, WinNT.VER_SERVICEPACKMAJOR, + (byte) WinNT.VER_GREATER_EQUAL); + + return Kernel32.INSTANCE.VerifyVersionInfoW(osvi, + WinNT.VER_MAJORVERSION | WinNT.VER_MINORVERSION | WinNT.VER_SERVICEPACKMAJOR, dwlConditionMask); + } + + /* + * The constants Kernel32.WIN32_WINNT_* are 2-byte encodings of windows + * version numbers, for example Windows XP is version 5.1 and is encoded as + * 0x0501. To pass to IsWindowsVersionOrGreater, we pass the HIBYTE (e.g., + * 0x05) as the first argument and LOBYTE (e.g., 0x01) as the second. To get + * the high byte of a short, we shift right 8 bits and cast to byte, e.g., + * (byte) (word>>>8); to get the low byte wse simply cast to byte. + */ + + /** + * @return true if the current OS version matches, or is greater than, the + * Windows XP version. + */ + public static boolean IsWindowsXPOrGreater() { + return IsWindowsVersionOrGreater((byte) (Kernel32.WIN32_WINNT_WINXP >>> 8), (byte) Kernel32.WIN32_WINNT_WINXP, + 0); + } + + /** + * @return true if the current OS version matches, or is greater than, the + * Windows XP with Service Pack 1 (SP1) version. + */ + public static boolean IsWindowsXPSP1OrGreater() { + return IsWindowsVersionOrGreater((byte) (Kernel32.WIN32_WINNT_WINXP >>> 8), (byte) Kernel32.WIN32_WINNT_WINXP, + 1); + } + + /** + * @return true if the current OS version matches, or is greater than, the + * Windows XP with Service Pack 2 (SP2) version. + */ + public static boolean IsWindowsXPSP2OrGreater() { + return IsWindowsVersionOrGreater((byte) (Kernel32.WIN32_WINNT_WINXP >>> 8), (byte) Kernel32.WIN32_WINNT_WINXP, + 2); + } + + /** + * @return true if the current OS version matches, or is greater than, the + * Windows XP with Service Pack 3 (SP3) version. + */ + public static boolean IsWindowsXPSP3OrGreater() { + return IsWindowsVersionOrGreater((byte) (Kernel32.WIN32_WINNT_WINXP >>> 8), (byte) Kernel32.WIN32_WINNT_WINXP, + 3); + } + + /** + * @return true if the current OS version matches, or is greater than, the + * Windows Vista version. + */ + public static boolean IsWindowsVistaOrGreater() { + return IsWindowsVersionOrGreater((byte) (Kernel32.WIN32_WINNT_VISTA >>> 8), (byte) Kernel32.WIN32_WINNT_VISTA, + 0); + } + + /** + * @return true if the current OS version matches, or is greater than, the + * Windows Vista with Service Pack 1 (SP1) version. + */ + public static boolean IsWindowsVistaSP1OrGreater() { + return IsWindowsVersionOrGreater((byte) (Kernel32.WIN32_WINNT_VISTA >>> 8), (byte) Kernel32.WIN32_WINNT_VISTA, + 1); + } + + /** + * @return true if the current OS version matches, or is greater than, the + * Windows Vista with Service Pack 2 (SP2) version. + */ + public static boolean IsWindowsVistaSP2OrGreater() { + return IsWindowsVersionOrGreater((byte) (Kernel32.WIN32_WINNT_VISTA >>> 8), (byte) Kernel32.WIN32_WINNT_VISTA, + 2); + } + + /** + * @return true if the current OS version matches, or is greater than, the + * Windows 7 version. + */ + public static boolean IsWindows7OrGreater() { + return IsWindowsVersionOrGreater((byte) (Kernel32.WIN32_WINNT_WIN7 >>> 8), (byte) Kernel32.WIN32_WINNT_WIN7, 0); + } + + /** + * @return true if the current OS version matches, or is greater than, the + * Windows 7 with Service Pack 1 (SP1) version. + */ + public static boolean IsWindows7SP1OrGreater() { + return IsWindowsVersionOrGreater((byte) (Kernel32.WIN32_WINNT_WIN7 >>> 8), (byte) Kernel32.WIN32_WINNT_WIN7, 1); + } + + /** + * @return true if the current OS version matches, or is greater than, the + * Windows 8 version. + */ + public static boolean IsWindows8OrGreater() { + return IsWindowsVersionOrGreater((byte) (Kernel32.WIN32_WINNT_WIN8 >>> 8), (byte) Kernel32.WIN32_WINNT_WIN8, 0); + } + + /** + * @return true if the current OS version matches, or is greater than, the + * Windows 8.1 version. For Windows 8.1 and/or Windows 10, + * {@link #IsWindows8Point1OrGreater} returns false unless the + * application contains a manifest that includes a compatibility + * section that contains the GUIDs that designate Windows 8.1 and/or + * Windows 10. + */ + public static boolean IsWindows8Point1OrGreater() { + return IsWindowsVersionOrGreater((byte) (Kernel32.WIN32_WINNT_WINBLUE >>> 8), + (byte) Kernel32.WIN32_WINNT_WINBLUE, 0); + } + + /** + * @return true if the current OS version matches, or is greater than, the + * Windows 10 version. For Windows 10, + * {@link #IsWindows8Point1OrGreater} returns false unless the + * application contains a manifest that includes a compatibility + * section that contains the GUID that designates Windows 10. + */ + public static boolean IsWindows10OrGreater() { + return IsWindowsVersionOrGreater((byte) (Kernel32.WIN32_WINNT_WIN10 >>> 8), (byte) Kernel32.WIN32_WINNT_WIN10, + 0); + } + + /** + * Applications that need to distinguish between server and client versions + * of Windows should call this function. + * + * @return true if the current OS is a Windows Server release. + */ + public static boolean IsWindowsServer() { + // This should properly be OSVERSIONINFOEXW which is not defined in JNA. + // The OSVERSIONINFOEX structure in JNA is the (W) Unicode-compliant + // version. + OSVERSIONINFOEX osvi = new OSVERSIONINFOEX(); + osvi.dwOSVersionInfoSize = new DWORD(osvi.size()); + osvi.wProductType = WinNT.VER_NT_WORKSTATION; + + long dwlConditionMask = Kernel32.INSTANCE.VerSetConditionMask(0, WinNT.VER_PRODUCT_TYPE, + (byte) WinNT.VER_EQUAL); + + return !Kernel32.INSTANCE.VerifyVersionInfoW(osvi, WinNT.VER_PRODUCT_TYPE, dwlConditionMask); + } +} + diff --git a/contrib/platform/src/com/sun/jna/platform/win32/WinNT.java b/contrib/platform/src/com/sun/jna/platform/win32/WinNT.java index 71a2e64339..315f8e9b90 100644 --- a/contrib/platform/src/com/sun/jna/platform/win32/WinNT.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/WinNT.java @@ -2016,6 +2016,23 @@ public byte getProductType() { int VER_PLATFORM_WIN32_WINDOWS = 1; int VER_PLATFORM_WIN32_NT = 2; + /* + * WIN32_WINNT version constants + */ + short WIN32_WINNT_NT4 = 0x0400; // Windows NT 4.0 + short WIN32_WINNT_WIN2K = 0x0500; // Windows 2000 + short WIN32_WINNT_WINXP = 0x0501; // Windows XP + short WIN32_WINNT_WS03 = 0x0502; // Windows Server 2003 + short WIN32_WINNT_WIN6 = 0x0600; // Windows Vista + short WIN32_WINNT_VISTA = 0x0600; // Windows Vista + short WIN32_WINNT_WS08 = 0x0600; // Windows Server 2008 + short WIN32_WINNT_LONGHORN = 0x0600; // Windows Vista + short WIN32_WINNT_WIN7 = 0x0601; // Windows 7 + short WIN32_WINNT_WIN8 = 0x0602; // Windows 8 + short WIN32_WINNT_WINBLUE = 0x0603; // Windows 8.1 + short WIN32_WINNT_WINTHRESHOLD = 0x0A00; // Windows 10 + short WIN32_WINNT_WIN10 = 0x0A00; // Windows 10 + /** * Read the records sequentially. If this is the first read operation, the * EVENTLOG_FORWARDS_READ EVENTLOG_BACKWARDS_READ flags determines which diff --git a/contrib/platform/test/com/sun/jna/platform/win32/VersionHelpersTest.java b/contrib/platform/test/com/sun/jna/platform/win32/VersionHelpersTest.java new file mode 100644 index 0000000000..ccc93dd0b3 --- /dev/null +++ b/contrib/platform/test/com/sun/jna/platform/win32/VersionHelpersTest.java @@ -0,0 +1,89 @@ +/* Copyright (c) 2019 Daniel Widdis, All Rights Reserved + * + * The contents of this file is dual-licensed under 2 + * alternative Open Source/Free licenses: LGPL 2.1 or later and + * Apache License 2.0. (starting with JNA version 4.0.0). + * + * You can freely decide which license you want to apply to + * the project. + * + * You may obtain a copy of the LGPL License at: + * + * http://www.gnu.org/licenses/licenses.html + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "LGPL2.1". + * + * You may obtain a copy of the Apache License at: + * + * http://www.apache.org/licenses/ + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "AL2.0". + */ +package com.sun.jna.platform.win32; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public class VersionHelpersTest { + @Test + public void testVersionHelpers() { + // All windows versions should be higher than version 0.0! + assertTrue(VersionHelpers.IsWindowsVersionOrGreater(0, 0, 0)); + // All windows versions should be lower than version Short.MAX_VALUE! + assertFalse(VersionHelpers.IsWindowsVersionOrGreater(Short.MAX_VALUE, Short.MAX_VALUE, Short.MAX_VALUE)); + // These tests in order should be true until false; once false never + // true again + boolean lastVersionTest = true; + boolean versionTest = VersionHelpers.IsWindowsXPOrGreater(); + assertTrue((lastVersionTest == versionTest) || !versionTest); + + lastVersionTest = versionTest; + versionTest = VersionHelpers.IsWindowsXPSP1OrGreater(); + assertTrue((lastVersionTest == versionTest) || !versionTest); + + lastVersionTest = versionTest; + versionTest = VersionHelpers.IsWindowsXPSP2OrGreater(); + assertTrue((lastVersionTest == versionTest) || !versionTest); + + lastVersionTest = versionTest; + versionTest = VersionHelpers.IsWindowsXPSP3OrGreater(); + assertTrue((lastVersionTest == versionTest) || !versionTest); + + lastVersionTest = versionTest; + versionTest = VersionHelpers.IsWindowsVistaOrGreater(); + assertTrue((lastVersionTest == versionTest) || !versionTest); + + lastVersionTest = versionTest; + versionTest = VersionHelpers.IsWindowsVistaSP1OrGreater(); + assertTrue((lastVersionTest == versionTest) || !versionTest); + + lastVersionTest = versionTest; + versionTest = VersionHelpers.IsWindowsVistaSP2OrGreater(); + assertTrue((lastVersionTest == versionTest) || !versionTest); + + lastVersionTest = versionTest; + versionTest = VersionHelpers.IsWindows7OrGreater(); + assertTrue((lastVersionTest == versionTest) || !versionTest); + + lastVersionTest = versionTest; + versionTest = VersionHelpers.IsWindows7SP1OrGreater(); + assertTrue((lastVersionTest == versionTest) || !versionTest); + + lastVersionTest = versionTest; + versionTest = VersionHelpers.IsWindows8OrGreater(); + assertTrue((lastVersionTest == versionTest) || !versionTest); + + lastVersionTest = versionTest; + versionTest = VersionHelpers.IsWindows8Point1OrGreater(); + assertTrue((lastVersionTest == versionTest) || !versionTest); + + lastVersionTest = versionTest; + versionTest = VersionHelpers.IsWindows10OrGreater(); + assertTrue((lastVersionTest == versionTest) || !versionTest); + } +} +