-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Make ProcessInfo.operatingSystemVersionString useful on Linux and Windows #2824
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
a36bdd0
c526129
65e524d
af3bfa2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -80,11 +80,68 @@ open class ProcessInfo: NSObject { | |
return CFUUIDCreateString(kCFAllocatorSystemDefault, uuid)._swiftObject | ||
} | ||
|
||
#if os(Windows) | ||
internal var _rawOperatingSystemVersionInfo: RTL_OSVERSIONINFOEXW? { | ||
guard let ntdll = ("ntdll.dll".withCString(encodedAs: UTF16.self) { | ||
LoadLibraryExW($0, nil, DWORD(LOAD_LIBRARY_SEARCH_SYSTEM32)) | ||
}) else { | ||
return nil | ||
} | ||
defer { FreeLibrary(ntdll) } | ||
typealias RTLGetVersionTy = @convention(c) (UnsafeMutablePointer<RTL_OSVERSIONINFOEXW>) -> NTSTATUS | ||
guard let pfnRTLGetVersion = unsafeBitCast(GetProcAddress(ntdll, "RtlGetVersion"), to: Optional<RTLGetVersionTy>.self) else { | ||
return nil | ||
} | ||
var osVersionInfo = RTL_OSVERSIONINFOEXW() | ||
osVersionInfo.dwOSVersionInfoSize = DWORD(MemoryLayout<RTL_OSVERSIONINFOEXW>.size) | ||
guard pfnRTLGetVersion(&osVersionInfo) == 0 else { | ||
return nil | ||
} | ||
return osVersionInfo | ||
} | ||
#endif | ||
|
||
open var operatingSystemVersionString: String { | ||
let fallback = "Unknown" | ||
#if os(Linux) | ||
let version = try? String(contentsOf: URL(fileURLWithPath: "/proc/version_signature", isDirectory: false), encoding: .utf8) | ||
return version ?? fallback | ||
var utsNameBuffer = utsname() | ||
guard uname(&utsNameBuffer) == 0 else { | ||
return fallback | ||
} | ||
let release = withUnsafePointer(to: &utsNameBuffer.release.0) { String(cString: $0) } | ||
gwynne marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
return release | ||
gwynne marked this conversation as resolved.
Show resolved
Hide resolved
|
||
#elseif os(Windows) | ||
guard let osVersionInfo = self._rawOperatingSystemVersionInfo else { | ||
return fallback | ||
} | ||
|
||
// Windows has no canonical way to turn the fairly complex `RTL_OSVERSIONINFOW` version info into a string. We | ||
// do our best here to construct something consistent. Unfortunately, to provide a useful result, this requires | ||
// hardcoding several of the somewhat ambiguous values in the table provided here: | ||
// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_osversioninfoexw#remarks | ||
var versionString = "" | ||
switch (osVersionInfo.dwMajorVersion, osVersionInfo.dwMinorVersion) { | ||
case (5, 0): versionString += "Windows 2000" | ||
case (5, 1): versionString += "Windows XP" | ||
case (5, 2) where osVersionInfo.wProductType == VER_NT_WORKSTATION: versionString += "Windows XP Professional x64" | ||
case (5, 2) where osVersionInfo.wSuiteMask == VER_SUITE_WH_SERVER: versionString += "Windows Home Server" | ||
case (5, 2): versionString += "Windows Server 2003" | ||
case (6, 0) where osVersionInfo.wProductType == VER_NT_WORKSTATION: versionString += "Windows Vista" | ||
case (6, 0): versionString += "Windows Server 2008" | ||
case (6, 1) where osVersionInfo.wProductType == VER_NT_WORKSTATION: versionString += "Windows 7" | ||
case (6, 1): versionString += "Windows Server 2008 R2" | ||
case (6, 2) where osVersionInfo.wProductType == VER_NT_WORKSTATION: versionString += "Windows 8" | ||
case (6, 2): versionString += "Windows Server 2012" | ||
case (6, 3) where osVersionInfo.wProductType == VER_NT_WORKSTATION: versionString += "Windows 8.1" | ||
case (6, 3): versionString += "Windows Server 2012 R2" // We assume the "10,0" numbers in the table for this are a typo | ||
case (10, 0) where osVersionInfo.wProductType == VER_NT_WORKSTATION: versionString += "Windows 10" | ||
case (10, 0): versionString += "Windows Server 2019" // The table gives identical values for 2016 and 2019, so we just assume 2019 here | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 2016 is 10.0.14393 and 10.0.16299, 2019 is 10.0.17763. Versions are also identified by year and month, for example, "Windows Server 2016, version 1709") and IIRC you can get the latter part from the registry key HKLM\Software\Microsoft\Windows\Windows NT\CurrentVersion\ReleaseID for Windows 10 and up. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
That is 10.0.19465 though, so, the mapping seems slightly confusing. |
||
default: return fallback | ||
} | ||
versionString += " (build \(osVersionInfo.dwBuildNumber))" | ||
// For now we ignore the `szCSDVersion`, `wServicePackMajor`, and `wServicePackMinor` values. | ||
return versionString | ||
#else | ||
return CFCopySystemVersionString()?._swiftObject ?? fallback | ||
#endif | ||
|
@@ -108,21 +165,10 @@ open class ProcessInfo: NSObject { | |
} | ||
versionString = productVersion._swiftObject | ||
#elseif os(Windows) | ||
guard let ntdll = ("ntdll.dll".withCString(encodedAs: UTF16.self) { | ||
LoadLibraryExW($0, nil, DWORD(LOAD_LIBRARY_SEARCH_SYSTEM32)) | ||
}) else { | ||
return OperatingSystemVersion(majorVersion: fallbackMajor, minorVersion: fallbackMinor, patchVersion: fallbackPatch) | ||
} | ||
defer { FreeLibrary(ntdll) } | ||
typealias RTLGetVersionTy = @convention(c) (UnsafeMutablePointer<RTL_OSVERSIONINFOW>) -> NTSTATUS | ||
guard let pfnRTLGetVersion = unsafeBitCast(GetProcAddress(ntdll, "RtlGetVersion"), to: Optional<RTLGetVersionTy>.self) else { | ||
return OperatingSystemVersion(majorVersion: fallbackMajor, minorVersion: fallbackMinor, patchVersion: fallbackPatch) | ||
} | ||
var osVersionInfo = RTL_OSVERSIONINFOW() | ||
osVersionInfo.dwOSVersionInfoSize = DWORD(MemoryLayout<RTL_OSVERSIONINFOW>.size) | ||
guard pfnRTLGetVersion(&osVersionInfo) == 0 else { | ||
guard let osVersionInfo = self._rawOperatingSystemVersionInfo else { | ||
return OperatingSystemVersion(majorVersion: fallbackMajor, minorVersion: fallbackMinor, patchVersion: fallbackPatch) | ||
} | ||
|
||
return OperatingSystemVersion( | ||
majorVersion: Int(osVersionInfo.dwMajorVersion), | ||
minorVersion: Int(osVersionInfo.dwMinorVersion), | ||
|
Uh oh!
There was an error while loading. Please reload this page.