-
Notifications
You must be signed in to change notification settings - Fork 534
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
271 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) }; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters