1#![doc = include_str!("../readme.md")]
2#![cfg(windows)]
3#![cfg_attr(not(test), no_std)]
4#![allow(non_snake_case, non_camel_case_types, clippy::upper_case_acronyms)]
5
6mod bindings;
7use bindings::*;
8
9#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
11pub struct OsVersion {
12 pub major: u32,
14
15 pub minor: u32,
17
18 pub pack: u32,
20
21 pub build: u32,
23}
24
25impl OsVersion {
26 pub const fn new(major: u32, minor: u32, pack: u32, build: u32) -> Self {
28 Self {
29 major,
30 minor,
31 pack,
32 build,
33 }
34 }
35
36 #[cfg(not(test))]
38 pub fn current() -> Self {
39 let mut info = OSVERSIONINFOEXW::new();
40
41 unsafe {
42 RtlGetVersion(&mut info as *mut _ as *mut _);
43 }
44
45 Self {
46 major: info.dwMajorVersion,
47 minor: info.dwMinorVersion,
48 pack: info.wServicePackMajor as u32,
49 build: info.dwBuildNumber,
50 }
51 }
52
53 #[cfg(test)]
55 fn current() -> Self {
56 test::test_current()
57 }
58}
59
60pub fn is_server() -> bool {
62 let mut info = OSVERSIONINFOEXW::new();
63
64 unsafe {
65 RtlGetVersion(&mut info as *mut _ as *mut _);
66 }
67
68 info.wProductType as u32 != VER_NT_WORKSTATION
69}
70
71pub fn revision() -> u32 {
73 let mut value = [0; 4];
74 let mut len = 4;
75
76 let result = unsafe {
77 RegGetValueA(
78 HKEY_LOCAL_MACHINE,
79 b"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\0".as_ptr(),
80 b"UBR\0".as_ptr(),
81 RRF_RT_REG_DWORD,
82 core::ptr::null_mut(),
83 value.as_mut_ptr() as _,
84 &mut len,
85 )
86 };
87
88 if result == 0 {
89 u32::from_le_bytes(value)
90 } else {
91 0
92 }
93}
94
95impl OSVERSIONINFOEXW {
96 fn new() -> Self {
97 Self {
98 dwOSVersionInfoSize: core::mem::size_of::<Self>() as u32,
99 ..Default::default()
100 }
101 }
102}
103
104#[cfg(test)]
105#[allow(clippy::nonminimal_bool)] mod test {
107 use super::*;
108 use std::sync::RwLock;
109
110 static TEST_CURRENT: RwLock<OsVersion> = RwLock::new(OsVersion::new(0, 0, 0, 0));
111
112 pub fn test_current() -> OsVersion {
113 *TEST_CURRENT.read().unwrap()
114 }
115
116 fn set_current(version: OsVersion) {
117 *TEST_CURRENT.write().unwrap() = version;
118 }
119
120 #[test]
121 fn test() {
122 assert_eq!(OsVersion::current(), OsVersion::new(0, 0, 0, 0));
123
124 set_current(OsVersion::new(1, 2, 3, 4));
125 assert_eq!(OsVersion::current(), OsVersion::new(1, 2, 3, 4));
126
127 set_current(OsVersion::new(10, 0, 0, 0));
128 assert!(OsVersion::current() >= OsVersion::new(9, 0, 0, 0));
129 assert!(OsVersion::current() >= OsVersion::new(10, 0, 0, 0));
130 assert!(!(OsVersion::current() >= OsVersion::new(11, 0, 0, 0)));
131
132 set_current(OsVersion::new(10, 100, 0, 0));
133 assert!(OsVersion::current() >= OsVersion::new(10, 99, 0, 0));
134 assert!(OsVersion::current() >= OsVersion::new(10, 100, 0, 0));
135 assert!(!(OsVersion::current() >= OsVersion::new(10, 101, 0, 0)));
136
137 set_current(OsVersion::new(10, 100, 1000, 0));
138 assert!(OsVersion::current() >= OsVersion::new(10, 100, 999, 0));
139 assert!(OsVersion::current() >= OsVersion::new(10, 100, 1000, 0));
140 assert!(!(OsVersion::current() >= OsVersion::new(10, 100, 1001, 0)));
141
142 set_current(OsVersion::new(10, 100, 1_000, 10_000));
143 assert!(OsVersion::current() >= OsVersion::new(10, 100, 1_000, 9_999));
144 assert!(OsVersion::current() >= OsVersion::new(10, 100, 1_000, 10_000));
145 assert!(!(OsVersion::current() >= OsVersion::new(10, 100, 1_000, 10_001)));
146 }
147
148 #[test]
150 fn test_uncertain() {
151 is_server();
152 assert_ne!(revision(), 0);
153 }
154}