use windows::Win32::Foundation::ERROR_SUCCESS;
use windows::Win32::System::Registry::{
HKEY, HKEY_LOCAL_MACHINE, KEY_READ, REG_SZ, REG_VALUE_TYPE, RegCloseKey, RegOpenKeyExW,
RegQueryValueExW,
};
use windows::core::{Error as WinError, HRESULT, PCWSTR, Result as WinResult, w};
use crate::{Error, Result, Vendor};
use super::utils::to_wide_null_vec;
struct RegistryKeyGuard(HKEY);
impl RegistryKeyGuard {
fn new(hkey: HKEY) -> Self {
Self(hkey)
}
}
impl Drop for RegistryKeyGuard {
fn drop(&mut self) {
if !self.0.is_invalid() {
unsafe {
let _ = RegCloseKey(self.0);
};
}
}
}
fn read_registry_string_value(hkey: HKEY, value_name: &str) -> WinResult<String> {
let wide_value_name_vec = to_wide_null_vec(value_name);
let pcwstr_value_name = PCWSTR(wide_value_name_vec.as_ptr());
let mut data_type: REG_VALUE_TYPE = REG_VALUE_TYPE(0);
let mut buffer_size_bytes: u32 = 0;
let mut win_err_code = unsafe {
RegQueryValueExW(
hkey,
pcwstr_value_name,
None, Some(&mut data_type),
None, Some(&mut buffer_size_bytes), )
};
if win_err_code != ERROR_SUCCESS {
return Err(WinError::from_hresult(HRESULT::from_win32(win_err_code.0)));
}
if data_type != REG_SZ {
return Err(WinError::new(
windows::Win32::Foundation::E_UNEXPECTED,
"Registry value is not of type REG_SZ",
));
}
if buffer_size_bytes == 0 {
return Ok(String::new());
}
if !buffer_size_bytes.is_multiple_of(2) {
return Err(WinError::new(
windows::Win32::Foundation::E_INVALIDARG,
"Registry value (REG_SZ) size in bytes is not an even number.",
));
}
let num_u16_elements = (buffer_size_bytes / 2) as usize;
let mut buffer_u16: Vec<u16> = vec![0u16; num_u16_elements];
let mut actual_bytes_written = buffer_size_bytes;
win_err_code = unsafe {
RegQueryValueExW(
hkey,
pcwstr_value_name,
None, None, Some(buffer_u16.as_mut_ptr() as *mut u8), Some(&mut actual_bytes_written), )
};
if win_err_code != ERROR_SUCCESS {
return Err(WinError::from_hresult(HRESULT::from_win32(win_err_code.0)));
}
let actual_u16s_written = (actual_bytes_written / 2) as usize;
let relevant_slice = &buffer_u16[..actual_u16s_written.min(buffer_u16.len())];
String::from_utf16(relevant_slice).map_err(|e| {
WinError::new(
windows::Win32::Foundation::E_UNEXPECTED,
format!("Failed to convert registry value from UTF-16: {}", e),
)
})
}
pub(crate) fn detect_via_registry(vendor: &mut Vendor, model_name: &mut String) -> Result<()> {
let pcwstr_subkey: PCWSTR = w!(r"HARDWARE\DESCRIPTION\System\CentralProcessor\0");
let mut hkey_opened = HKEY::default();
let win_err_code_open = unsafe {
RegOpenKeyExW(
HKEY_LOCAL_MACHINE,
pcwstr_subkey,
Some(0), KEY_READ, &mut hkey_opened,
)
};
if win_err_code_open != ERROR_SUCCESS {
return Err(Error::Detection(format!(
"Failed to open registry key '{}': {}",
r"HARDWARE\DESCRIPTION\System\CentralProcessor\0",
WinError::from_hresult(HRESULT::from_win32(win_err_code_open.0))
)));
}
let hkey_guard = RegistryKeyGuard::new(hkey_opened);
let hkey = hkey_guard.0;
if let Ok(name_str) = read_registry_string_value(hkey, "ProcessorNameString") {
*model_name = name_str;
}
if model_name.to_lowercase().contains("apple") {
*vendor = Vendor::Apple;
}
if *vendor == Vendor::Unknown {
if let Ok(idf_str) = read_registry_string_value(hkey, "Identifier") {
let lower_idf = idf_str.to_lowercase();
if lower_idf.contains("armv8") || lower_idf.contains("arm64") {
*vendor = Vendor::Arm;
}
}
}
if *vendor == Vendor::Unknown {
if let Ok(vid_str) = read_registry_string_value(hkey, "VendorIdentifier") {
let lower_vid = vid_str.trim().to_lowercase();
match lower_vid.as_str() {
"genuineintel" => *vendor = Vendor::Intel,
"authenticamd" => *vendor = Vendor::Amd,
"apple" => *vendor = Vendor::Apple, other_vid if !other_vid.is_empty() => {
*vendor = Vendor::Other;
}
_ => {} }
}
}
Ok(())
}