Skip to main content

windows_erg/registry/
key.rs

1//! Registry key handle and core operations.
2
3use super::builder::RegistryKeyBuilder;
4use super::types::Hive;
5use super::values::RegistryValue;
6use crate::error::{Error as CrateError, SecurityError, SecurityUnsupportedError};
7use crate::security::{
8    ApplyMode, DescriptorEditResult, PermissionEditPlan, PermissionTarget, SecurityDescriptor,
9};
10use crate::{Error, Result};
11use windows::Win32::Foundation::{ERROR_FILE_NOT_FOUND, ERROR_NO_MORE_ITEMS};
12use windows::Win32::System::Registry::*;
13use windows::core::{HSTRING, PCWSTR};
14
15/// A Windows Registry key with automatic handle management.
16pub struct RegistryKey {
17    pub(crate) handle: HKEY,
18    close_on_drop: bool,
19    hive: Option<Hive>,
20    subkey: Option<String>,
21}
22
23impl RegistryKey {
24    /// Create from an existing handle with optional key metadata.
25    pub(crate) fn from_handle_with_metadata(
26        handle: HKEY,
27        close_on_drop: bool,
28        hive: Option<Hive>,
29        subkey: Option<String>,
30    ) -> Self {
31        RegistryKey {
32            handle,
33            close_on_drop,
34            hive,
35            subkey,
36        }
37    }
38
39    /// Create a builder for opening a registry key with specific options.
40    ///
41    /// # Examples
42    ///
43    /// ```no_run
44    /// use windows_erg::registry::{Hive, RegistryKey};
45    ///
46    /// let key = RegistryKey::builder()
47    ///     .hive(Hive::LocalMachine)
48    ///     .path(r"SOFTWARE\Microsoft")
49    ///     .read()
50    ///     .open()?;
51    /// # Ok::<(), windows_erg::Error>(())
52    /// ```
53    pub fn builder() -> RegistryKeyBuilder {
54        RegistryKeyBuilder::new()
55    }
56
57    /// Open an existing registry key with read-only access.
58    ///
59    /// # Examples
60    ///
61    /// ```no_run
62    /// use windows_erg::registry::{Hive, RegistryKey};
63    ///
64    /// let key = RegistryKey::open(
65    ///     Hive::LocalMachine,
66    ///     r"SOFTWARE\Microsoft\Windows\CurrentVersion"
67    /// )?;
68    /// # Ok::<(), windows_erg::Error>(())
69    /// ```
70    pub fn open(hive: Hive, subkey: &str) -> Result<Self> {
71        let subkey_wide = HSTRING::from(subkey);
72        let mut handle = HKEY::default();
73
74        unsafe {
75            let result = RegOpenKeyExW(hive.as_hkey(), &subkey_wide, 0, KEY_READ, &mut handle);
76
77            if result.is_err() {
78                if result == ERROR_FILE_NOT_FOUND {
79                    return Err(Error::Registry(crate::error::RegistryError::KeyNotFound(
80                        crate::error::RegistryKeyNotFoundError::new(subkey.to_string()),
81                    )));
82                }
83                return Err(Error::WindowsApi(crate::error::WindowsApiError::new(
84                    result.into(),
85                )));
86            }
87        }
88
89        Ok(RegistryKey {
90            handle,
91            close_on_drop: true,
92            hive: Some(hive),
93            subkey: Some(subkey.to_string()),
94        })
95    }
96
97    /// Create a new registry key or open it if it already exists.
98    ///
99    /// # Examples
100    ///
101    /// ```no_run
102    /// use windows_erg::registry::{Hive, RegistryKey};
103    ///
104    /// let key = RegistryKey::create(Hive::CurrentUser, r"Software\MyApp")?;
105    /// # Ok::<(), windows_erg::Error>(())
106    /// ```
107    pub fn create(hive: Hive, subkey: &str) -> Result<Self> {
108        let subkey_wide = HSTRING::from(subkey);
109        let mut handle = HKEY::default();
110        let mut disposition = REG_CREATED_NEW_KEY;
111
112        unsafe {
113            let result = RegCreateKeyExW(
114                hive.as_hkey(),
115                &subkey_wide,
116                0,
117                PCWSTR::null(),
118                REG_OPTION_NON_VOLATILE,
119                KEY_READ | KEY_WRITE,
120                None,
121                &mut handle,
122                Some(&mut disposition),
123            );
124
125            if result.is_err() {
126                return Err(Error::WindowsApi(
127                    crate::error::WindowsApiError::with_context(result.into(), "RegCreateKeyExW"),
128                ));
129            }
130        }
131
132        Ok(RegistryKey {
133            handle,
134            close_on_drop: true,
135            hive: Some(hive),
136            subkey: Some(subkey.to_string()),
137        })
138    }
139
140    /// Read this key's security descriptor through the security module.
141    pub fn security_descriptor(&self) -> Result<SecurityDescriptor> {
142        let target = self.security_target()?;
143        target.read_descriptor()
144    }
145
146    /// Write a security descriptor to this key through the security module.
147    pub fn set_security_descriptor(&self, descriptor: &SecurityDescriptor) -> Result<()> {
148        let target = self.security_target()?;
149        target.write_descriptor(descriptor)
150    }
151
152    /// Execute a planned permission edit against this key.
153    pub fn apply_permissions(
154        &self,
155        plan: &PermissionEditPlan,
156        mode: ApplyMode,
157    ) -> Result<DescriptorEditResult> {
158        let target = self.security_target()?;
159        plan.execute_against_target(&target, mode)
160    }
161
162    fn security_target(&self) -> Result<PermissionTarget> {
163        let hive = self.hive.ok_or_else(|| {
164            CrateError::Security(SecurityError::Unsupported(
165                SecurityUnsupportedError::with_reason(
166                    "registry_key".to_string(),
167                    "security_target".to_string(),
168                    "registry key metadata is unavailable for this handle",
169                ),
170            ))
171        })?;
172
173        let path = match &self.subkey {
174            Some(subkey) if subkey.is_empty() => hive.as_short_name().to_string(),
175            Some(subkey) => format!("{}\\{}", hive.as_short_name(), subkey),
176            None => {
177                return Err(CrateError::Security(SecurityError::Unsupported(
178                    SecurityUnsupportedError::with_reason(
179                        "registry_key".to_string(),
180                        "security_target".to_string(),
181                        "registry key path metadata is unavailable",
182                    ),
183                )));
184            }
185        };
186
187        Ok(PermissionTarget::registry(path))
188    }
189
190    /// Get a typed value from the registry key.
191    ///
192    /// # Examples
193    ///
194    /// ```no_run
195    /// # use windows_erg::registry::{Hive, RegistryKey};
196    /// # let key = RegistryKey::open(Hive::LocalMachine, r"SOFTWARE\Microsoft")?;
197    /// let value: String = key.get_value("SomeString")?;
198    /// # Ok::<(), windows_erg::Error>(())
199    /// ```
200    pub fn get_value<T: RegistryValue>(&self, name: &str) -> Result<T> {
201        T::read_from_key(self, name)
202    }
203
204    /// Set a typed value in the registry key.
205    ///
206    /// # Examples
207    ///
208    /// ```no_run
209    /// # use windows_erg::registry::{Hive, RegistryKey};
210    /// # let key = RegistryKey::create(Hive::CurrentUser, r"Software\MyApp")?;
211    /// key.set_value("Version", "1.0.0")?;
212    /// key.set_value("Count", 42u32)?;
213    /// # Ok::<(), windows_erg::Error>(())
214    /// ```
215    pub fn set_value<T: RegistryValue>(&self, name: &str, value: T) -> Result<()> {
216        value.write_to_key(self, name)
217    }
218
219    /// Delete a value from the registry key.
220    pub fn delete_value(&self, name: &str) -> Result<()> {
221        let name_wide = HSTRING::from(name);
222        unsafe {
223            let result = RegDeleteValueW(self.handle, &name_wide);
224            if result.is_err() {
225                return Err(Error::WindowsApi(crate::error::WindowsApiError::new(
226                    result.into(),
227                )));
228            }
229        }
230        Ok(())
231    }
232
233    /// Check if a value exists in the registry key.
234    ///
235    /// # Examples
236    ///
237    /// ```no_run
238    /// # use windows_erg::registry::{Hive, RegistryKey};
239    /// # let key = RegistryKey::open(Hive::CurrentUser, r"Software\MyApp")?;
240    /// if key.value_exists("Version")? {
241    ///     println!("Version exists");
242    /// }
243    /// # Ok::<(), windows_erg::Error>(())
244    /// ```
245    pub fn value_exists(&self, name: &str) -> Result<bool> {
246        let name_wide = HSTRING::from(name);
247        let mut typ = REG_NONE;
248
249        unsafe {
250            let result =
251                RegQueryValueExW(self.handle, &name_wide, None, Some(&mut typ), None, None);
252
253            if result == ERROR_FILE_NOT_FOUND {
254                return Ok(false);
255            }
256
257            if result.is_err() {
258                return Err(Error::WindowsApi(crate::error::WindowsApiError::new(
259                    result.into(),
260                )));
261            }
262
263            Ok(true)
264        }
265    }
266
267    /// Try to get a value, returning None if it doesn't exist.
268    ///
269    /// # Examples
270    ///
271    /// ```no_run
272    /// # use windows_erg::registry::{Hive, RegistryKey};
273    /// # let key = RegistryKey::open(Hive::CurrentUser, r"Software\MyApp")?;
274    /// if let Some(version) = key.try_get_value::<String>("Version") {
275    ///     println!("Version: {}", version);
276    /// }
277    /// # Ok::<(), windows_erg::Error>(())
278    /// ```
279    pub fn try_get_value<T: RegistryValue>(&self, name: &str) -> Option<T> {
280        T::read_from_key(self, name).ok()
281    }
282
283    /// Get a value with a default if it doesn't exist.
284    ///
285    /// # Examples
286    ///
287    /// ```no_run
288    /// # use windows_erg::registry::{Hive, RegistryKey};
289    /// # let key = RegistryKey::open(Hive::CurrentUser, r"Software\MyApp")?;
290    /// let count = key.get_value_or("Count", 0u32);
291    /// # Ok::<(), windows_erg::Error>(())
292    /// ```
293    pub fn get_value_or<T: RegistryValue>(&self, name: &str, default: T) -> T {
294        self.try_get_value(name).unwrap_or(default)
295    }
296
297    /// Delete a registry key.
298    ///
299    /// Note: The key must not have any subkeys. Use `delete_tree()` to delete
300    /// a key and all its subkeys.
301    pub fn delete_key(hive: Hive, subkey: &str) -> Result<()> {
302        let subkey_wide = HSTRING::from(subkey);
303        unsafe {
304            let result = RegDeleteKeyW(hive.as_hkey(), &subkey_wide);
305            if result.is_err() {
306                return Err(Error::WindowsApi(crate::error::WindowsApiError::new(
307                    result.into(),
308                )));
309            }
310        }
311        Ok(())
312    }
313
314    /// Delete a registry key and all its subkeys recursively.
315    pub fn delete_tree(hive: Hive, subkey: &str) -> Result<()> {
316        let subkey_wide = HSTRING::from(subkey);
317        unsafe {
318            let result = RegDeleteTreeW(hive.as_hkey(), &subkey_wide);
319            if result.is_err() {
320                return Err(Error::WindowsApi(crate::error::WindowsApiError::new(
321                    result.into(),
322                )));
323            }
324        }
325        Ok(())
326    }
327
328    /// Enumerate all subkeys of this key.
329    pub fn subkeys(&self) -> Result<Vec<String>> {
330        let mut subkeys = Vec::new();
331        let mut index = 0u32;
332
333        loop {
334            let mut name_buf = vec![0u16; 256];
335            let mut name_len = name_buf.len() as u32;
336
337            unsafe {
338                let result = RegEnumKeyExW(
339                    self.handle,
340                    index,
341                    windows::core::PWSTR(name_buf.as_mut_ptr()),
342                    &mut name_len,
343                    None,
344                    windows::core::PWSTR::null(),
345                    None,
346                    None,
347                );
348
349                if result == ERROR_NO_MORE_ITEMS {
350                    break;
351                }
352
353                if result.is_err() {
354                    return Err(Error::WindowsApi(crate::error::WindowsApiError::new(
355                        result.into(),
356                    )));
357                }
358
359                name_buf.truncate(name_len as usize);
360                subkeys.push(String::from_utf16_lossy(&name_buf));
361            }
362
363            index += 1;
364        }
365
366        Ok(subkeys)
367    }
368
369    /// Enumerate all value names in this key.
370    pub fn value_names(&self) -> Result<Vec<String>> {
371        let mut names = Vec::new();
372        let mut index = 0u32;
373
374        loop {
375            let mut name_buf = vec![0u16; 256];
376            let mut name_len = name_buf.len() as u32;
377
378            unsafe {
379                let result = RegEnumValueW(
380                    self.handle,
381                    index,
382                    windows::core::PWSTR(name_buf.as_mut_ptr()),
383                    &mut name_len,
384                    None,
385                    None,
386                    None,
387                    None,
388                );
389
390                if result == ERROR_NO_MORE_ITEMS {
391                    break;
392                }
393
394                if result.is_err() {
395                    return Err(Error::WindowsApi(crate::error::WindowsApiError::new(
396                        result.into(),
397                    )));
398                }
399
400                name_buf.truncate(name_len as usize);
401                names.push(String::from_utf16_lossy(&name_buf));
402            }
403
404            index += 1;
405        }
406
407        Ok(names)
408    }
409}
410
411impl Drop for RegistryKey {
412    fn drop(&mut self) {
413        if self.close_on_drop {
414            unsafe {
415                let _ = RegCloseKey(self.handle);
416            }
417        }
418    }
419}