@@ -80,11 +80,68 @@ open class ProcessInfo: NSObject {
80
80
return CFUUIDCreateString ( kCFAllocatorSystemDefault, uuid) . _swiftObject
81
81
}
82
82
83
+ #if os(Windows)
84
+ internal var _rawOperatingSystemVersionInfo : RTL_OSVERSIONINFOEXW ? {
85
+ guard let ntdll = ( " ntdll.dll " . withCString ( encodedAs: UTF16 . self) {
86
+ LoadLibraryExW ( $0, nil , DWORD ( LOAD_LIBRARY_SEARCH_SYSTEM32) )
87
+ } ) else {
88
+ return nil
89
+ }
90
+ defer { FreeLibrary ( ntdll) }
91
+ typealias RTLGetVersionTy = @convention ( c) ( UnsafeMutablePointer < RTL_OSVERSIONINFOEXW > ) -> NTSTATUS
92
+ guard let pfnRTLGetVersion = unsafeBitCast ( GetProcAddress ( ntdll, " RtlGetVersion " ) , to: Optional< RTLGetVersionTy> . self ) else {
93
+ return nil
94
+ }
95
+ var osVersionInfo = RTL_OSVERSIONINFOEXW ( )
96
+ osVersionInfo. dwOSVersionInfoSize = DWORD ( MemoryLayout< RTL_OSVERSIONINFOEXW> . size)
97
+ guard pfnRTLGetVersion ( & osVersionInfo) == 0 else {
98
+ return nil
99
+ }
100
+ return osVersionInfo
101
+ }
102
+ #endif
103
+
83
104
open var operatingSystemVersionString : String {
84
105
let fallback = " Unknown "
85
106
#if os(Linux)
86
- let version = try ? String ( contentsOf: URL ( fileURLWithPath: " /proc/version_signature " , isDirectory: false ) , encoding: . utf8)
87
- return version ?? fallback
107
+ var utsNameBuffer = utsname ( )
108
+ guard uname ( & utsNameBuffer) == 0 else {
109
+ return fallback
110
+ }
111
+ let release = withUnsafePointer ( to: & utsNameBuffer. release. 0 ) { String ( cString: $0) }
112
+
113
+ return release
114
+ #elseif os(Windows)
115
+ guard let osVersionInfo = self . _rawOperatingSystemVersionInfo else {
116
+ return fallback
117
+ }
118
+
119
+ // Windows has no canonical way to turn the fairly complex `RTL_OSVERSIONINFOW` version info into a string. We
120
+ // do our best here to construct something consistent. Unfortunately, to provide a useful result, this requires
121
+ // hardcoding several of the somewhat ambiguous values in the table provided here:
122
+ // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_osversioninfoexw#remarks
123
+ var versionString = " "
124
+ switch ( osVersionInfo. dwMajorVersion, osVersionInfo. dwMinorVersion) {
125
+ case ( 5 , 0 ) : versionString += " Windows 2000 "
126
+ case ( 5 , 1 ) : versionString += " Windows XP "
127
+ case ( 5 , 2 ) where osVersionInfo. wProductType == VER_NT_WORKSTATION: versionString += " Windows XP Professional x64 "
128
+ case ( 5 , 2 ) where osVersionInfo. wSuiteMask == VER_SUITE_WH_SERVER: versionString += " Windows Home Server "
129
+ case ( 5 , 2 ) : versionString += " Windows Server 2003 "
130
+ case ( 6 , 0 ) where osVersionInfo. wProductType == VER_NT_WORKSTATION: versionString += " Windows Vista "
131
+ case ( 6 , 0 ) : versionString += " Windows Server 2008 "
132
+ case ( 6 , 1 ) where osVersionInfo. wProductType == VER_NT_WORKSTATION: versionString += " Windows 7 "
133
+ case ( 6 , 1 ) : versionString += " Windows Server 2008 R2 "
134
+ case ( 6 , 2 ) where osVersionInfo. wProductType == VER_NT_WORKSTATION: versionString += " Windows 8 "
135
+ case ( 6 , 2 ) : versionString += " Windows Server 2012 "
136
+ case ( 6 , 3 ) where osVersionInfo. wProductType == VER_NT_WORKSTATION: versionString += " Windows 8.1 "
137
+ case ( 6 , 3 ) : versionString += " Windows Server 2012 R2 " // We assume the "10,0" numbers in the table for this are a typo
138
+ case ( 10 , 0 ) where osVersionInfo. wProductType == VER_NT_WORKSTATION: versionString += " Windows 10 "
139
+ case ( 10 , 0 ) : versionString += " Windows Server 2019 " // The table gives identical values for 2016 and 2019, so we just assume 2019 here
140
+ default : return fallback
141
+ }
142
+ versionString += " (build \( osVersionInfo. dwBuildNumber) ) "
143
+ // For now we ignore the `szCSDVersion`, `wServicePackMajor`, and `wServicePackMinor` values.
144
+ return versionString
88
145
#else
89
146
return CFCopySystemVersionString ( ) ? . _swiftObject ?? fallback
90
147
#endif
@@ -108,21 +165,10 @@ open class ProcessInfo: NSObject {
108
165
}
109
166
versionString = productVersion. _swiftObject
110
167
#elseif os(Windows)
111
- guard let ntdll = ( " ntdll.dll " . withCString ( encodedAs: UTF16 . self) {
112
- LoadLibraryExW ( $0, nil , DWORD ( LOAD_LIBRARY_SEARCH_SYSTEM32) )
113
- } ) else {
114
- return OperatingSystemVersion ( majorVersion: fallbackMajor, minorVersion: fallbackMinor, patchVersion: fallbackPatch)
115
- }
116
- defer { FreeLibrary ( ntdll) }
117
- typealias RTLGetVersionTy = @convention ( c) ( UnsafeMutablePointer < RTL_OSVERSIONINFOW > ) -> NTSTATUS
118
- guard let pfnRTLGetVersion = unsafeBitCast ( GetProcAddress ( ntdll, " RtlGetVersion " ) , to: Optional< RTLGetVersionTy> . self ) else {
119
- return OperatingSystemVersion ( majorVersion: fallbackMajor, minorVersion: fallbackMinor, patchVersion: fallbackPatch)
120
- }
121
- var osVersionInfo = RTL_OSVERSIONINFOW ( )
122
- osVersionInfo. dwOSVersionInfoSize = DWORD ( MemoryLayout< RTL_OSVERSIONINFOW> . size)
123
- guard pfnRTLGetVersion ( & osVersionInfo) == 0 else {
168
+ guard let osVersionInfo = self . _rawOperatingSystemVersionInfo else {
124
169
return OperatingSystemVersion ( majorVersion: fallbackMajor, minorVersion: fallbackMinor, patchVersion: fallbackPatch)
125
170
}
171
+
126
172
return OperatingSystemVersion (
127
173
majorVersion: Int ( osVersionInfo. dwMajorVersion) ,
128
174
minorVersion: Int ( osVersionInfo. dwMinorVersion) ,
0 commit comments