use crate::error::Result;
use crate::string::from_wide;
use std::path::PathBuf;
use windows::Win32::System::SystemInformation::{
ComputerNameDnsDomain, ComputerNameDnsFullyQualified, ComputerNameDnsHostname,
ComputerNameNetBIOS, ComputerNamePhysicalDnsDomain, ComputerNamePhysicalDnsFullyQualified,
ComputerNamePhysicalDnsHostname, ComputerNamePhysicalNetBIOS, GetComputerNameExW,
GetNativeSystemInfo, GetVersionExW, OSVERSIONINFOEXW, SYSTEM_INFO,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ProcessorArchitecture {
X86,
X64,
Arm,
Arm64,
Unknown(u16),
}
impl ProcessorArchitecture {
fn from_id(id: u16) -> Self {
match id {
0 => ProcessorArchitecture::X86,
9 => ProcessorArchitecture::X64,
5 => ProcessorArchitecture::Arm,
12 => ProcessorArchitecture::Arm64,
other => ProcessorArchitecture::Unknown(other),
}
}
}
#[derive(Debug, Clone)]
pub struct ProcessorInfo {
pub architecture: ProcessorArchitecture,
pub processor_count: u32,
pub processor_type: u32,
pub processor_level: u16,
pub processor_revision: u16,
pub page_size: u32,
pub min_address: usize,
pub max_address: usize,
pub active_processor_mask: usize,
pub allocation_granularity: u32,
}
pub fn processor_info() -> ProcessorInfo {
let mut info = SYSTEM_INFO::default();
unsafe {
GetNativeSystemInfo(&mut info);
}
let arch = unsafe { info.Anonymous.Anonymous.wProcessorArchitecture };
ProcessorInfo {
architecture: ProcessorArchitecture::from_id(arch.0),
processor_count: info.dwNumberOfProcessors,
processor_type: info.dwProcessorType,
processor_level: 0, processor_revision: 0, page_size: info.dwPageSize,
min_address: info.lpMinimumApplicationAddress as usize,
max_address: info.lpMaximumApplicationAddress as usize,
active_processor_mask: info.dwActiveProcessorMask,
allocation_granularity: info.dwAllocationGranularity,
}
}
#[derive(Debug, Clone)]
pub struct OsVersion {
pub major: u32,
pub minor: u32,
pub build: u32,
pub service_pack_major: u16,
pub service_pack_minor: u16,
pub product_type: u8,
}
impl OsVersion {
pub fn get() -> Result<Self> {
let mut info = OSVERSIONINFOEXW {
dwOSVersionInfoSize: std::mem::size_of::<OSVERSIONINFOEXW>() as u32,
..Default::default()
};
unsafe {
GetVersionExW(&mut info as *mut _ as *mut _)?;
}
Ok(Self {
major: info.dwMajorVersion,
minor: info.dwMinorVersion,
build: info.dwBuildNumber,
service_pack_major: info.wServicePackMajor,
service_pack_minor: info.wServicePackMinor,
product_type: info.wProductType,
})
}
pub fn is_windows_10_or_later(&self) -> bool {
self.major >= 10
}
pub fn is_windows_11_or_later(&self) -> bool {
self.major >= 10 && self.build >= 22000
}
pub fn display_string(&self) -> String {
if self.major >= 10 {
if self.build >= 22000 {
format!("Windows 11 (Build {})", self.build)
} else {
format!("Windows 10 (Build {})", self.build)
}
} else if self.major == 6 {
match self.minor {
3 => format!("Windows 8.1 (Build {})", self.build),
2 => format!("Windows 8 (Build {})", self.build),
1 => format!("Windows 7 (Build {})", self.build),
0 => format!("Windows Vista (Build {})", self.build),
_ => format!(
"Windows {}.{} (Build {})",
self.major, self.minor, self.build
),
}
} else {
format!(
"Windows {}.{} (Build {})",
self.major, self.minor, self.build
)
}
}
}
impl std::fmt::Display for OsVersion {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.display_string())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ComputerNameType {
NetBios,
DnsHostname,
DnsDomain,
DnsFullyQualified,
PhysicalNetBios,
PhysicalDnsHostname,
PhysicalDnsDomain,
PhysicalDnsFullyQualified,
}
impl ComputerNameType {
fn to_native(self) -> windows::Win32::System::SystemInformation::COMPUTER_NAME_FORMAT {
match self {
ComputerNameType::NetBios => ComputerNameNetBIOS,
ComputerNameType::DnsHostname => ComputerNameDnsHostname,
ComputerNameType::DnsDomain => ComputerNameDnsDomain,
ComputerNameType::DnsFullyQualified => ComputerNameDnsFullyQualified,
ComputerNameType::PhysicalNetBios => ComputerNamePhysicalNetBIOS,
ComputerNameType::PhysicalDnsHostname => ComputerNamePhysicalDnsHostname,
ComputerNameType::PhysicalDnsDomain => ComputerNamePhysicalDnsDomain,
ComputerNameType::PhysicalDnsFullyQualified => ComputerNamePhysicalDnsFullyQualified,
}
}
}
pub fn computer_name(name_type: ComputerNameType) -> Result<String> {
let mut size = 0u32;
let _ = unsafe {
GetComputerNameExW(
name_type.to_native(),
windows::core::PWSTR::null(),
&mut size,
)
};
if size == 0 {
return Ok(String::new());
}
let mut buffer = vec![0u16; size as usize];
unsafe {
GetComputerNameExW(
name_type.to_native(),
windows::core::PWSTR(buffer.as_mut_ptr()),
&mut size,
)?;
}
from_wide(&buffer[..size as usize])
}
pub fn hostname() -> Result<String> {
computer_name(ComputerNameType::NetBios)
}
pub fn dns_hostname() -> Result<String> {
computer_name(ComputerNameType::DnsHostname)
}
pub fn dns_domain() -> Result<String> {
computer_name(ComputerNameType::DnsDomain)
}
pub fn fqdn() -> Result<String> {
computer_name(ComputerNameType::DnsFullyQualified)
}
#[derive(Debug, Clone)]
pub struct WindowsDirectories {
pub windows: PathBuf,
pub system: PathBuf,
pub temp: PathBuf,
}
pub fn windows_directories() -> Result<WindowsDirectories> {
Ok(WindowsDirectories {
windows: crate::fs::get_windows_directory()?,
system: crate::fs::get_system_directory()?,
temp: crate::fs::get_temp_directory()?,
})
}
#[derive(Debug)]
pub struct SystemSummary {
pub os_version: OsVersion,
pub processor: ProcessorInfo,
pub hostname: String,
pub memory: crate::mem::MemoryStatus,
}
pub fn system_summary() -> Result<SystemSummary> {
Ok(SystemSummary {
os_version: OsVersion::get()?,
processor: processor_info(),
hostname: hostname()?,
memory: crate::mem::memory_status()?,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_processor_info() {
let info = processor_info();
assert!(info.processor_count > 0);
assert!(info.page_size > 0);
}
#[test]
fn test_os_version() {
let version = OsVersion::get().unwrap();
assert!(version.major >= 6); println!("OS: {}", version);
}
#[test]
fn test_hostname() {
let name = hostname().unwrap();
assert!(!name.is_empty());
}
#[test]
fn test_system_summary() {
let summary = system_summary().unwrap();
println!("OS: {}", summary.os_version);
println!("Hostname: {}", summary.hostname);
println!("Processors: {}", summary.processor.processor_count);
println!(
"Memory: {} MB total",
summary.memory.total_physical / 1024 / 1024
);
}
}