sem_reg/
reg.rs

1pub mod monitor;
2
3use std::fmt::Write as FmtWrite;
4use std::io::Write as IoWrite;
5use std::{
6    fs::File,
7    io::{self},
8    mem::ManuallyDrop,
9    path::Path,
10};
11use winreg::{
12    enums::{RegType, KEY_QUERY_VALUE, KEY_SET_VALUE},
13    RegKey, RegValue, HKEY,
14};
15
16pub struct RegValuePath<'a> {
17    pub hkey: HKEY,
18    pub subkey_path: &'a str,
19    pub value_name: &'a str,
20}
21
22pub fn read_reg_bin_value(reg_value_path: &RegValuePath) -> Result<Vec<u8>, io::Error> {
23    let key = RegKey::predef(reg_value_path.hkey)
24        .open_subkey_with_flags(reg_value_path.subkey_path, KEY_QUERY_VALUE)?;
25    let value = key.get_raw_value(reg_value_path.value_name)?;
26
27    if value.vtype == RegType::REG_BINARY {
28        Ok(value.bytes)
29    } else {
30        Err(io::Error::new(
31            io::ErrorKind::InvalidData,
32            "expected binary value",
33        ))
34    }
35}
36
37pub fn write_reg_bin_value(
38    reg_value_path: &RegValuePath,
39    bytes: &Vec<u8>,
40) -> Result<(), io::Error> {
41    let key = RegKey::predef(reg_value_path.hkey)
42        .open_subkey_with_flags(reg_value_path.subkey_path, KEY_SET_VALUE)?;
43
44    //TODO: See https://github.com/gentoo90/winreg-rs/issues/64 ("RegValue should contain Cow<[u8]>, not Vec<u8>").
45    let unsafe_reg_value = ManuallyDrop::new(RegValue {
46        vtype: RegType::REG_BINARY,
47        // Unsafely double-owned `Vec`.
48        bytes: unsafe { Vec::from_raw_parts(bytes.as_ptr() as _, bytes.len(), bytes.capacity()) },
49    });
50
51    // A panic would leak the reg value, but at least not cause a double-drop.
52    let result = key.set_raw_value(reg_value_path.value_name, &unsafe_reg_value);
53
54    // Drop only parts in fact owned. Use `ManuallyDrop` like `Vec::into_raw_parts()`, which is available in nightly Rust (as of Nov. 2023).
55    let RegValue { bytes, .. } = ManuallyDrop::into_inner(unsafe_reg_value);
56    let _ = ManuallyDrop::new(bytes);
57
58    result?;
59    Ok(())
60}
61
62pub(crate) fn export_reg_bin_values<T: AsRef<Path>>(
63    reg_value_paths: &[RegValuePath],
64    file_path: T,
65) -> Result<(), io::Error> {
66    //! # Panics
67    //! Panics in case of an unknown `HKEY`. As of Nov. 2023, there are 10 that the `winreg` crate re-exports.
68
69    let io_error = |_| io::Error::from(io::ErrorKind::Other);
70
71    let mut text = String::with_capacity(2048);
72
73    text.push_str("\u{feff}Windows Registry Editor Version 5.00\r\n");
74    text.push_str("\r\n");
75
76    for reg_value_path in reg_value_paths {
77        write!(
78            text,
79            "[{}\\{}]\r\n",
80            hkey_to_str(reg_value_path.hkey),
81            reg_value_path.subkey_path
82        )
83        .map_err(io_error)?;
84
85        write!(text, "\"{}\"=hex:", reg_value_path.value_name).map_err(io_error)?;
86
87        let mut first = true;
88        for byte in read_reg_bin_value(reg_value_path)? {
89            write!(text, "{}{:02x}", if first { "" } else { "," }, byte).map_err(io_error)?;
90            first = false;
91        }
92
93        text.push_str("\r\n");
94        text.push_str("\r\n");
95    }
96
97    // Write file as UTF-16LE. BOM was added at the beginning. This is how `regedit.exe` saves .reg files.
98    let mut file = File::create(file_path)?;
99    for int16 in text.encode_utf16() {
100        file.write(&int16.to_le_bytes())?;
101    }
102
103    Ok(())
104}
105
106pub(crate) fn delete_reg_value(reg_value_path: &RegValuePath) -> Result<(), io::Error> {
107    let key = RegKey::predef(reg_value_path.hkey)
108        .open_subkey_with_flags(reg_value_path.subkey_path, KEY_SET_VALUE)?;
109
110    key.delete_value(reg_value_path.value_name)
111        .or_else(|error| {
112            if error.kind() == io::ErrorKind::NotFound {
113                Ok(())
114            } else {
115                Err(error)
116            }
117        })
118}
119
120const fn hkey_to_str(hkey: HKEY) -> &'static str {
121    use winreg::enums::*;
122
123    match hkey {
124        HKEY_CLASSES_ROOT => "HKEY_CLASSES_ROOT",
125        HKEY_CURRENT_USER => "HKEY_CURRENT_USER",
126        HKEY_LOCAL_MACHINE => "HKEY_LOCAL_MACHINE",
127        HKEY_USERS => "HKEY_USERS",
128        HKEY_PERFORMANCE_DATA => "HKEY_PERFORMANCE_DATA",
129        HKEY_PERFORMANCE_TEXT => "HKEY_PERFORMANCE_TEXT",
130        HKEY_PERFORMANCE_NLSTEXT => "HKEY_PERFORMANCE_NLSTEXT",
131        HKEY_CURRENT_CONFIG => "HKEY_CURRENT_CONFIG",
132        HKEY_DYN_DATA => "HKEY_DYN_DATA",
133        HKEY_CURRENT_USER_LOCAL_SETTINGS => "HKEY_CURRENT_USER_LOCAL_SETTINGS",
134        _ => panic!("unknown `HKEY`"),
135    }
136}