diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/winreg/src/lib.rs | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/winreg/src/lib.rs')
-rw-r--r-- | third_party/rust/winreg/src/lib.rs | 1143 |
1 files changed, 1143 insertions, 0 deletions
diff --git a/third_party/rust/winreg/src/lib.rs b/third_party/rust/winreg/src/lib.rs new file mode 100644 index 0000000000..784976c5c8 --- /dev/null +++ b/third_party/rust/winreg/src/lib.rs @@ -0,0 +1,1143 @@ +// 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() } +} |