Skip to content

Commit

Permalink
Add OpenOptions to windows-registry (#3461)
Browse files Browse the repository at this point in the history
  • Loading branch information
kennykerr authored Jan 22, 2025
1 parent 493d5da commit e5c8f82
Show file tree
Hide file tree
Showing 8 changed files with 271 additions and 31 deletions.
24 changes: 24 additions & 0 deletions crates/libs/registry/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,27 @@ fn main() -> Result<()> {
Ok(())
}
```

Use the `options()` method for even more control:

```rust,no_run
use windows_registry::*;
fn main() -> Result<()> {
let tx = Transaction::new()?;
let key = CURRENT_USER
.options()
.read(true)
.write(true)
.create(true)
.transaction(&tx)
.open("software\\windows-rs")?;
key.set_u32("name", 123)?;
tx.commit()?;
Ok(())
}
```
24 changes: 24 additions & 0 deletions crates/libs/registry/src/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,21 @@
clippy::all
)]

windows_link::link!("kernel32.dll" "system" fn CloseHandle(hobject : HANDLE) -> BOOL);
windows_link::link!("ktmw32.dll" "system" fn CommitTransaction(transactionhandle : HANDLE) -> BOOL);
windows_link::link!("ktmw32.dll" "system" fn CreateTransaction(lptransactionattributes : *mut SECURITY_ATTRIBUTES, uow : *mut GUID, createoptions : u32, isolationlevel : u32, isolationflags : u32, timeout : u32, description : PCWSTR) -> HANDLE);
windows_link::link!("kernel32.dll" "system" fn GetProcessHeap() -> HANDLE);
windows_link::link!("kernel32.dll" "system" fn HeapAlloc(hheap : HANDLE, dwflags : HEAP_FLAGS, dwbytes : usize) -> *mut core::ffi::c_void);
windows_link::link!("kernel32.dll" "system" fn HeapFree(hheap : HANDLE, dwflags : HEAP_FLAGS, lpmem : *const core::ffi::c_void) -> BOOL);
windows_link::link!("advapi32.dll" "system" fn RegCloseKey(hkey : HKEY) -> WIN32_ERROR);
windows_link::link!("advapi32.dll" "system" fn RegCreateKeyExW(hkey : HKEY, lpsubkey : PCWSTR, reserved : u32, lpclass : PCWSTR, dwoptions : REG_OPEN_CREATE_OPTIONS, samdesired : REG_SAM_FLAGS, lpsecurityattributes : *const SECURITY_ATTRIBUTES, phkresult : *mut HKEY, lpdwdisposition : *mut REG_CREATE_KEY_DISPOSITION) -> WIN32_ERROR);
windows_link::link!("advapi32.dll" "system" fn RegCreateKeyTransactedW(hkey : HKEY, lpsubkey : PCWSTR, reserved : u32, lpclass : PCWSTR, dwoptions : REG_OPEN_CREATE_OPTIONS, samdesired : REG_SAM_FLAGS, lpsecurityattributes : *const SECURITY_ATTRIBUTES, phkresult : *mut HKEY, lpdwdisposition : *mut REG_CREATE_KEY_DISPOSITION, htransaction : HANDLE, pextendedparemeter : *const core::ffi::c_void) -> WIN32_ERROR);
windows_link::link!("advapi32.dll" "system" fn RegDeleteTreeW(hkey : HKEY, lpsubkey : PCWSTR) -> WIN32_ERROR);
windows_link::link!("advapi32.dll" "system" fn RegDeleteValueW(hkey : HKEY, lpvaluename : PCWSTR) -> WIN32_ERROR);
windows_link::link!("advapi32.dll" "system" fn RegEnumKeyExW(hkey : HKEY, dwindex : u32, lpname : PWSTR, lpcchname : *mut u32, lpreserved : *const u32, lpclass : PWSTR, lpcchclass : *mut u32, lpftlastwritetime : *mut FILETIME) -> WIN32_ERROR);
windows_link::link!("advapi32.dll" "system" fn RegEnumValueW(hkey : HKEY, dwindex : u32, lpvaluename : PWSTR, lpcchvaluename : *mut u32, lpreserved : *const u32, lptype : *mut u32, lpdata : *mut u8, lpcbdata : *mut u32) -> WIN32_ERROR);
windows_link::link!("advapi32.dll" "system" fn RegOpenKeyExW(hkey : HKEY, lpsubkey : PCWSTR, uloptions : u32, samdesired : REG_SAM_FLAGS, phkresult : *mut HKEY) -> WIN32_ERROR);
windows_link::link!("advapi32.dll" "system" fn RegOpenKeyTransactedW(hkey : HKEY, lpsubkey : PCWSTR, uloptions : u32, samdesired : REG_SAM_FLAGS, phkresult : *mut HKEY, htransaction : HANDLE, pextendedparemeter : *const core::ffi::c_void) -> WIN32_ERROR);
windows_link::link!("advapi32.dll" "system" fn RegQueryInfoKeyW(hkey : HKEY, lpclass : PWSTR, lpcchclass : *mut u32, lpreserved : *const u32, lpcsubkeys : *mut u32, lpcbmaxsubkeylen : *mut u32, lpcbmaxclasslen : *mut u32, lpcvalues : *mut u32, lpcbmaxvaluenamelen : *mut u32, lpcbmaxvaluelen : *mut u32, lpcbsecuritydescriptor : *mut u32, lpftlastwritetime : *mut FILETIME) -> WIN32_ERROR);
windows_link::link!("advapi32.dll" "system" fn RegQueryValueExW(hkey : HKEY, lpvaluename : PCWSTR, lpreserved : *const u32, lptype : *mut REG_VALUE_TYPE, lpdata : *mut u8, lpcbdata : *mut u32) -> WIN32_ERROR);
windows_link::link!("advapi32.dll" "system" fn RegSetValueExW(hkey : HKEY, lpvaluename : PCWSTR, reserved : u32, dwtype : REG_VALUE_TYPE, lpdata : *const u8, cbdata : u32) -> WIN32_ERROR);
Expand All @@ -28,6 +33,24 @@ pub struct FILETIME {
pub dwLowDateTime: u32,
pub dwHighDateTime: u32,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct GUID {
pub data1: u32,
pub data2: u16,
pub data3: u16,
pub data4: [u8; 8],
}
impl GUID {
pub const fn from_u128(uuid: u128) -> Self {
Self {
data1: (uuid >> 96) as u32,
data2: (uuid >> 80 & 0xffff) as u16,
data3: (uuid >> 64 & 0xffff) as u16,
data4: (uuid as u64).to_be_bytes(),
}
}
}
pub type HANDLE = *mut core::ffi::c_void;
pub type HEAP_FLAGS = u32;
pub type HKEY = *mut core::ffi::c_void;
Expand All @@ -36,6 +59,7 @@ pub const HKEY_CURRENT_CONFIG: HKEY = -2147483643i32 as _;
pub const HKEY_CURRENT_USER: HKEY = -2147483647i32 as _;
pub const HKEY_LOCAL_MACHINE: HKEY = -2147483646i32 as _;
pub const HKEY_USERS: HKEY = -2147483645i32 as _;
pub const INVALID_HANDLE_VALUE: HANDLE = -1i32 as _;
pub const KEY_READ: REG_SAM_FLAGS = 131097u32;
pub const KEY_WRITE: REG_SAM_FLAGS = 131078u32;
pub type PCWSTR = *const u16;
Expand Down
42 changes: 11 additions & 31 deletions crates/libs/registry/src/key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,42 +5,24 @@ use super::*;
#[derive(Debug)]
pub struct Key(pub(crate) HKEY);

impl Default for Key {
fn default() -> Self {
Self(null_mut())
}
}

impl Key {
/// Creates a registry key. If the key already exists, the function opens it.
pub fn create<T: AsRef<str>>(&self, path: T) -> Result<Self> {
let mut handle = null_mut();

let result = unsafe {
RegCreateKeyExW(
self.0,
pcwstr(path).as_ptr(),
0,
null(),
REG_OPTION_NON_VOLATILE,
KEY_READ | KEY_WRITE,
null(),
&mut handle,
null_mut(),
)
};

win32_error(result).map(|_| Self(handle))
self.options()
.read(true)
.write(true)
.create(true)
.open(path)
}

/// Opens a registry key.
pub fn open<T: AsRef<str>>(&self, path: T) -> Result<Self> {
let mut handle = null_mut();

let result =
unsafe { RegOpenKeyExW(self.0, pcwstr(path).as_ptr(), 0, KEY_READ, &mut handle) };
self.options().read(true).open(path)
}

win32_error(result).map(|_| Self(handle))
/// Creates an `OpenOptions` object for the registry key.
pub fn options(&self) -> OpenOptions<'_> {
OpenOptions::new(self)
}

/// Constructs a registry key from an existing handle.
Expand Down Expand Up @@ -284,8 +266,6 @@ impl Key {

impl Drop for Key {
fn drop(&mut self) {
unsafe {
RegCloseKey(self.0);
}
unsafe { RegCloseKey(self.0) };
}
}
6 changes: 6 additions & 0 deletions crates/libs/registry/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,15 @@ use core::ptr::{null, null_mut};
mod bindings;
use bindings::*;

mod open_options;
pub use open_options::OpenOptions;

mod key;
pub use key::Key;

mod transaction;
pub use transaction::Transaction;

mod value;
pub use value::Value;

Expand Down
107 changes: 107 additions & 0 deletions crates/libs/registry/src/open_options.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
use super::*;

/// Options and flags used to configure how a registry key is opened.
pub struct OpenOptions<'a> {
parent: &'a Key,
read: bool,
write: bool,
create: bool,
transaction: Option<&'a Transaction>,
}

impl<'a> OpenOptions<'a> {
pub(crate) fn new(parent: &'a Key) -> Self {
Self {
parent,
read: false,
write: false,
create: false,
transaction: None,
}
}

/// Sets the option for read access.
pub fn read(&mut self, read: bool) -> &mut Self {
self.read = read;
self
}

/// Sets the option for write access.
pub fn write(&mut self, write: bool) -> &mut Self {
self.write = write;
self
}

/// Sets the option to create a new registry key, or open it if it already exists.
pub fn create(&mut self, create: bool) -> &mut Self {
self.create = create;
self
}

/// Associate the registry key with a transaction.
pub fn transaction(&mut self, transaction: &'a Transaction) -> &mut Self {
self.transaction = Some(transaction);
self
}

/// Opens a registry key with the options provided by `self`.
pub fn open<T: AsRef<str>>(&self, path: T) -> Result<Key> {
let mut flags = 0;

if self.read {
flags |= KEY_READ;
}

if self.write {
flags |= KEY_WRITE;
}

let mut handle = null_mut();

let result = unsafe {
if let Some(transaction) = self.transaction {
if self.create {
RegCreateKeyTransactedW(
self.parent.0,
pcwstr(path).as_ptr(),
0,
null(),
REG_OPTION_NON_VOLATILE,
flags,
null(),
&mut handle,
null_mut(),
transaction.0,
null(),
)
} else {
RegOpenKeyTransactedW(
self.parent.0,
pcwstr(path).as_ptr(),
0,
flags,
&mut handle,
transaction.0,
null(),
)
}
} else if self.create {
RegCreateKeyExW(
self.parent.0,
pcwstr(path).as_ptr(),
0,
null(),
REG_OPTION_NON_VOLATILE,
flags,
null(),
&mut handle,
null_mut(),
)
} else {
RegOpenKeyExW(self.parent.0, pcwstr(path).as_ptr(), 0, flags, &mut handle)
}
};

win32_error(result).map(|_| Key(handle))
}
}
53 changes: 53 additions & 0 deletions crates/libs/registry/src/transaction.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use super::*;

/// A transaction object.
#[repr(transparent)]
#[derive(Debug)]
pub struct Transaction(pub(crate) HANDLE);

impl Transaction {
/// Creates a new transaction.
pub fn new() -> Result<Self> {
let handle = unsafe { CreateTransaction(null_mut(), null_mut(), 0, 0, 0, 0, null()) };

if handle == INVALID_HANDLE_VALUE {
Err(Error::from_win32())
} else {
Ok(Self(handle))
}
}

/// Commits the transaction.
///
/// The transaction rolls back if it is dropped before `commit` is called.
pub fn commit(self) -> Result<()> {
let result = unsafe { CommitTransaction(self.0) };

if result == 0 {
Err(Error::from_win32())
} else {
Ok(())
}
}

/// Constructs a transaction object from an existing handle.
///
/// # Safety
///
/// This function takes ownership of the handle.
/// The handle must be owned by the caller and safe to free with `CloseHandle`.
pub unsafe fn from_raw(handle: *mut core::ffi::c_void) -> Self {
Self(handle)
}

/// Returns the underlying transaction handle.
pub fn as_raw(&self) -> *mut core::ffi::c_void {
self.0
}
}

impl Drop for Transaction {
fn drop(&mut self) {
unsafe { CloseHandle(self.0) };
}
}
40 changes: 40 additions & 0 deletions crates/tests/misc/registry/tests/transaction.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use windows_registry::*;
use windows_result::*;
use windows_sys::Win32::Foundation::*;

#[test]
fn create_with_transaction() {
let test_key = "software\\windows-rs\\tests\\transaction";
_ = CURRENT_USER.remove_tree(test_key);
let key = CURRENT_USER.create(test_key).unwrap();

let tx = Transaction::new().unwrap();

let tx_key = CURRENT_USER
.options()
.transaction(&tx)
.read(true)
.write(true)
.open(test_key)
.unwrap();

tx_key.set_u64("u64", 123u64).unwrap();
assert_eq!(tx_key.get_u64("u64").unwrap(), 123u64);

// The transaction is not yet committed so this non-transaction read will fail.
assert_eq!(
key.get_u64("u64").unwrap_err().code(),
HRESULT::from_win32(ERROR_FILE_NOT_FOUND)
);

tx.commit().unwrap();

// Now that the transaction is committed the non-transaction read will succeed.
assert_eq!(key.get_u64("u64").unwrap(), 123u64);

// The transaction is no longer active so this key cannot be used.
assert_eq!(
tx_key.get_u64("u64").unwrap_err().code(),
HRESULT::from_win32(ERROR_TRANSACTION_NOT_ACTIVE)
);
}
6 changes: 6 additions & 0 deletions crates/tools/bindings/src/registry.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
--flat --sys --no-core --no-comment

--filter
CloseHandle
CommitTransaction
CreateTransaction
ERROR_INVALID_DATA
ERROR_NO_MORE_ITEMS
GetProcessHeap
Expand All @@ -13,6 +16,7 @@
HKEY_CURRENT_USER
HKEY_LOCAL_MACHINE
HKEY_USERS
INVALID_HANDLE_VALUE
KEY_READ
KEY_WRITE
REG_BINARY
Expand All @@ -24,11 +28,13 @@
REG_SZ
RegCloseKey
RegCreateKeyExW
RegCreateKeyTransactedW
RegDeleteTreeW
RegDeleteValueW
RegEnumKeyExW
RegEnumValueW
RegOpenKeyExW
RegOpenKeyTransactedW
RegQueryInfoKeyW
RegQueryValueExW
RegSetValueExW

0 comments on commit e5c8f82

Please sign in to comment.