Source code

Revision control

Copy as Markdown

Other Tools

// Copyright 2015, Igor Shaula
// Licensed under the MIT License <LICENSE or
// may not be copied, modified, or distributed
// except according to those terms.
//! Crate for accessing MS Windows registry
//!
//!## Usage
//!
//!### Basic usage
//!
//!```toml,ignore
//!# Cargo.toml
//![dependencies]
//!winreg = "0.10"
//!```
//!
//!```no_run
//!extern crate winreg;
//!use std::io;
//!use std::path::Path;
//!use winreg::enums::*;
//!use winreg::RegKey;
//!
//!fn main() -> io::Result<()> {
//! println!("Reading some system info...");
//! let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);
//! let cur_ver = hklm.open_subkey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion")?;
//! let pf: String = cur_ver.get_value("ProgramFilesDir")?;
//! let dp: String = cur_ver.get_value("DevicePath")?;
//! println!("ProgramFiles = {}\nDevicePath = {}", pf, dp);
//! let info = cur_ver.query_info()?;
//! println!("info = {:?}", info);
//! let mt = info.get_last_write_time_system();
//! println!(
//! "last_write_time as winapi::um::minwinbase::SYSTEMTIME = {}-{:02}-{:02} {:02}:{:02}:{:02}",
//! mt.wYear, mt.wMonth, mt.wDay, mt.wHour, mt.wMinute, mt.wSecond
//! );
//!
//! // enable `chrono` feature on `winreg` to make this work
//! // println!(
//! // "last_write_time as chrono::NaiveDateTime = {}",
//! // info.get_last_write_time_chrono()
//! // );
//!
//! println!("And now lets write something...");
//! let hkcu = RegKey::predef(HKEY_CURRENT_USER);
//! let path = Path::new("Software").join("WinregRsExample1");
//! let (key, disp) = hkcu.create_subkey(&path)?;
//!
//! match disp {
//! REG_CREATED_NEW_KEY => println!("A new key has been created"),
//! REG_OPENED_EXISTING_KEY => println!("An existing key has been opened"),
//! }
//!
//! key.set_value("TestSZ", &"written by Rust")?;
//! let sz_val: String = key.get_value("TestSZ")?;
//! key.delete_value("TestSZ")?;
//! println!("TestSZ = {}", sz_val);
//!
//! key.set_value("TestMultiSZ", &vec!["written", "by", "Rust"])?;
//! let multi_sz_val: Vec<String> = key.get_value("TestMultiSZ")?;
//! key.delete_value("TestMultiSZ")?;
//! println!("TestMultiSZ = {:?}", multi_sz_val);
//!
//! key.set_value("TestDWORD", &1234567890u32)?;
//! let dword_val: u32 = key.get_value("TestDWORD")?;
//! println!("TestDWORD = {}", dword_val);
//!
//! key.set_value("TestQWORD", &1234567891011121314u64)?;
//! let qword_val: u64 = key.get_value("TestQWORD")?;
//! println!("TestQWORD = {}", qword_val);
//!
//! key.create_subkey("sub\\key")?;
//! hkcu.delete_subkey_all(&path)?;
//!
//! println!("Trying to open nonexistent key...");
//! hkcu.open_subkey(&path).unwrap_or_else(|e| match e.kind() {
//! io::ErrorKind::NotFound => panic!("Key doesn't exist"),
//! io::ErrorKind::PermissionDenied => panic!("Access denied"),
//! _ => panic!("{:?}", e),
//! });
//! Ok(())
//!}
//!```
//!
//!### Iterators
//!
//!```no_run
//!extern crate winreg;
//!use std::io;
//!use winreg::RegKey;
//!use winreg::enums::*;
//!
//!fn main() -> io::Result<()> {
//! println!("File extensions, registered in system:");
//! for i in RegKey::predef(HKEY_CLASSES_ROOT)
//! .enum_keys().map(|x| x.unwrap())
//! .filter(|x| x.starts_with("."))
//! {
//! println!("{}", i);
//! }
//!
//! let system = RegKey::predef(HKEY_LOCAL_MACHINE)
//! .open_subkey("HARDWARE\\DESCRIPTION\\System")?;
//! for (name, value) in system.enum_values().map(|x| x.unwrap()) {
//! println!("{} = {:?}", name, value);
//! }
//!
//! Ok(())
//!}
//!```
//!
#[cfg(feature = "chrono")]
extern crate chrono;
#[cfg(feature = "serialization-serde")]
extern crate serde;
extern crate winapi;
use enums::*;
use std::default::Default;
use std::ffi::OsStr;
use std::fmt;
use std::io;
use std::mem::transmute;
use std::os::windows::ffi::OsStrExt;
use std::ptr;
use std::slice;
#[cfg(feature = "transactions")]
use transaction::Transaction;
use types::{FromRegValue, ToRegValue};
pub use winapi::shared::minwindef::HKEY;
use winapi::shared::minwindef::{BYTE, DWORD, FILETIME, LPBYTE};
use winapi::shared::winerror;
use winapi::um::minwinbase::SYSTEMTIME;
use winapi::um::timezoneapi::FileTimeToSystemTime;
use winapi::um::winnt::{self, WCHAR};
use winapi::um::winreg as winapi_reg;
macro_rules! werr {
($e:expr) => {
Err(io::Error::from_raw_os_error($e as i32))
};
}
#[cfg(feature = "serialization-serde")]
mod decoder;
#[cfg(feature = "serialization-serde")]
mod encoder;
pub mod enums;
#[cfg(feature = "transactions")]
pub mod transaction;
pub mod types;
/// Metadata returned by `RegKey::query_info`
#[derive(Debug, Default)]
pub struct RegKeyMetadata {
// pub Class: winapi::LPWSTR,
// pub ClassLen: DWORD,
pub sub_keys: DWORD,
pub max_sub_key_len: DWORD,
pub max_class_len: DWORD,
pub values: DWORD,
pub max_value_name_len: DWORD,
pub max_value_len: DWORD,
// pub SecurityDescriptor: DWORD,
pub last_write_time: FILETIME,
}
impl RegKeyMetadata {
/// Returns `last_write_time` field as `winapi::um::minwinbase::SYSTEMTIME`
pub fn get_last_write_time_system(&self) -> SYSTEMTIME {
let mut st: SYSTEMTIME = unsafe { ::std::mem::zeroed() };
unsafe {
FileTimeToSystemTime(&self.last_write_time, &mut st);
}
st
}
/// Returns `last_write_time` field as `chrono::NaiveDateTime`.
/// Part of `chrono` feature.
#[cfg(feature = "chrono")]
pub fn get_last_write_time_chrono(&self) -> chrono::NaiveDateTime {
let st = self.get_last_write_time_system();
chrono::NaiveDate::from_ymd(st.wYear.into(), st.wMonth.into(), st.wDay.into()).and_hms(
st.wHour.into(),
st.wMinute.into(),
st.wSecond.into(),
)
}
}
/// Raw registry value
#[derive(PartialEq)]
pub struct RegValue {
pub bytes: Vec<u8>,
pub vtype: RegType,
}
macro_rules! format_reg_value {
($e:expr => $t:ident) => {
match $t::from_reg_value($e) {
Ok(val) => format!("{:?}", val),
Err(_) => return Err(fmt::Error),
}
};
}
impl fmt::Display for RegValue {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let f_val = match self.vtype {
REG_SZ | REG_EXPAND_SZ | REG_MULTI_SZ => format_reg_value!(self => String),
REG_DWORD => format_reg_value!(self => u32),
REG_QWORD => format_reg_value!(self => u64),
_ => format!("{:?}", self.bytes), //TODO: implement more types
};
write!(f, "{}", f_val)
}
}
impl fmt::Debug for RegValue {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "RegValue({:?}: {})", self.vtype, self)
}
}
/// Handle of opened registry key
#[derive(Debug)]
pub struct RegKey {
hkey: HKEY,
}
unsafe impl Send for RegKey {}
impl RegKey {
/// Open one of predefined keys:
///
/// * `HKEY_CLASSES_ROOT`
/// * `HKEY_CURRENT_USER`
/// * `HKEY_LOCAL_MACHINE`
/// * `HKEY_USERS`
/// * `HKEY_PERFORMANCE_DATA`
/// * `HKEY_PERFORMANCE_TEXT`
/// * `HKEY_PERFORMANCE_NLSTEXT`
/// * `HKEY_CURRENT_CONFIG`
/// * `HKEY_DYN_DATA`
/// * `HKEY_CURRENT_USER_LOCAL_SETTINGS`
///
/// # Examples
///
/// ```no_run
/// # use winreg::RegKey;
/// # use winreg::enums::*;
/// let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);
/// ```
pub const fn predef(hkey: HKEY) -> RegKey {
RegKey { hkey }
}
/// Load a registry hive from a file as an application hive.
/// If `lock` is set to `true`, then the hive cannot be loaded again until
/// it's unloaded (i.e. all keys from it go out of scope).
///
/// # Examples
///
/// ```no_run
/// # use std::error::Error;
/// # use winreg::RegKey;
/// # use winreg::enums::*;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// let handle = RegKey::load_app_key("C:\\myhive.dat", false)?;
/// # Ok(())
/// # }
/// ```
pub fn load_app_key<N: AsRef<OsStr>>(filename: N, lock: bool) -> io::Result<RegKey> {
let options = if lock {
winapi_reg::REG_PROCESS_APPKEY
} else {
0
};
RegKey::load_app_key_with_flags(filename, enums::KEY_ALL_ACCESS, options)
}
/// Load a registry hive from a file as an application hive with desired
/// permissions and options. If `options` is set to `REG_PROCESS_APPKEY`,
/// then the hive cannot be loaded again until it's unloaded (i.e. all keys
/// from it go out of scope).
/// # Examples
///
/// ```no_run
/// # use std::error::Error;
/// # use winreg::RegKey;
/// # use winreg::enums::*;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// let handle = RegKey::load_app_key_with_flags("C:\\myhive.dat", KEY_READ, 0)?;
/// # Ok(())
/// # }
/// ```
pub fn load_app_key_with_flags<N: AsRef<OsStr>>(
filename: N,
perms: winapi_reg::REGSAM,
options: DWORD,
) -> io::Result<RegKey> {
let c_filename = to_utf16(filename);
let mut new_hkey: HKEY = ptr::null_mut();
match unsafe {
winapi_reg::RegLoadAppKeyW(c_filename.as_ptr(), &mut new_hkey, perms, options, 0)
as DWORD
} {
0 => Ok(RegKey { hkey: new_hkey }),
err => werr!(err),
}
}
/// Return inner winapi HKEY of a key:
///
/// # Examples
///
/// ```no_run
/// # use std::error::Error;
/// # use winreg::RegKey;
/// # use winreg::enums::*;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);
/// let soft = hklm.open_subkey("SOFTWARE")?;
/// let handle = soft.raw_handle();
/// # Ok(())
/// # }
/// ```
pub const fn raw_handle(&self) -> HKEY {
self.hkey
}
/// Open subkey with `KEY_READ` permissions.
/// Will open another handle to itself if `path` is an empty string.
/// To open with different permissions use `open_subkey_with_flags`.
/// You can also use `create_subkey` to open with `KEY_ALL_ACCESS` permissions.
///
/// # Examples
///
/// ```no_run
/// # use std::error::Error;
/// # use winreg::RegKey;
/// # use winreg::enums::*;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// let soft = RegKey::predef(HKEY_CURRENT_USER)
/// .open_subkey("Software")?;
/// # Ok(())
/// # }
/// ```
pub fn open_subkey<P: AsRef<OsStr>>(&self, path: P) -> io::Result<RegKey> {
self.open_subkey_with_flags(path, enums::KEY_READ)
}
/// Open subkey with desired permissions.
/// Will open another handle to itself if `path` is an empty string.
///
/// # Examples
///
/// ```no_run
/// # use std::error::Error;
/// # use winreg::RegKey;
/// # use winreg::enums::*;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);
/// hklm.open_subkey_with_flags("SOFTWARE\\Microsoft", KEY_READ)?;
/// # Ok(())
/// # }
/// ```
pub fn open_subkey_with_flags<P: AsRef<OsStr>>(
&self,
path: P,
perms: winapi_reg::REGSAM,
) -> io::Result<RegKey> {
let c_path = to_utf16(path);
let mut new_hkey: HKEY = ptr::null_mut();
match unsafe {
winapi_reg::RegOpenKeyExW(self.hkey, c_path.as_ptr(), 0, perms, &mut new_hkey) as DWORD
} {
0 => Ok(RegKey { hkey: new_hkey }),
err => werr!(err),
}
}
/// Part of `transactions` feature.
#[cfg(feature = "transactions")]
pub fn open_subkey_transacted<P: AsRef<OsStr>>(
&self,
path: P,
t: &Transaction,
) -> io::Result<RegKey> {
self.open_subkey_transacted_with_flags(path, t, winnt::KEY_READ)
}
/// Part of `transactions` feature.
#[cfg(feature = "transactions")]
pub fn open_subkey_transacted_with_flags<P: AsRef<OsStr>>(
&self,
path: P,
t: &Transaction,
perms: winapi_reg::REGSAM,
) -> io::Result<RegKey> {
let c_path = to_utf16(path);
let mut new_hkey: HKEY = ptr::null_mut();
match unsafe {
winapi_reg::RegOpenKeyTransactedW(
self.hkey,
c_path.as_ptr(),
0,
perms,
&mut new_hkey,
t.handle,
ptr::null_mut(),
) as DWORD
} {
0 => Ok(RegKey { hkey: new_hkey }),
err => werr!(err),
}
}
/// Create subkey (and all missing parent keys)
/// and open it with `KEY_ALL_ACCESS` permissions.
/// Will just open key if it already exists.
/// If succeeds returns a tuple with the created subkey and its disposition,
/// which can be `REG_CREATED_NEW_KEY` or `REG_OPENED_EXISTING_KEY`.
/// Will open another handle to itself if `path` is an empty string.
/// To create with different permissions use `create_subkey_with_flags`.
///
/// # Examples
///
/// ```no_run
/// # use std::error::Error;
/// # use winreg::RegKey;
/// use winreg::enums::*;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// let hkcu = RegKey::predef(HKEY_CURRENT_USER);
/// let (settings, disp) = hkcu.create_subkey("Software\\MyProduct\\Settings")?;
///
/// match disp {
/// REG_CREATED_NEW_KEY => println!("A new key has been created"),
/// REG_OPENED_EXISTING_KEY => println!("An existing key has been opened")
/// }
/// # Ok(())
/// # }
/// ```
pub fn create_subkey<P: AsRef<OsStr>>(&self, path: P) -> io::Result<(RegKey, RegDisposition)> {
self.create_subkey_with_flags(path, enums::KEY_ALL_ACCESS)
}
pub fn create_subkey_with_flags<P: AsRef<OsStr>>(
&self,
path: P,
perms: winapi_reg::REGSAM,
) -> io::Result<(RegKey, RegDisposition)> {
let c_path = to_utf16(path);
let mut new_hkey: HKEY = ptr::null_mut();
let mut disp_buf: DWORD = 0;
match unsafe {
winapi_reg::RegCreateKeyExW(
self.hkey,
c_path.as_ptr(),
0,
ptr::null_mut(),
winnt::REG_OPTION_NON_VOLATILE,
perms,
ptr::null_mut(),
&mut new_hkey,
&mut disp_buf,
)
} {
0 => {
let disp: RegDisposition = unsafe { transmute(disp_buf as u8) };
Ok((RegKey { hkey: new_hkey }, disp))
}
err => werr!(err),
}
}
/// Part of `transactions` feature.
#[cfg(feature = "transactions")]
pub fn create_subkey_transacted<P: AsRef<OsStr>>(
&self,
path: P,
t: &Transaction,
) -> io::Result<(RegKey, RegDisposition)> {
self.create_subkey_transacted_with_flags(path, t, winnt::KEY_ALL_ACCESS)
}
/// Part of `transactions` feature.
#[cfg(feature = "transactions")]
pub fn create_subkey_transacted_with_flags<P: AsRef<OsStr>>(
&self,
path: P,
t: &Transaction,
perms: winapi_reg::REGSAM,
) -> io::Result<(RegKey, RegDisposition)> {
let c_path = to_utf16(path);
let mut new_hkey: HKEY = ptr::null_mut();
let mut disp_buf: DWORD = 0;
match unsafe {
winapi_reg::RegCreateKeyTransactedW(
self.hkey,
c_path.as_ptr(),
0,
ptr::null_mut(),
winnt::REG_OPTION_NON_VOLATILE,
perms,
ptr::null_mut(),
&mut new_hkey,
&mut disp_buf,
t.handle,
ptr::null_mut(),
) as DWORD
} {
0 => {
let disp: RegDisposition = unsafe { transmute(disp_buf as u8) };
Ok((RegKey { hkey: new_hkey }, disp))
}
err => werr!(err),
}
}
/// Copy all the values and subkeys from `path` to `dest` key.
/// WIll copy the content of `self` if `path` is an empty string.
///
/// # Examples
///
/// ```no_run
/// # use std::error::Error;
/// # use winreg::RegKey;
/// # use winreg::enums::*;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// let hkcu = RegKey::predef(HKEY_CURRENT_USER);
/// let src = hkcu.open_subkey_with_flags("Software\\MyProduct", KEY_READ)?;
/// let (dst, dst_disp) = hkcu.create_subkey("Software\\MyProduct\\Section2")?;
/// src.copy_tree("Section1", &dst)?;
/// # Ok(())
/// # }
/// ```
pub fn copy_tree<P: AsRef<OsStr>>(&self, path: P, dest: &RegKey) -> io::Result<()> {
let c_path = to_utf16(path);
match unsafe { winapi_reg::RegCopyTreeW(self.hkey, c_path.as_ptr(), dest.hkey) } {
0 => Ok(()),
err => werr!(err),
}
}
pub fn query_info(&self) -> io::Result<RegKeyMetadata> {
let mut info: RegKeyMetadata = Default::default();
match unsafe {
winapi_reg::RegQueryInfoKeyW(
self.hkey,
ptr::null_mut(), // Class: winapi::LPWSTR,
ptr::null_mut(), // ClassLen: DWORD,
ptr::null_mut(), // Reserved
&mut info.sub_keys,
&mut info.max_sub_key_len,
&mut info.max_class_len,
&mut info.values,
&mut info.max_value_name_len,
&mut info.max_value_len,
ptr::null_mut(), // lpcbSecurityDescriptor: winapi::LPDWORD,
&mut info.last_write_time,
) as DWORD
} {
0 => Ok(info),
err => werr!(err),
}
}
/// Return an iterator over subkeys names.
///
/// # Examples
///
/// ```no_run
/// # use winreg::RegKey;
/// # use winreg::enums::*;
/// println!("File extensions, registered in this system:");
/// for i in RegKey::predef(HKEY_CLASSES_ROOT)
/// .enum_keys().map(|x| x.unwrap())
/// .filter(|x| x.starts_with("."))
/// {
/// println!("{}", i);
/// }
/// ```
pub const fn enum_keys(&self) -> EnumKeys {
EnumKeys {
key: self,
index: 0,
}
}
/// Return an iterator over values.
///
/// # Examples
///
/// ```no_run
/// # use std::error::Error;
/// # use winreg::RegKey;
/// # use winreg::enums::*;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// let system = RegKey::predef(HKEY_LOCAL_MACHINE)
/// .open_subkey_with_flags("HARDWARE\\DESCRIPTION\\System", KEY_READ)?;
/// for (name, value) in system.enum_values().map(|x| x.unwrap()) {
/// println!("{} = {:?}", name, value);
/// }
/// # Ok(())
/// # }
/// ```
pub const fn enum_values(&self) -> EnumValues {
EnumValues {
key: self,
index: 0,
}
}
/// Delete key. Key names are not case sensitive.
/// Cannot delete if it has subkeys.
/// Use `delete_subkey_all` for that.
///
/// # Examples
///
/// ```no_run
/// # use std::error::Error;
/// # use winreg::RegKey;
/// # use winreg::enums::*;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// RegKey::predef(HKEY_CURRENT_USER)
/// .delete_subkey(r"Software\MyProduct\History")?;
/// # Ok(())
/// # }
/// ```
pub fn delete_subkey<P: AsRef<OsStr>>(&self, path: P) -> io::Result<()> {
self.delete_subkey_with_flags(path, 0)
}
/// Delete key from the desired platform-specific view of the registry.
/// Key names are not case sensitive.
///
/// # Examples
/// ```no_run
/// # use std::error::Error;
/// # use winreg::RegKey;
/// # use winreg::enums::*;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// // delete the key from the 32-bit registry view
/// RegKey::predef(HKEY_LOCAL_MACHINE)
/// .delete_subkey_with_flags(r"Software\MyProduct\History", KEY_WOW64_32KEY)?;
/// # Ok(())
/// # }
/// ```
pub fn delete_subkey_with_flags<P: AsRef<OsStr>>(
&self,
path: P,
perms: winapi_reg::REGSAM,
) -> io::Result<()> {
let c_path = to_utf16(path);
match unsafe {
winapi_reg::RegDeleteKeyExW(
self.hkey,
c_path.as_ptr(), // This parameter cannot be NULL.
perms,
0,
)
} {
0 => Ok(()),
err => werr!(err),
}
}
/// Part of `transactions` feature.
#[cfg(feature = "transactions")]
pub fn delete_subkey_transacted<P: AsRef<OsStr>>(
&self,
path: P,
t: &Transaction,
) -> io::Result<()> {
self.delete_subkey_transacted_with_flags(path, t, 0)
}
/// Part of `transactions` feature.
#[cfg(feature = "transactions")]
pub fn delete_subkey_transacted_with_flags<P: AsRef<OsStr>>(
&self,
path: P,
t: &Transaction,
perms: winapi_reg::REGSAM,
) -> io::Result<()> {
let c_path = to_utf16(path);
match unsafe {
winapi_reg::RegDeleteKeyTransactedW(
self.hkey,
c_path.as_ptr(), // This parameter cannot be NULL.
perms,
0,
t.handle,
ptr::null_mut(),
)
} {
0 => Ok(()),
err => werr!(err),
}
}
/// Recursively delete subkey with all its subkeys and values.
/// If `path` is an empty string, the subkeys and values of this key are deleted.
///
/// # Examples
///
/// ```no_run
/// # use std::error::Error;
/// # use winreg::RegKey;
/// # use winreg::enums::*;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// RegKey::predef(HKEY_CURRENT_USER)
/// .delete_subkey_all("Software\\MyProduct")?;
/// # Ok(())
/// # }
/// ```
pub fn delete_subkey_all<P: AsRef<OsStr>>(&self, path: P) -> io::Result<()> {
let c_path;
let path_ptr = if path.as_ref().is_empty() {
ptr::null()
} else {
c_path = to_utf16(path);
c_path.as_ptr()
};
match unsafe {
winapi_reg::RegDeleteTreeW(
self.hkey,
path_ptr, //If this parameter is NULL, the subkeys and values of this key are deleted.
) as DWORD
} {
0 => Ok(()),
err => werr!(err),
}
}
/// Get a value from registry and seamlessly convert it to the specified rust type
/// with `FromRegValue` implemented (currently `String`, `u32` and `u64`).
/// Will get the `Default` value if `name` is an empty string.
///
/// # Examples
///
/// ```no_run
/// # use std::error::Error;
/// # use winreg::RegKey;
/// # use winreg::enums::*;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// let hkcu = RegKey::predef(HKEY_CURRENT_USER);
/// let settings = hkcu.open_subkey("Software\\MyProduct\\Settings")?;
/// let server: String = settings.get_value("server")?;
/// let port: u32 = settings.get_value("port")?;
/// # Ok(())
/// # }
/// ```
pub fn get_value<T: FromRegValue, N: AsRef<OsStr>>(&self, name: N) -> io::Result<T> {
match self.get_raw_value(name) {
Ok(ref val) => FromRegValue::from_reg_value(val),
Err(err) => Err(err),
}
}
/// Get raw bytes from registry value.
/// Will get the `Default` value if `name` is an empty string.
///
/// # Examples
///
/// ```no_run
/// # use std::error::Error;
/// # use winreg::RegKey;
/// # use winreg::enums::*;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// let hkcu = RegKey::predef(HKEY_CURRENT_USER);
/// let settings = hkcu.open_subkey("Software\\MyProduct\\Settings")?;
/// let data = settings.get_raw_value("data")?;
/// println!("Bytes: {:?}", data.bytes);
/// # Ok(())
/// # }
/// ```
pub fn get_raw_value<N: AsRef<OsStr>>(&self, name: N) -> io::Result<RegValue> {
let c_name = to_utf16(name);
let mut buf_len: DWORD = 2048;
let mut buf_type: DWORD = 0;
let mut buf: Vec<u8> = Vec::with_capacity(buf_len as usize);
loop {
match unsafe {
winapi_reg::RegQueryValueExW(
self.hkey,
c_name.as_ptr() as *const u16,
ptr::null_mut(),
&mut buf_type,
buf.as_mut_ptr() as LPBYTE,
&mut buf_len,
) as DWORD
} {
0 => {
unsafe {
buf.set_len(buf_len as usize);
}
// minimal check before transmute to RegType
if buf_type > winnt::REG_QWORD {
return werr!(winerror::ERROR_BAD_FILE_TYPE);
}
let t: RegType = unsafe { transmute(buf_type as u8) };
return Ok(RegValue {
bytes: buf,
vtype: t,
});
}
winerror::ERROR_MORE_DATA => {
buf.reserve(buf_len as usize);
}
err => return werr!(err),
}
}
}
/// Seamlessly convert a value from a rust type and write it to the registry value
/// with `ToRegValue` trait implemented (currently `String`, `&str`, `u32` and `u64`).
/// Will set the `Default` value if `name` is an empty string.
///
/// # Examples
///
/// ```no_run
/// # use std::error::Error;
/// # use winreg::RegKey;
/// # use winreg::enums::*;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// let hkcu = RegKey::predef(HKEY_CURRENT_USER);
/// let (settings, disp) = hkcu.create_subkey("Software\\MyProduct\\Settings")?;
/// settings.set_value("server", &"www.example.com")?;
/// settings.set_value("port", &8080u32)?;
/// # Ok(())
/// # }
/// ```
pub fn set_value<T: ToRegValue, N: AsRef<OsStr>>(&self, name: N, value: &T) -> io::Result<()> {
self.set_raw_value(name, &value.to_reg_value())
}
/// Write raw bytes from `RegValue` struct to a registry value.
/// Will set the `Default` value if `name` is an empty string.
///
/// # Examples
///
/// ```no_run
/// # use std::error::Error;
/// use winreg::{RegKey, RegValue};
/// use winreg::enums::*;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// let hkcu = RegKey::predef(HKEY_CURRENT_USER);
/// let settings = hkcu.open_subkey("Software\\MyProduct\\Settings")?;
/// let bytes: Vec<u8> = vec![1, 2, 3, 5, 8, 13, 21, 34, 55, 89];
/// let data = RegValue{ vtype: REG_BINARY, bytes: bytes};
/// settings.set_raw_value("data", &data)?;
/// println!("Bytes: {:?}", data.bytes);
/// # Ok(())
/// # }
/// ```
pub fn set_raw_value<N: AsRef<OsStr>>(&self, name: N, value: &RegValue) -> io::Result<()> {
let c_name = to_utf16(name);
let t = value.vtype.clone() as DWORD;
match unsafe {
winapi_reg::RegSetValueExW(
self.hkey,
c_name.as_ptr(),
0,
t,
value.bytes.as_ptr() as *const BYTE,
value.bytes.len() as u32,
) as DWORD
} {
0 => Ok(()),
err => werr!(err),
}
}
/// Delete specified value from registry.
/// Will delete the `Default` value if `name` is an empty string.
///
/// # Examples
///
/// ```no_run
/// # use std::error::Error;
/// # use winreg::RegKey;
/// # use winreg::enums::*;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// let hkcu = RegKey::predef(HKEY_CURRENT_USER);
/// let settings = hkcu.open_subkey("Software\\MyProduct\\Settings")?;
/// settings.delete_value("data")?;
/// # Ok(())
/// # }
/// ```
pub fn delete_value<N: AsRef<OsStr>>(&self, name: N) -> io::Result<()> {
let c_name = to_utf16(name);
match unsafe { winapi_reg::RegDeleteValueW(self.hkey, c_name.as_ptr()) as DWORD } {
0 => Ok(()),
err => werr!(err),
}
}
/// Save `Encodable` type to a registry key.
/// Part of `serialization-serde` feature.
///
/// # Examples
///
/// ```no_run
/// # use std::error::Error;
/// #[macro_use]
/// extern crate serde_derive;
/// extern crate winreg;
/// use winreg::RegKey;
/// use winreg::enums::*;
///
/// #[derive(Serialize)]
/// struct Rectangle{
/// x: u32,
/// y: u32,
/// w: u32,
/// h: u32,
/// }
///
/// #[derive(Serialize)]
/// struct Settings{
/// current_dir: String,
/// window_pos: Rectangle,
/// show_in_tray: bool,
/// }
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// let s: Settings = Settings{
/// current_dir: "C:\\".to_owned(),
/// window_pos: Rectangle{ x:200, y: 100, w: 800, h: 500 },
/// show_in_tray: false,
/// };
/// let s_key = RegKey::predef(HKEY_CURRENT_USER)
/// .open_subkey("Software\\MyProduct\\Settings")?;
/// s_key.encode(&s)?;
/// # Ok(())
/// # }
/// ```
#[cfg(feature = "serialization-serde")]
pub fn encode<T: serde::Serialize>(&self, value: &T) -> encoder::EncodeResult<()> {
let mut encoder = encoder::Encoder::from_key(self)?;
value.serialize(&mut encoder)?;
encoder.commit()
}
/// Load `Decodable` type from a registry key.
/// Part of `serialization-serde` feature.
///
/// # Examples
///
/// ```no_run
/// # use std::error::Error;
/// #[macro_use]
/// extern crate serde_derive;
/// extern crate winreg;
/// use winreg::RegKey;
/// use winreg::enums::*;
///
/// #[derive(Deserialize)]
/// struct Rectangle{
/// x: u32,
/// y: u32,
/// w: u32,
/// h: u32,
/// }
///
/// #[derive(Deserialize)]
/// struct Settings{
/// current_dir: String,
/// window_pos: Rectangle,
/// show_in_tray: bool,
/// }
///
/// # fn main() -> Result<(), Box<dyn Error>> {
/// let s_key = RegKey::predef(HKEY_CURRENT_USER)
/// .open_subkey("Software\\MyProduct\\Settings")?;
/// let s: Settings = s_key.decode()?;
/// # Ok(())
/// # }
/// ```
#[cfg(feature = "serialization-serde")]
pub fn decode<'de, T: serde::Deserialize<'de>>(&self) -> decoder::DecodeResult<T> {
let mut decoder = decoder::Decoder::from_key(self)?;
T::deserialize(&mut decoder)
}
fn close_(&mut self) -> io::Result<()> {
// don't try to close predefined keys
if self.hkey >= enums::HKEY_CLASSES_ROOT {
return Ok(());
};
match unsafe { winapi_reg::RegCloseKey(self.hkey) as DWORD } {
0 => Ok(()),
err => werr!(err),
}
}
fn enum_key(&self, index: DWORD) -> Option<io::Result<String>> {
let mut name_len = 2048;
#[allow(clippy::unnecessary_cast)]
let mut name = [0 as WCHAR; 2048];
match unsafe {
winapi_reg::RegEnumKeyExW(
self.hkey,
index,
name.as_mut_ptr(),
&mut name_len,
ptr::null_mut(), // reserved
ptr::null_mut(), // lpClass: LPWSTR,
ptr::null_mut(), // lpcClass: LPDWORD,
ptr::null_mut(), // lpftLastWriteTime: PFILETIME,
) as DWORD
} {
0 => match String::from_utf16(&name[..name_len as usize]) {
Ok(s) => Some(Ok(s)),
Err(_) => Some(werr!(winerror::ERROR_INVALID_BLOCK)),
},
winerror::ERROR_NO_MORE_ITEMS => None,
err => Some(werr!(err)),
}
}
fn enum_value(&self, index: DWORD) -> Option<io::Result<(String, RegValue)>> {
let mut name_len = 2048;
#[allow(clippy::unnecessary_cast)]
let mut name = [0 as WCHAR; 2048];
let mut buf_len: DWORD = 2048;
let mut buf_type: DWORD = 0;
let mut buf: Vec<u8> = Vec::with_capacity(buf_len as usize);
loop {
match unsafe {
winapi_reg::RegEnumValueW(
self.hkey,
index,
name.as_mut_ptr(),
&mut name_len,
ptr::null_mut(), // reserved
&mut buf_type,
buf.as_mut_ptr() as LPBYTE,
&mut buf_len,
) as DWORD
} {
0 => {
let name = match String::from_utf16(&name[..name_len as usize]) {
Ok(s) => s,
Err(_) => return Some(werr!(winerror::ERROR_INVALID_DATA)),
};
unsafe {
buf.set_len(buf_len as usize);
}
// minimal check before transmute to RegType
if buf_type > winnt::REG_QWORD {
return Some(werr!(winerror::ERROR_BAD_FILE_TYPE));
}
let t: RegType = unsafe { transmute(buf_type as u8) };
let value = RegValue {
bytes: buf,
vtype: t,
};
return Some(Ok((name, value)));
}
winerror::ERROR_MORE_DATA => {
name_len += 1; //for NULL char
buf.reserve(buf_len as usize);
}
winerror::ERROR_NO_MORE_ITEMS => return None,
err => return Some(werr!(err)),
}
}
}
}
impl Drop for RegKey {
fn drop(&mut self) {
self.close_().unwrap_or(());
}
}
/// Iterator over subkeys names
pub struct EnumKeys<'key> {
key: &'key RegKey,
index: DWORD,
}
impl<'key> Iterator for EnumKeys<'key> {
type Item = io::Result<String>;
fn next(&mut self) -> Option<io::Result<String>> {
match self.key.enum_key(self.index) {
v @ Some(_) => {
self.index += 1;
v
}
e @ None => e,
}
}
fn nth(&mut self, n: usize) -> Option<Self::Item> {
self.index += n as DWORD;
self.next()
}
}
/// Iterator over values
pub struct EnumValues<'key> {
key: &'key RegKey,
index: DWORD,
}
impl<'key> Iterator for EnumValues<'key> {
type Item = io::Result<(String, RegValue)>;
fn next(&mut self) -> Option<io::Result<(String, RegValue)>> {
match self.key.enum_value(self.index) {
v @ Some(_) => {
self.index += 1;
v
}
e @ None => e,
}
}
fn nth(&mut self, n: usize) -> Option<Self::Item> {
self.index += n as DWORD;
self.next()
}
}
fn to_utf16<P: AsRef<OsStr>>(s: P) -> Vec<u16> {
s.as_ref()
.encode_wide()
.chain(Some(0).into_iter())
.collect()
}
fn v16_to_v8(v: &[u16]) -> Vec<u8> {
unsafe { slice::from_raw_parts(v.as_ptr() as *const u8, v.len() * 2).to_vec() }
}