use super::*;
#[repr(transparent)]
#[derive(Debug)]
pub struct Key(pub(crate) HKEY);
impl Default for Key {
fn default() -> Self {
Self(null_mut())
}
}
impl Key {
pub fn create<T: AsRef<str>>(&self, path: T) -> Result<Self> {
let mut handle = null_mut();
let result = unsafe {
RegCreateKeyExW(
self.0,
pcwstr(path).as_ptr(),
0,
null(),
REG_OPTION_NON_VOLATILE,
KEY_READ | KEY_WRITE,
null(),
&mut handle,
null_mut(),
)
};
win32_error(result).map(|_| Self(handle))
}
pub fn open<T: AsRef<str>>(&self, path: T) -> Result<Self> {
let mut handle = null_mut();
let result =
unsafe { RegOpenKeyExW(self.0, pcwstr(path).as_ptr(), 0, KEY_READ, &mut handle) };
win32_error(result).map(|_| Self(handle))
}
pub unsafe fn from_raw(handle: *mut core::ffi::c_void) -> Self {
Self(handle)
}
pub fn as_raw(&self) -> *mut core::ffi::c_void {
self.0
}
pub fn remove_tree<T: AsRef<str>>(&self, path: T) -> Result<()> {
let result = unsafe { RegDeleteTreeW(self.0, pcwstr(path).as_ptr()) };
win32_error(result)
}
pub fn remove_value<T: AsRef<str>>(&self, name: T) -> Result<()> {
let result = unsafe { RegDeleteValueW(self.0, pcwstr(name).as_ptr()) };
win32_error(result)
}
pub fn keys(&self) -> Result<KeyIterator<'_>> {
KeyIterator::new(self)
}
pub fn values(&self) -> Result<ValueIterator<'_>> {
ValueIterator::new(self)
}
pub fn set_u32<T: AsRef<str>>(&self, name: T, value: u32) -> Result<()> {
self.set_bytes(name, Type::U32, &value.to_le_bytes())
}
pub fn set_u64<T: AsRef<str>>(&self, name: T, value: u64) -> Result<()> {
self.set_bytes(name, Type::U64, &value.to_le_bytes())
}
pub fn set_string<T: AsRef<str>>(&self, name: T, value: T) -> Result<()> {
self.set_bytes(name, Type::String, pcwstr(value).as_bytes())
}
pub fn set_hstring<T: AsRef<str>>(
&self,
name: T,
value: &windows_strings::HSTRING,
) -> Result<()> {
self.set_bytes(name, Type::String, as_bytes(value))
}
pub fn set_expand_string<T: AsRef<str>>(&self, name: T, value: T) -> Result<()> {
self.set_bytes(name, Type::ExpandString, pcwstr(value).as_bytes())
}
pub fn set_expand_hstring<T: AsRef<str>>(
&self,
name: T,
value: &windows_strings::HSTRING,
) -> Result<()> {
self.set_bytes(name, Type::ExpandString, as_bytes(value))
}
pub fn set_multi_string<T: AsRef<str>>(&self, name: T, value: &[T]) -> Result<()> {
let value = multi_pcwstr(value);
self.set_bytes(name, Type::MultiString, value.as_bytes())
}
pub fn set_value<T: AsRef<str>>(&self, name: T, value: &Value) -> Result<()> {
self.set_bytes(name, value.ty(), value)
}
pub fn set_bytes<T: AsRef<str>>(&self, name: T, ty: Type, value: &[u8]) -> Result<()> {
unsafe { self.raw_set_bytes(pcwstr(name).as_raw(), ty, value) }
}
pub fn get_type<T: AsRef<str>>(&self, name: T) -> Result<Type> {
let (ty, _) = unsafe { self.raw_get_info(pcwstr(name).as_raw())? };
Ok(ty)
}
pub fn get_value<T: AsRef<str>>(&self, name: T) -> Result<Value> {
let name = pcwstr(name);
let (ty, len) = unsafe { self.raw_get_info(name.as_raw())? };
let mut data = Data::new(len);
unsafe { self.raw_get_bytes(name.as_raw(), &mut data)? };
Ok(Value { data, ty })
}
pub fn get_u32<T: AsRef<str>>(&self, name: T) -> Result<u32> {
Ok(self.get_u64(name)?.try_into()?)
}
pub fn get_u64<T: AsRef<str>>(&self, name: T) -> Result<u64> {
let value = &mut [0; 8];
let (ty, value) = unsafe { self.raw_get_bytes(pcwstr(name).as_raw(), value)? };
from_le_bytes(ty, value)
}
pub fn get_string<T: AsRef<str>>(&self, name: T) -> Result<String> {
self.get_value(name)?.try_into()
}
pub fn get_hstring<T: AsRef<str>>(&self, name: T) -> Result<HSTRING> {
let name = pcwstr(name);
let (ty, len) = unsafe { self.raw_get_info(name.as_raw())? };
if !matches!(ty, Type::String | Type::ExpandString) {
return Err(invalid_data());
}
let mut value = HStringBuilder::new(len / 2);
unsafe { self.raw_get_bytes(name.as_raw(), value.as_bytes_mut())? };
value.trim_end();
Ok(value.into())
}
pub fn get_multi_string<T: AsRef<str>>(&self, name: T) -> Result<Vec<String>> {
self.get_value(name)?.try_into()
}
#[track_caller]
pub unsafe fn raw_set_bytes<N: AsRef<PCWSTR>>(
&self,
name: N,
ty: Type,
value: &[u8],
) -> Result<()> {
if cfg!(debug_assertions) {
if matches!(ty, Type::String | Type::ExpandString | Type::MultiString) {
debug_assert!(
value.get(value.len() - 2) == Some(&0),
"`value` isn't null-terminated"
);
debug_assert!(value.last() == Some(&0), "`value` isn't null-terminated");
}
}
let result = RegSetValueExW(
self.0,
name.as_ref().as_ptr(),
0,
ty.into(),
value.as_ptr(),
value.len().try_into()?,
);
win32_error(result)
}
pub unsafe fn raw_get_info<N: AsRef<PCWSTR>>(&self, name: N) -> Result<(Type, usize)> {
let mut ty = 0;
let mut len = 0;
let result = RegQueryValueExW(
self.0,
name.as_ref().as_ptr(),
null(),
&mut ty,
core::ptr::null_mut(),
&mut len,
);
win32_error(result)?;
Ok((ty.into(), len as usize))
}
pub unsafe fn raw_get_bytes<'a, N: AsRef<PCWSTR>>(
&self,
name: N,
value: &'a mut [u8],
) -> Result<(Type, &'a [u8])> {
let mut ty = 0;
let mut len = value.len().try_into()?;
let result = RegQueryValueExW(
self.0,
name.as_ref().as_ptr(),
null(),
&mut ty,
value.as_mut_ptr(),
&mut len,
);
win32_error(result)?;
Ok((ty.into(), value.get(0..len as usize).unwrap()))
}
}
impl Drop for Key {
fn drop(&mut self) {
unsafe {
RegCloseKey(self.0);
}
}
}