1144 lines
35 KiB
Rust
1144 lines
35 KiB
Rust
// Copyright 2015, Igor Shaula
|
|
// Licensed under the MIT License <LICENSE or
|
|
// http://opensource.org/licenses/MIT>. This file
|
|
// 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() }
|
|
}
|