├── .editorconfig ├── .gitattributes ├── .github └── workflows │ └── ci.yaml ├── .gitignore ├── .taplo.toml ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE ├── README.md ├── examples ├── basic_usage.rs ├── enum.rs ├── installed_apps.rs ├── load_app_key.rs ├── map_key_serialization.rs ├── reg2json.rs ├── serialization.rs ├── transacted_serialization.rs └── transactions.rs ├── src ├── common.rs ├── decoder │ ├── mod.rs │ └── serialization_serde.rs ├── encoder │ ├── mod.rs │ └── serialization_serde.rs ├── enums.rs ├── lib.rs ├── reg_key.rs ├── reg_key_metadata.rs ├── reg_value.rs ├── transaction.rs └── types.rs └── tests ├── common └── mod.rs ├── reg_key.rs ├── reg_value.rs └── serialization.rs /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | trim_trailing_whitespace = true 7 | insert_final_newline = true 8 | indent_style = space 9 | indent_size = 4 10 | 11 | [*.yaml] 12 | indent_style = space 13 | indent_size = 2 14 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | - push 4 | - pull_request 5 | - workflow_dispatch 6 | jobs: 7 | tests: 8 | name: CI 9 | runs-on: windows-latest 10 | strategy: 11 | fail-fast: false 12 | matrix: 13 | channel: [stable] 14 | target: 15 | - x86_64-pc-windows-msvc 16 | - x86_64-pc-windows-gnu 17 | - i686-pc-windows-msvc 18 | - i686-pc-windows-gnu 19 | include: 20 | - channel: stable 21 | target: x86_64-pc-windows-msvc 22 | lint: true 23 | - channel: '1.60' 24 | target: x86_64-pc-windows-msvc 25 | restrict_deps_versions: true 26 | env: 27 | RUST_BACKTRACE: full 28 | steps: 29 | - uses: actions/checkout@v4 30 | - name: Install rust-${{ matrix.channel }} 31 | uses: actions-rs/toolchain@v1 32 | with: 33 | toolchain: ${{ matrix.channel }} 34 | profile: minimal 35 | override: true 36 | - name: Cache cargo registry 37 | uses: actions/cache@v4 38 | continue-on-error: true 39 | with: 40 | path: | 41 | ~/.cargo/registry/index/ 42 | ~/.cargo/registry/cache/ 43 | key: ${{ runner.os }}-cargo-${{ matrix.channel }} 44 | restore-keys: | 45 | ${{ runner.os }}-cargo-${{ matrix.channel }} 46 | - name: Create Cargo.lock 47 | uses: actions-rs/cargo@v1 48 | with: 49 | command: generate-lockfile 50 | - name: Restrict serde version 51 | if: matrix.restrict_deps_versions 52 | uses: actions-rs/cargo@v1 53 | with: 54 | command: update 55 | args: --package serde --precise 1.0.210 56 | - name: Restrict syn version 57 | if: matrix.restrict_deps_versions 58 | uses: actions-rs/cargo@v1 59 | with: 60 | command: update 61 | args: --package syn --precise 2.0.67 62 | - name: Restrict chrono version 63 | if: matrix.restrict_deps_versions 64 | uses: actions-rs/cargo@v1 65 | with: 66 | command: update 67 | args: --package chrono --precise 0.4.31 68 | - name: Restrict libc version 69 | if: matrix.restrict_deps_versions 70 | uses: actions-rs/cargo@v1 71 | with: 72 | command: update 73 | args: --package libc --precise 0.2.163 74 | - name: Restrict memchr version 75 | if: matrix.restrict_deps_versions 76 | uses: actions-rs/cargo@v1 77 | with: 78 | command: update 79 | args: --package memchr --precise 2.6.2 80 | - name: Setup Taplo 81 | if: matrix.lint 82 | uses: uncenter/setup-taplo@v1 83 | with: 84 | version: "0.8.1" 85 | - name: Check Cargo.toml formatting 86 | if: matrix.lint 87 | run: taplo fmt --check --diff 88 | - name: Check sourcecode formatting 89 | if: matrix.lint 90 | uses: actions-rs/cargo@v1 91 | with: 92 | command: fmt 93 | args: --all -- --check 94 | - name: Lint 95 | if: matrix.lint 96 | uses: actions-rs/cargo@v1 97 | with: 98 | command: clippy 99 | args: --all-features --all-targets 100 | - name: Run tests (no features) 101 | uses: actions-rs/cargo@v1 102 | with: 103 | command: test 104 | args: --locked --release --no-fail-fast --no-default-features 105 | - name: Run tests (all features) 106 | uses: actions-rs/cargo@v1 107 | with: 108 | command: test 109 | args: --locked --release --no-fail-fast --all-features 110 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /.taplo.toml: -------------------------------------------------------------------------------- 1 | include = [".taplo.toml", "Cargo.toml"] 2 | 3 | [formatting] 4 | indent_string = " " 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 0.55.0 4 | * Breaking change: Increate MSRV to 1.60 5 | * Breaking change: Upgrade `windows-sys` to version 0.59 ([#77](https://github.com/gentoo90/winreg-rs/pull/77)) 6 | 7 | ## 0.54.0 8 | * Breaking change: Migrate to the 2021 edition of Rust (MSRV 1.56) 9 | * Breaking change: Upgrade `windows-sys` to version 0.52 (closes [#63](https://github.com/gentoo90/winreg-rs/pull/63), [#70](https://github.com/gentoo90/winreg-rs/pull/70)) 10 | 11 | ## 0.15.0, 0.53.0 12 | * Don't stop deserialization of `Any` due to `REG_NONE` (pullrequest [#67](https://github.com/gentoo90/winreg-rs/pull/67), fixes [#66](https://github.com/gentoo90/winreg-rs/issues/66)) 13 | * Implement (de)serialization of `Option` ([#56](https://github.com/gentoo90/winreg-rs/issues/56)) 14 | * Add `RegKey` methods for creating/opening subkeys with custom options ([#65](https://github.com/gentoo90/winreg-rs/pull/65)) 15 | 16 | ## 0.52.0 17 | * Breaking change: `.commit()` and `.rollback()` now consume the transaction ([#62](https://github.com/gentoo90/winreg-rs/issues/62)) 18 | * Add `RegKey::rename_subkey()` method ([#58](https://github.com/gentoo90/winreg-rs/issues/58)) 19 | * Make serialization modules public ([#59](https://github.com/gentoo90/winreg-rs/issues/59)) 20 | * Fix UB in `FromRegValue` for `u32` and `u64` ([#61](https://github.com/gentoo90/winreg-rs/issues/61)) 21 | 22 | ## 0.14.0 23 | * Breaking change: increase MSRV to 1.34 24 | * Fix UB in `FromRegValue` for `u32` and `u64` ([#61](https://github.com/gentoo90/winreg-rs/issues/61)) 25 | 26 | ## 0.13.0 27 | 28 | * Breaking change: `.commit()` and `.rollback()` now consume the transaction ([#62](https://github.com/gentoo90/winreg-rs/issues/62)) 29 | * Add `RegKey::rename_subkey()` method ([#58](https://github.com/gentoo90/winreg-rs/issues/58)) 30 | * Make serialization modules public ([#59](https://github.com/gentoo90/winreg-rs/issues/59)) 31 | 32 | ## 0.12.0, 0.51.0 33 | 34 | * Breaking change: fix `std::fmt::Display` implementation for `RegValue` ([#52](https://github.com/gentoo90/winreg-rs/issues/52)) 35 | * Add `RegKey::encode_transacted()` method (pullrequest [#55](https://github.com/gentoo90/winreg-rs/pull/55), fixes [#54](https://github.com/gentoo90/winreg-rs/issues/54)) 36 | 37 | ## 0.50.0 38 | 39 | * Breaking change: [`winapi-rs`](https://crates.io/crates/winapi) is not maintained any more, so migrate to Microsofts [`windows-sys`](https://crates.io/crates/windows-sys) as a backend ([#48](https://github.com/gentoo90/winreg-rs/pull/48), [#51](https://github.com/gentoo90/winreg-rs/pull/51)) 40 | * Breaking change: Increase minimum supported Rust version to `1.46` since `windows-sys` doesn't compile with older versions 41 | * Replace deprecated methods from `chrono` ([#48](https://github.com/gentoo90/winreg-rs/pull/48)) 42 | 43 | ## 0.11.0 44 | 45 | * Migrate to the 2018 edition of Rust 46 | * Move the code from `lib.rs` to separate files 47 | * Use [`cfg-if`](https://crates.io/crates/cfg-if) instead of `build.rs` to fail build on non-windows systems 48 | * Reimplement deserialization logic, implement [de]serialization for byte arrays ([#49](https://github.com/gentoo90/winreg-rs/issues/49)) 49 | * Fix some typos and `clippy` warnings 50 | 51 | ## 0.10.1 52 | 53 | * Bump the minimal required version of `winapi` to `0.3.9` (required for `load_app_key`) 54 | * Reexport `REG_PROCESS_APPKEY` and use it in the `load_app_key` example 55 | 56 | ## 0.10.0 57 | 58 | * Add `RegKey::load_app_key()` and `RegKey::load_app_key_with_flags()` ([#30](https://github.com/gentoo90/winreg-rs/issues/30)) 59 | * Update dev dependency `rand` to `0.8` 60 | * Add Github actions 61 | * Fix some clippy warnings 62 | 63 | ## 0.9.0 64 | 65 | * Breaking change: `OsStr` and `OsString` registry values are not `NULL`-terminated any more ([#34](https://github.com/gentoo90/winreg-rs/issues/34), [#42](https://github.com/gentoo90/winreg-rs/issues/42)) 66 | * Refactoring: use macros for `ToRegValue` impls and tests for string values 67 | * Fix `bare_trait_objects` warning in the doctests 68 | * Add `impl ToRegValue for OsString` 69 | * Add conversion between `REG_MULTI_SZ` and vectors of strings ([#16](https://github.com/gentoo90/winreg-rs/issues/16)) 70 | * Fix: set minimal `winapi` version to 0.3.7 (earlier versions don't have `impl-default` and `impl-debug` features which we use) 71 | * Appveyor now checks the crate against `rust-1.31.1` too 72 | 73 | ## 0.8.0 74 | 75 | * Implement serialization of `char` and maps 76 | * Implement `std::fmt::Display` for `RegValue` 77 | * Make `RegKey::{predef,raw_handle,enum_keys,enum_values}` functions `const` 78 | * Give a better error message when compiling on platforms other than Windows ([#38](https://github.com/gentoo90/winreg-rs/pull/38)) 79 | * Tests are moved from `src/lib.rs` to `tests/reg_key.rs` 80 | 81 | ## 0.7.0 82 | 83 | * Breaking change: remove deprecated `Error::description` ([#28](https://github.com/gentoo90/winreg-rs/pull/28)) 84 | * Optimize `Iterator::nth()` for the `Enum*` iterators ([#29](https://github.com/gentoo90/winreg-rs/pull/29)) 85 | 86 | ## 0.6.2 87 | 88 | * Add `RegKey::delete_subkey_with_flags()` ([#27](https://github.com/gentoo90/winreg-rs/pull/27)) 89 | 90 | ## 0.6.1 91 | 92 | * Add `last_write_time` field to `RegKeyMetadata` (returned by `RegKey::query_info()`) ([#25](https://github.com/gentoo90/winreg-rs/pull/25)). 93 | * Add `get_last_write_time_system()` and `get_last_write_time_chrono()` (under `chrono` feature) methods to `RegKeyMetadata`. 94 | 95 | ## 0.6.0 96 | 97 | * Breaking change: `create_subkey`, `create_subkey_with_flags`, `create_subkey_transacted` and 98 | `create_subkey_transacted_with_flags` now return a tuple which contains the subkey and its disposition 99 | which can be `REG_CREATED_NEW_KEY` or `REG_OPENED_EXISTING_KEY` ([#21](https://github.com/gentoo90/winreg-rs/issues/21)). 100 | * Examples fixed to not use `unwrap` according to [Rust API guidelines](https://rust-lang-nursery.github.io/api-guidelines/documentation.html#examples-use--not-try-not-unwrap-c-question-mark). 101 | 102 | ## 0.5.1 103 | 104 | * Reexport `HKEY` ([#15](https://github.com/gentoo90/winreg-rs/issues/15)). 105 | * Add `raw_handle` method ([#18](https://github.com/gentoo90/winreg-rs/pull/18)). 106 | 107 | ## 0.5.0 108 | 109 | * Breaking change: `open_subkey` now opens a key with readonly permissions. 110 | Use `create_subkey` or `open_subkey_with_flags` to open with read-write permissions. 111 | * Breaking change: features `transactions` and `serialization-serde` are now disabled by default. 112 | * Breaking change: serialization now uses `serde` instead of `rustc-serialize`. 113 | * `winapi` updated to `0.3`. 114 | * Documentation fixes ([#14](https://github.com/gentoo90/winreg-rs/pull/14)) 115 | 116 | ## 0.4.0 117 | 118 | * Make transactions and serialization otional features 119 | * Update dependencies + minor fixes ([#12](https://github.com/gentoo90/winreg-rs/pull/12)) 120 | 121 | ## 0.3.5 122 | 123 | * Implement `FromRegValue` for `OsString` and `ToRegValue` for `OsStr` ([#8](https://github.com/gentoo90/winreg-rs/issues/8)) 124 | * Minor fixes 125 | 126 | ## 0.3.4 127 | 128 | * Add `copy_tree` method to `RegKey` 129 | * Now checked with [rust-clippy](https://github.com/Manishearth/rust-clippy) 130 | * no more `unwrap`s 131 | * replaced `to_string` with `to_owned` 132 | * Fix: reading strings longer than 2048 characters ([#6](https://github.com/gentoo90/winreg-rs/pull/6)) 133 | 134 | ## 0.3.3 135 | 136 | * Fix: now able to read values longer than 2048 bytes ([#3](https://github.com/gentoo90/winreg-rs/pull/3)) 137 | 138 | ## 0.3.2 139 | 140 | * Fix: `FromRegValue` trait now requires `Sized` (fixes build with rust 1.4) 141 | 142 | ## 0.3.1 143 | 144 | * Fix: bump `winapi` version to fix build 145 | 146 | ## 0.3.0 147 | 148 | * Add transactions support and make serialization transacted 149 | * Breaking change: use `std::io::{Error,Result}` instead of own `RegError` and `RegResult` 150 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "winreg" 3 | edition = "2021" 4 | rust-version = "1.60" 5 | version = "0.55.0" 6 | authors = ["Igor Shaula "] 7 | license = "MIT" 8 | description = "Rust bindings to MS Windows Registry API" 9 | repository = "https://github.com/gentoo90/winreg-rs" 10 | documentation = "https://docs.rs/winreg" 11 | readme = "README.md" 12 | keywords = ["Windows", "WinSDK", "Registry"] 13 | categories = ["api-bindings", "os::windows-apis"] 14 | 15 | [dependencies] 16 | cfg-if = "1.0" 17 | windows-sys = { version = "0.59", features = [ 18 | "Win32_Foundation", 19 | "Win32_System_Time", 20 | "Win32_System_Registry", 21 | "Win32_Security", 22 | "Win32_Storage_FileSystem", 23 | "Win32_System_Diagnostics_Debug", 24 | ] } 25 | chrono = { version = "0.4.6", optional = true } 26 | serde = { version = "1", optional = true } 27 | 28 | [dev-dependencies] 29 | rand = "0.3" 30 | tempfile = "~3.0" 31 | serde_derive = "1" 32 | serde_json = "1" 33 | serde-transcode = "1" 34 | serde_bytes = "0.11" 35 | 36 | [features] 37 | transactions = [] 38 | serialization-serde = ["transactions", "serde"] 39 | 40 | [[example]] 41 | name = "basic_usage" 42 | required-features = ["chrono"] 43 | 44 | [[example]] 45 | name = "enum" 46 | 47 | [[example]] 48 | name = "load_app_key" 49 | 50 | [[example]] 51 | name = "transactions" 52 | required-features = ["transactions"] 53 | 54 | [[example]] 55 | name = "serialization" 56 | required-features = ["serialization-serde"] 57 | 58 | [[example]] 59 | name = "transacted_serialization" 60 | required-features = ["serialization-serde"] 61 | 62 | [[example]] 63 | name = "reg2json" 64 | required-features = ["serialization-serde"] 65 | 66 | [[example]] 67 | name = "map_key_serialization" 68 | required-features = ["serialization-serde"] 69 | 70 | [[example]] 71 | name = "installed_apps" 72 | required-features = ["serialization-serde"] 73 | 74 | [package.metadata.docs.rs] 75 | all-features = true 76 | targets = ["x86_64-pc-windows-msvc", "i686-pc-windows-msvc"] 77 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Igor Shaula 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | winreg 2 | [![Github Actions][actions-image]][actions] 3 | [![Winreg on crates.io][cratesio-image]][cratesio] 4 | [![Winreg on docs.rs][docsrs-image]][docsrs] 5 | ====== 6 | 7 | [actions-image]: https://github.com/gentoo90/winreg-rs/actions/workflows/ci.yaml/badge.svg 8 | [actions]: https://github.com/gentoo90/winreg-rs/actions 9 | [cratesio-image]: https://img.shields.io/crates/v/winreg.svg 10 | [cratesio]: https://crates.io/crates/winreg 11 | [docsrs-image]: https://docs.rs/winreg/badge.svg 12 | [docsrs]: https://docs.rs/winreg 13 | 14 | Rust bindings to MS Windows Registry API. Work in progress. 15 | 16 | Current features: 17 | * Basic registry operations: 18 | * open/create/delete/rename keys 19 | * load application hive from a file 20 | * read and write values 21 | * seamless conversion between `REG_*` types and rust primitives 22 | * `String` and `OsString` <= `REG_SZ`, `REG_EXPAND_SZ` or `REG_MULTI_SZ` 23 | * `String`, `&str`, `OsString`, `&OsStr` => `REG_SZ` 24 | * `Vec`, `Vec` <= `REG_MULTI_SZ` 25 | * `Vec`, `Vec<&str>`, `Vec`, `Vec<&OsStr>` => `REG_MULTI_SZ` 26 | * `u32` <=> `REG_DWORD` 27 | * `u64` <=> `REG_QWORD` 28 | * Iteration through key names and through values 29 | * Transactions 30 | * Transacted serialization of rust types into/from registry (only primitives, `Option`s, structures and maps for now) 31 | 32 | ## Usage 33 | 34 | ### Basic usage 35 | 36 | ```toml 37 | # Cargo.toml 38 | [dependencies] 39 | winreg = "0.55" 40 | ``` 41 | 42 | ```rust 43 | use std::io; 44 | use std::path::Path; 45 | use winreg::enums::*; 46 | use winreg::RegKey; 47 | 48 | fn main() -> io::Result<()> { 49 | println!("Reading some system info..."); 50 | let hklm = RegKey::predef(HKEY_LOCAL_MACHINE); 51 | let cur_ver = hklm.open_subkey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion")?; 52 | let pf: String = cur_ver.get_value("ProgramFilesDir")?; 53 | let dp: String = cur_ver.get_value("DevicePath")?; 54 | println!("ProgramFiles = {}\nDevicePath = {}", pf, dp); 55 | let info = cur_ver.query_info()?; 56 | println!("info = {:?}", info); 57 | let mt = info.get_last_write_time_system(); 58 | println!( 59 | "last_write_time as windows_sys::Win32::Foundation::SYSTEMTIME = {}-{:02}-{:02} {:02}:{:02}:{:02}", 60 | mt.wYear, mt.wMonth, mt.wDay, mt.wHour, mt.wMinute, mt.wSecond 61 | ); 62 | 63 | // enable `chrono` feature on `winreg` to make this work 64 | // println!( 65 | // "last_write_time as chrono::NaiveDateTime = {}", 66 | // info.get_last_write_time_chrono() 67 | // ); 68 | 69 | println!("And now lets write something..."); 70 | let hkcu = RegKey::predef(HKEY_CURRENT_USER); 71 | let path = Path::new("Software").join("WinregRsExample1"); 72 | let (key, disp) = hkcu.create_subkey(&path)?; 73 | 74 | match disp { 75 | REG_CREATED_NEW_KEY => println!("A new key has been created"), 76 | REG_OPENED_EXISTING_KEY => println!("An existing key has been opened"), 77 | } 78 | 79 | key.set_value("TestSZ", &"written by Rust")?; 80 | let sz_val: String = key.get_value("TestSZ")?; 81 | key.delete_value("TestSZ")?; 82 | println!("TestSZ = {}", sz_val); 83 | 84 | key.set_value("TestMultiSZ", &vec!["written", "by", "Rust"])?; 85 | let multi_sz_val: Vec = key.get_value("TestMultiSZ")?; 86 | key.delete_value("TestMultiSZ")?; 87 | println!("TestMultiSZ = {:?}", multi_sz_val); 88 | 89 | key.set_value("TestDWORD", &1234567890u32)?; 90 | let dword_val: u32 = key.get_value("TestDWORD")?; 91 | println!("TestDWORD = {}", dword_val); 92 | 93 | key.set_value("TestQWORD", &1234567891011121314u64)?; 94 | let qword_val: u64 = key.get_value("TestQWORD")?; 95 | println!("TestQWORD = {}", qword_val); 96 | 97 | key.create_subkey("sub\\key")?; 98 | hkcu.delete_subkey_all(&path)?; 99 | 100 | println!("Trying to open nonexistent key..."); 101 | hkcu.open_subkey(&path).unwrap_or_else(|e| match e.kind() { 102 | io::ErrorKind::NotFound => panic!("Key doesn't exist"), 103 | io::ErrorKind::PermissionDenied => panic!("Access denied"), 104 | _ => panic!("{:?}", e), 105 | }); 106 | Ok(()) 107 | } 108 | ``` 109 | 110 | ### Iterators 111 | 112 | ```rust 113 | use std::io; 114 | use winreg::RegKey; 115 | use winreg::enums::*; 116 | 117 | fn main() -> io::Result<()> { 118 | println!("File extensions, registered in system:"); 119 | for i in RegKey::predef(HKEY_CLASSES_ROOT) 120 | .enum_keys().map(|x| x.unwrap()) 121 | .filter(|x| x.starts_with(".")) 122 | { 123 | println!("{}", i); 124 | } 125 | 126 | let system = RegKey::predef(HKEY_LOCAL_MACHINE) 127 | .open_subkey("HARDWARE\\DESCRIPTION\\System")?; 128 | for (name, value) in system.enum_values().map(|x| x.unwrap()) { 129 | println!("{} = {:?}", name, value); 130 | } 131 | 132 | Ok(()) 133 | } 134 | ``` 135 | 136 | ### Transactions 137 | 138 | ```toml 139 | # Cargo.toml 140 | [dependencies] 141 | winreg = { version = "0.55", features = ["transactions"] } 142 | ``` 143 | 144 | ```rust 145 | use std::io; 146 | use winreg::RegKey; 147 | use winreg::enums::*; 148 | use winreg::transaction::Transaction; 149 | 150 | fn main() -> io::Result<()> { 151 | let t = Transaction::new()?; 152 | let hkcu = RegKey::predef(HKEY_CURRENT_USER); 153 | let (key, _disp) = hkcu.create_subkey_transacted("Software\\RustTransaction", &t)?; 154 | key.set_value("TestQWORD", &1234567891011121314u64)?; 155 | key.set_value("TestDWORD", &1234567890u32)?; 156 | 157 | println!("Commit transaction? [y/N]:"); 158 | let mut input = String::new(); 159 | io::stdin().read_line(&mut input)?; 160 | input = input.trim_right().to_owned(); 161 | if input == "y" || input == "Y" { 162 | t.commit()?; 163 | println!("Transaction committed."); 164 | } 165 | else { 166 | // this is optional, if transaction wasn't committed, 167 | // it will be rolled back on disposal 168 | t.rollback()?; 169 | 170 | println!("Transaction wasn't committed, it will be rolled back."); 171 | } 172 | 173 | Ok(()) 174 | } 175 | ``` 176 | 177 | ### Serialization 178 | 179 | ```toml 180 | # Cargo.toml 181 | [dependencies] 182 | winreg = { version = "0.55", features = ["serialization-serde"] } 183 | serde = "1" 184 | serde_derive = "1" 185 | ``` 186 | 187 | ```rust 188 | use serde_derive::{Deserialize, Serialize}; 189 | use std::collections::HashMap; 190 | use std::error::Error; 191 | use winreg::enums::*; 192 | 193 | #[derive(Debug, Serialize, Deserialize, PartialEq)] 194 | struct Coords { 195 | x: u32, 196 | y: u32, 197 | } 198 | 199 | #[derive(Debug, Serialize, Deserialize, PartialEq)] 200 | struct Size { 201 | w: u32, 202 | h: u32, 203 | } 204 | 205 | #[derive(Debug, Serialize, Deserialize, PartialEq)] 206 | struct Rectangle { 207 | coords: Option, 208 | size: Size, 209 | } 210 | 211 | #[derive(Debug, Serialize, Deserialize, PartialEq)] 212 | struct Test { 213 | t_bool: bool, 214 | t_u8: u8, 215 | t_u16: u16, 216 | t_u32: u32, 217 | t_u64: u64, 218 | t_usize: usize, 219 | t_struct: Rectangle, 220 | t_map: HashMap, 221 | t_string: String, 222 | t_optional_string: Option, 223 | #[serde(rename = "")] // empty name becomes the (Default) value in the registry 224 | t_char: char, 225 | t_i8: i8, 226 | t_i16: i16, 227 | t_i32: i32, 228 | t_i64: i64, 229 | t_isize: isize, 230 | t_f64: f64, 231 | t_f32: f32, 232 | } 233 | 234 | fn main() -> Result<(), Box> { 235 | let hkcu = winreg::RegKey::predef(HKEY_CURRENT_USER); 236 | let (key, _disp) = hkcu.create_subkey("Software\\RustEncode")?; 237 | 238 | let mut map = HashMap::new(); 239 | map.insert("".to_owned(), 0); // empty name becomes the (Default) value in the registry 240 | map.insert("v1".to_owned(), 1); 241 | map.insert("v2".to_owned(), 2); 242 | map.insert("v3".to_owned(), 3); 243 | 244 | let v1 = Test { 245 | t_bool: false, 246 | t_u8: 127, 247 | t_u16: 32768, 248 | t_u32: 123_456_789, 249 | t_u64: 123_456_789_101_112, 250 | t_usize: 1_234_567_891, 251 | t_struct: Rectangle { 252 | coords: Some(Coords { x: 55, y: 77 }), 253 | size: Size { w: 500, h: 300 }, 254 | }, 255 | t_map: map, 256 | t_string: "test 123!".to_owned(), 257 | t_optional_string: Some("test 456!".to_owned()), 258 | t_char: 'a', 259 | t_i8: -123, 260 | t_i16: -2049, 261 | t_i32: 20100, 262 | t_i64: -12_345_678_910, 263 | t_isize: -1_234_567_890, 264 | t_f64: -0.01, 265 | t_f32: 3.15, 266 | }; 267 | 268 | key.encode(&v1)?; 269 | 270 | let v2: Test = key.decode()?; 271 | println!("Decoded {:?}", v2); 272 | 273 | println!("Equal to encoded: {:?}", v1 == v2); 274 | Ok(()) 275 | } 276 | ``` 277 | -------------------------------------------------------------------------------- /examples/basic_usage.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Igor Shaula 2 | // Licensed under the MIT License . This file 4 | // may not be copied, modified, or distributed 5 | // except according to those terms. 6 | use std::io; 7 | use std::path::Path; 8 | use winreg::enums::*; 9 | use winreg::RegKey; 10 | 11 | fn main() -> io::Result<()> { 12 | println!("Reading some system info..."); 13 | let hklm = RegKey::predef(HKEY_LOCAL_MACHINE); 14 | let cur_ver = hklm.open_subkey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion")?; 15 | let pf: String = cur_ver.get_value("ProgramFilesDir")?; 16 | let dp: String = cur_ver.get_value("DevicePath")?; 17 | println!("ProgramFiles = {}\nDevicePath = {}", pf, dp); 18 | let info = cur_ver.query_info()?; 19 | println!("info = {:?}", info); 20 | let mt = info.get_last_write_time_system(); 21 | println!( 22 | "last_write_time as windows_sys::Win32::Foundation::SYSTEMTIME = {}-{:02}-{:02} {:02}:{:02}:{:02}", 23 | mt.wYear, mt.wMonth, mt.wDay, mt.wHour, mt.wMinute, mt.wSecond 24 | ); 25 | println!( 26 | "last_write_time as chrono::NaiveDateTime = {}", 27 | info.get_last_write_time_chrono() 28 | ); 29 | 30 | println!("And now lets write something..."); 31 | let hkcu = RegKey::predef(HKEY_CURRENT_USER); 32 | let path = Path::new("Software").join("WinregRsExample1"); 33 | let (key, disp) = hkcu.create_subkey(&path)?; 34 | 35 | match disp { 36 | REG_CREATED_NEW_KEY => println!("A new key has been created"), 37 | REG_OPENED_EXISTING_KEY => println!("An existing key has been opened"), 38 | } 39 | 40 | key.set_value("TestSZ", &"written by Rust")?; 41 | let sz_val: String = key.get_value("TestSZ")?; 42 | key.delete_value("TestSZ")?; 43 | println!("TestSZ = {}", sz_val); 44 | 45 | key.set_value("TestMultiSZ", &vec!["written", "by", "Rust"])?; 46 | let multi_sz_val: Vec = key.get_value("TestMultiSZ")?; 47 | key.delete_value("TestMultiSZ")?; 48 | println!("TestMultiSZ = {:?}", multi_sz_val); 49 | 50 | key.set_value("TestDWORD", &1_234_567_890u32)?; 51 | let dword_val: u32 = key.get_value("TestDWORD")?; 52 | println!("TestDWORD = {}", dword_val); 53 | 54 | key.set_value("TestQWORD", &1_234_567_891_011_121_314u64)?; 55 | let qword_val: u64 = key.get_value("TestQWORD")?; 56 | println!("TestQWORD = {}", qword_val); 57 | 58 | key.create_subkey("sub\\key")?; 59 | hkcu.delete_subkey_all(&path)?; 60 | 61 | println!("Trying to open nonexistent key..."); 62 | hkcu.open_subkey(&path).unwrap_or_else(|e| match e.kind() { 63 | io::ErrorKind::NotFound => panic!("Key doesn't exist"), 64 | io::ErrorKind::PermissionDenied => panic!("Access denied"), 65 | _ => panic!("{:?}", e), 66 | }); 67 | Ok(()) 68 | } 69 | -------------------------------------------------------------------------------- /examples/enum.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Igor Shaula 2 | // Licensed under the MIT License . This file 4 | // may not be copied, modified, or distributed 5 | // except according to those terms. 6 | use std::io; 7 | use winreg::enums::*; 8 | use winreg::RegKey; 9 | 10 | fn main() -> io::Result<()> { 11 | println!("File extensions, registered in system:"); 12 | for i in RegKey::predef(HKEY_CLASSES_ROOT) 13 | .enum_keys() 14 | .map(|x| x.unwrap()) 15 | .filter(|x| x.starts_with('.')) 16 | { 17 | println!("{}", i); 18 | } 19 | 20 | let system = RegKey::predef(HKEY_LOCAL_MACHINE).open_subkey("HARDWARE\\DESCRIPTION\\System")?; 21 | for (name, value) in system.enum_values().map(|x| x.unwrap()) { 22 | println!("{} = {:?}", name, value); 23 | } 24 | 25 | Ok(()) 26 | } 27 | -------------------------------------------------------------------------------- /examples/installed_apps.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Igor Shaula 2 | // Licensed under the MIT License . This file 4 | // may not be copied, modified, or distributed 5 | // except according to those terms. 6 | use serde_derive::{Deserialize, Serialize}; 7 | use std::collections::HashMap; 8 | use std::fmt; 9 | use winreg::enums::*; 10 | 11 | #[allow(non_snake_case)] 12 | #[derive(Debug, Serialize, Deserialize)] 13 | struct InstalledApp { 14 | DisplayName: Option, 15 | DisplayVersion: Option, 16 | UninstallString: Option, 17 | } 18 | 19 | macro_rules! str_from_opt { 20 | ($s:expr) => { 21 | $s.as_ref().map(|x| &**x).unwrap_or("") 22 | }; 23 | } 24 | 25 | impl fmt::Display for InstalledApp { 26 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 27 | write!( 28 | f, 29 | "{}-{}", 30 | str_from_opt!(self.DisplayName), 31 | str_from_opt!(self.DisplayVersion) 32 | ) 33 | } 34 | } 35 | 36 | fn main() { 37 | let hklm = winreg::RegKey::predef(HKEY_LOCAL_MACHINE); 38 | let uninstall_key = hklm 39 | .open_subkey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall") 40 | .expect("key is missing"); 41 | 42 | let apps: HashMap = 43 | uninstall_key.decode().expect("deserialization failed"); 44 | 45 | for v in apps.values() { 46 | println!("{}", v); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /examples/load_app_key.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Igor Shaula 2 | // Licensed under the MIT License . This file 4 | // may not be copied, modified, or distributed 5 | // except according to those terms. 6 | use std::io; 7 | use winreg::enums::*; 8 | use winreg::RegKey; 9 | 10 | fn main() -> io::Result<()> { 11 | { 12 | // put this in a block so app_key_1 gets out of scope and doesn't prevent us 13 | // from loading the key again later 14 | let app_key_1 = RegKey::load_app_key("myhive.dat", true)?; 15 | app_key_1.set_value("answer", &42u32)?; 16 | } 17 | let answer: u32 = { 18 | // NOTE: on Windows 7 this fails with ERROR_ALREADY_EXISTS 19 | let app_key_2 = 20 | RegKey::load_app_key_with_flags("myhive.dat", KEY_READ, REG_PROCESS_APPKEY)?; 21 | app_key_2.get_value("answer")? 22 | }; 23 | println!("The Answer is {}", answer); 24 | Ok(()) 25 | } 26 | -------------------------------------------------------------------------------- /examples/map_key_serialization.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Igor Shaula 2 | // Licensed under the MIT License . This file 4 | // may not be copied, modified, or distributed 5 | // except according to those terms. 6 | use serde_derive::{Deserialize, Serialize}; 7 | use std::collections::HashMap; 8 | use std::error::Error; 9 | use winreg::enums::*; 10 | 11 | #[derive(Debug, Serialize, Deserialize, PartialEq)] 12 | struct Coords { 13 | x: u32, 14 | y: u32, 15 | } 16 | 17 | #[derive(Debug, Serialize, Deserialize, PartialEq)] 18 | struct Size { 19 | w: u32, 20 | h: u32, 21 | } 22 | 23 | #[derive(Debug, Serialize, Deserialize, PartialEq)] 24 | struct Rectangle { 25 | coords: Option, 26 | size: Size, 27 | } 28 | 29 | fn main() -> Result<(), Box> { 30 | let hkcu = winreg::RegKey::predef(HKEY_CURRENT_USER); 31 | let (key, _disp) = hkcu.create_subkey("Software\\RustEncodeMapKey")?; 32 | let mut v1 = HashMap::new(); 33 | v1.insert( 34 | "first".to_owned(), 35 | Rectangle { 36 | coords: Some(Coords { x: 55, y: 77 }), 37 | size: Size { w: 500, h: 300 }, 38 | }, 39 | ); 40 | v1.insert( 41 | "second".to_owned(), 42 | Rectangle { 43 | coords: None, 44 | size: Size { w: 1000, h: 600 }, 45 | }, 46 | ); 47 | 48 | key.encode(&v1)?; 49 | 50 | let v2: HashMap = key.decode()?; 51 | println!("Decoded {:?}", v2); 52 | 53 | println!("Equal to encoded: {:?}", v1 == v2); 54 | Ok(()) 55 | } 56 | -------------------------------------------------------------------------------- /examples/reg2json.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Igor Shaula 2 | // Licensed under the MIT License . This file 4 | // may not be copied, modified, or distributed 5 | // except according to those terms. 6 | extern crate serde_transcode; 7 | extern crate winreg; 8 | use std::error::Error; 9 | use winreg::enums::*; 10 | use winreg::RegKey; 11 | 12 | fn main() -> Result<(), Box> { 13 | let key = RegKey::predef(HKEY_CLASSES_ROOT).open_subkey("Folder")?; 14 | 15 | let mut deserializer = winreg::decoder::Decoder::from_key(&key)?; 16 | let mut serializer = serde_json::Serializer::pretty(std::io::stdout()); 17 | 18 | serde_transcode::transcode(&mut deserializer, &mut serializer)?; 19 | Ok(()) 20 | } 21 | -------------------------------------------------------------------------------- /examples/serialization.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Igor Shaula 2 | // Licensed under the MIT License . This file 4 | // may not be copied, modified, or distributed 5 | // except according to those terms. 6 | use serde_derive::{Deserialize, Serialize}; 7 | use std::collections::HashMap; 8 | use std::error::Error; 9 | use winreg::enums::*; 10 | 11 | #[derive(Debug, Serialize, Deserialize, PartialEq)] 12 | struct Coords { 13 | x: u32, 14 | y: u32, 15 | } 16 | 17 | #[derive(Debug, Serialize, Deserialize, PartialEq)] 18 | struct Size { 19 | w: u32, 20 | h: u32, 21 | } 22 | 23 | #[derive(Debug, Serialize, Deserialize, PartialEq)] 24 | struct Rectangle { 25 | coords: Option, 26 | size: Size, 27 | } 28 | 29 | #[derive(Debug, Serialize, Deserialize, PartialEq)] 30 | struct Test { 31 | t_bool: bool, 32 | t_u8: u8, 33 | t_u16: u16, 34 | t_u32: u32, 35 | t_u64: u64, 36 | t_usize: usize, 37 | t_struct: Rectangle, 38 | t_map: HashMap, 39 | t_string: String, 40 | t_optional_string: Option, 41 | #[serde(with = "serde_bytes")] 42 | t_bytes: Vec, 43 | #[serde(rename = "")] // empty name becomes the (Default) value in the registry 44 | t_char: char, 45 | t_i8: i8, 46 | t_i16: i16, 47 | t_i32: i32, 48 | t_i64: i64, 49 | t_isize: isize, 50 | t_f64: f64, 51 | t_f32: f32, 52 | } 53 | 54 | fn main() -> Result<(), Box> { 55 | let hkcu = winreg::RegKey::predef(HKEY_CURRENT_USER); 56 | let (key, _disp) = hkcu.create_subkey("Software\\RustEncode")?; 57 | 58 | let mut map = HashMap::new(); 59 | map.insert("".to_owned(), 0); // empty name becomes the (Default) value in the registry 60 | map.insert("v1".to_owned(), 1); 61 | map.insert("v2".to_owned(), 2); 62 | map.insert("v3".to_owned(), 3); 63 | 64 | let v1 = Test { 65 | t_bool: false, 66 | t_u8: 127, 67 | t_u16: 32768, 68 | t_u32: 123_456_789, 69 | t_u64: 123_456_789_101_112, 70 | t_usize: 1_234_567_891, 71 | t_struct: Rectangle { 72 | coords: Some(Coords { x: 55, y: 77 }), 73 | size: Size { w: 500, h: 300 }, 74 | }, 75 | t_map: map, 76 | t_string: "test 123!".to_owned(), 77 | t_optional_string: Some("test 456!".to_owned()), 78 | t_bytes: vec![0xDE, 0xAD, 0xBE, 0xEF], 79 | t_char: 'a', 80 | t_i8: -123, 81 | t_i16: -2049, 82 | t_i32: 20100, 83 | t_i64: -12_345_678_910, 84 | t_isize: -1_234_567_890, 85 | t_f64: -0.01, 86 | t_f32: 3.15, 87 | }; 88 | 89 | key.encode(&v1)?; 90 | 91 | let v2: Test = key.decode()?; 92 | println!("Decoded {:?}", v2); 93 | 94 | println!("Equal to encoded: {:?}", v1 == v2); 95 | Ok(()) 96 | } 97 | -------------------------------------------------------------------------------- /examples/transacted_serialization.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Igor Shaula 2 | // Licensed under the MIT License . This file 4 | // may not be copied, modified, or distributed 5 | // except according to those terms. 6 | use serde_derive::{Deserialize, Serialize}; 7 | use std::collections::HashMap; 8 | use std::error::Error; 9 | use winreg::enums::*; 10 | use winreg::transaction::Transaction; 11 | 12 | #[derive(Debug, Serialize, Deserialize, PartialEq)] 13 | struct Coords { 14 | x: u32, 15 | y: u32, 16 | } 17 | 18 | #[derive(Debug, Serialize, Deserialize, PartialEq)] 19 | struct Size { 20 | w: u32, 21 | h: u32, 22 | } 23 | 24 | #[derive(Debug, Serialize, Deserialize, PartialEq)] 25 | struct Rectangle { 26 | coords: Option, 27 | size: Size, 28 | } 29 | 30 | #[derive(Debug, Serialize, Deserialize, PartialEq)] 31 | struct Test { 32 | t_bool: bool, 33 | t_u8: u8, 34 | t_u16: u16, 35 | t_u32: u32, 36 | t_u64: u64, 37 | t_usize: usize, 38 | t_struct: Rectangle, 39 | t_map: HashMap, 40 | t_string: String, 41 | #[serde(with = "serde_bytes")] 42 | t_bytes: Vec, 43 | #[serde(rename = "")] // empty name becomes the (Default) value in the registry 44 | t_char: char, 45 | t_i8: i8, 46 | t_i16: i16, 47 | t_i32: i32, 48 | t_i64: i64, 49 | t_isize: isize, 50 | t_f64: f64, 51 | t_f32: f32, 52 | } 53 | 54 | fn main() -> Result<(), Box> { 55 | let transaction = Transaction::new()?; 56 | let hkcu = winreg::RegKey::predef(HKEY_CURRENT_USER); 57 | let (key, _disp) = hkcu.create_subkey_transacted("Software\\RustEncode", &transaction)?; 58 | 59 | let mut map = HashMap::new(); 60 | map.insert("".to_owned(), 0); // empty name becomes the (Default) value in the registry 61 | map.insert("v1".to_owned(), 1); 62 | map.insert("v2".to_owned(), 2); 63 | map.insert("v3".to_owned(), 3); 64 | 65 | let v1 = Test { 66 | t_bool: false, 67 | t_u8: 127, 68 | t_u16: 32768, 69 | t_u32: 123_456_789, 70 | t_u64: 123_456_789_101_112, 71 | t_usize: 1_234_567_891, 72 | t_struct: Rectangle { 73 | coords: Some(Coords { x: 55, y: 77 }), 74 | size: Size { w: 500, h: 300 }, 75 | }, 76 | t_map: map, 77 | t_string: "test 123!".to_owned(), 78 | t_bytes: vec![0xDE, 0xAD, 0xBE, 0xEF], 79 | t_char: 'a', 80 | t_i8: -123, 81 | t_i16: -2049, 82 | t_i32: 20100, 83 | t_i64: -12_345_678_910, 84 | t_isize: -1_234_567_890, 85 | t_f64: -0.01, 86 | t_f32: 3.15, 87 | }; 88 | 89 | key.encode_transacted(&v1, &transaction).unwrap(); 90 | transaction.commit().unwrap(); 91 | 92 | let key = hkcu.open_subkey("Software\\RustEncode")?; 93 | 94 | let v2: Test = key.decode().unwrap(); 95 | println!("Decoded {:?}", v2); 96 | 97 | println!("Equal to encoded: {:?}", v1 == v2); 98 | Ok(()) 99 | } 100 | -------------------------------------------------------------------------------- /examples/transactions.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Igor Shaula 2 | // Licensed under the MIT License . This file 4 | // may not be copied, modified, or distributed 5 | // except according to those terms. 6 | use std::io; 7 | use winreg::enums::*; 8 | use winreg::transaction::Transaction; 9 | use winreg::RegKey; 10 | 11 | fn main() -> io::Result<()> { 12 | let t = Transaction::new()?; 13 | let hkcu = RegKey::predef(HKEY_CURRENT_USER); 14 | let (key, _disp) = hkcu.create_subkey_transacted("Software\\RustTransaction", &t)?; 15 | key.set_value("TestQWORD", &1_234_567_891_011_121_314u64)?; 16 | key.set_value("TestDWORD", &1_234_567_890u32)?; 17 | 18 | println!("Commit transaction? [y/N]:"); 19 | let mut input = String::new(); 20 | io::stdin().read_line(&mut input)?; 21 | input = input.trim_end().to_owned(); 22 | if input == "y" || input == "Y" { 23 | t.commit()?; 24 | println!("Transaction committed."); 25 | } else { 26 | // this is optional, if transaction wasn't committed, 27 | // it will be rolled back on disposal 28 | t.rollback()?; 29 | 30 | println!("Transaction wasn't committed, it will be rolled back."); 31 | } 32 | 33 | Ok(()) 34 | } 35 | -------------------------------------------------------------------------------- /src/common.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Igor Shaula 2 | // Licensed under the MIT License . This file 4 | // may not be copied, modified, or distributed 5 | // except according to those terms. 6 | #![macro_use] 7 | use std::ffi::OsStr; 8 | use std::os::windows::ffi::OsStrExt; 9 | use std::slice; 10 | 11 | macro_rules! werr { 12 | ($e:expr) => { 13 | Err(io::Error::from_raw_os_error($e as i32)) 14 | }; 15 | } 16 | 17 | pub(crate) fn to_utf16>(s: P) -> Vec { 18 | s.as_ref().encode_wide().chain(Some(0)).collect() 19 | } 20 | 21 | pub(crate) fn v16_to_v8(v: &[u16]) -> Vec { 22 | unsafe { slice::from_raw_parts(v.as_ptr() as *const u8, v.len() * 2).to_vec() } 23 | } 24 | -------------------------------------------------------------------------------- /src/decoder/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Igor Shaula 2 | // Licensed under the MIT License . This file 4 | // may not be copied, modified, or distributed 5 | // except according to those terms. 6 | use crate::enums::*; 7 | use crate::reg_key::RegKey; 8 | use crate::reg_value::RegValue; 9 | use crate::types::FromRegValue; 10 | use std::error::Error; 11 | use std::fmt; 12 | use std::io; 13 | 14 | macro_rules! parse_string { 15 | ($s:ident) => {{ 16 | let s: String = $s.read_value()?; 17 | s.parse() 18 | .map_err(|e| DecoderError::ParseError(format!("{:?}", e))) 19 | }}; 20 | } 21 | 22 | macro_rules! no_impl { 23 | ($e:expr) => { 24 | Err(DecoderError::DecodeNotImplemented($e.to_owned())) 25 | }; 26 | } 27 | 28 | #[cfg(feature = "serialization-serde")] 29 | mod serialization_serde; 30 | 31 | #[derive(Debug)] 32 | pub enum DecoderError { 33 | DecodeNotImplemented(String), 34 | DeserializerError(String), 35 | IoError(io::Error), 36 | ParseError(String), 37 | NoFieldName, 38 | } 39 | 40 | impl fmt::Display for DecoderError { 41 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 42 | write!(f, "{:?}", self) 43 | } 44 | } 45 | 46 | impl Error for DecoderError {} 47 | 48 | impl From for DecoderError { 49 | fn from(err: io::Error) -> DecoderError { 50 | DecoderError::IoError(err) 51 | } 52 | } 53 | 54 | pub type DecodeResult = Result; 55 | 56 | #[derive(Debug, Clone)] 57 | enum DecoderCursor { 58 | Start, 59 | Key(u32), 60 | KeyName(u32, String), 61 | KeyVal(u32, String), 62 | Field(u32), 63 | FieldName(u32, String), 64 | FieldVal(u32, String), 65 | } 66 | 67 | #[derive(Debug)] 68 | pub struct Decoder { 69 | key: RegKey, 70 | cursor: DecoderCursor, 71 | } 72 | 73 | const DECODER_SAM: u32 = KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS; 74 | 75 | impl Decoder { 76 | pub fn from_key(key: &RegKey) -> DecodeResult { 77 | key.open_subkey_with_flags("", DECODER_SAM) 78 | .map(Decoder::new) 79 | .map_err(DecoderError::IoError) 80 | } 81 | 82 | fn new(key: RegKey) -> Decoder { 83 | Decoder { 84 | key, 85 | cursor: DecoderCursor::Start, 86 | } 87 | } 88 | 89 | fn read_value(&mut self) -> Result { 90 | use self::DecoderCursor::*; 91 | let cursor = self.cursor.clone(); 92 | match cursor { 93 | FieldVal(index, name) => { 94 | self.cursor = DecoderCursor::Field(index + 1); 95 | self.key.get_value(name).map_err(DecoderError::IoError) 96 | } 97 | _ => Err(DecoderError::DeserializerError("Not a value".to_owned())), 98 | } 99 | } 100 | 101 | fn read_bytes(&mut self) -> Result, DecoderError> { 102 | use self::DecoderCursor::*; 103 | let cursor = self.cursor.clone(); 104 | match cursor { 105 | FieldVal(index, name) => { 106 | self.cursor = DecoderCursor::Field(index + 1); 107 | let RegValue { bytes, .. } = self 108 | .key 109 | .get_raw_value(name) 110 | .map_err(DecoderError::IoError)?; 111 | Ok(bytes) 112 | } 113 | _ => Err(DecoderError::DeserializerError("Not a value".to_owned())), 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/decoder/serialization_serde.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Igor Shaula 2 | // Licensed under the MIT License . This file 4 | // may not be copied, modified, or distributed 5 | // except according to those terms. 6 | use super::{DecodeResult, Decoder, DecoderCursor, DecoderError, DECODER_SAM}; 7 | use crate::{types::FromRegValue, RegValue}; 8 | use serde::de::*; 9 | use std::fmt; 10 | 11 | impl Error for DecoderError { 12 | fn custom(msg: T) -> Self { 13 | DecoderError::DeserializerError(format!("{}", msg)) 14 | } 15 | } 16 | 17 | impl<'de> Deserializer<'de> for &mut Decoder { 18 | type Error = DecoderError; 19 | fn deserialize_any(self, visitor: V) -> DecodeResult 20 | where 21 | V: Visitor<'de>, 22 | { 23 | use super::DecoderCursor::*; 24 | let cursor = self.cursor.clone(); 25 | match cursor { 26 | Start => self.deserialize_map(visitor), 27 | KeyName(..) | FieldName(..) => self.deserialize_string(visitor), 28 | FieldVal(index, name) => { 29 | use crate::enums::RegType::*; 30 | let v = self.key.get_raw_value(name)?; 31 | self.cursor = Field(index + 1); 32 | match v.vtype { 33 | REG_SZ | REG_EXPAND_SZ | REG_MULTI_SZ => { 34 | visitor.visit_string(String::from_reg_value(&v)?) 35 | } 36 | REG_DWORD => visitor.visit_u32(u32::from_reg_value(&v)?), 37 | REG_QWORD => visitor.visit_u64(u64::from_reg_value(&v)?), 38 | REG_BINARY => visitor.visit_byte_buf(v.bytes), 39 | REG_NONE => visitor.visit_none(), 40 | _ => no_impl!(format!( 41 | "value type deserialization not implemented {:?}", 42 | v.vtype 43 | )), 44 | } 45 | } 46 | _ => no_impl!("deserialize_any"), 47 | } 48 | } 49 | 50 | fn deserialize_bool(self, visitor: V) -> DecodeResult 51 | where 52 | V: Visitor<'de>, 53 | { 54 | visitor.visit_bool(self.read_value().map(|v: u32| v > 0)?) 55 | } 56 | 57 | fn deserialize_u8(self, visitor: V) -> DecodeResult 58 | where 59 | V: Visitor<'de>, 60 | { 61 | self.deserialize_u32(visitor) 62 | } 63 | 64 | fn deserialize_u16(self, visitor: V) -> DecodeResult 65 | where 66 | V: Visitor<'de>, 67 | { 68 | self.deserialize_u32(visitor) 69 | } 70 | 71 | fn deserialize_u32(self, visitor: V) -> DecodeResult 72 | where 73 | V: Visitor<'de>, 74 | { 75 | visitor.visit_u32(self.read_value()?) 76 | } 77 | 78 | fn deserialize_u64(self, visitor: V) -> DecodeResult 79 | where 80 | V: Visitor<'de>, 81 | { 82 | visitor.visit_u64(self.read_value()?) 83 | } 84 | 85 | fn deserialize_i8(self, visitor: V) -> DecodeResult 86 | where 87 | V: Visitor<'de>, 88 | { 89 | visitor.visit_i8(parse_string!(self)?) 90 | } 91 | 92 | fn deserialize_i16(self, visitor: V) -> DecodeResult 93 | where 94 | V: Visitor<'de>, 95 | { 96 | visitor.visit_i16(parse_string!(self)?) 97 | } 98 | 99 | fn deserialize_i32(self, visitor: V) -> DecodeResult 100 | where 101 | V: Visitor<'de>, 102 | { 103 | visitor.visit_i32(parse_string!(self)?) 104 | } 105 | 106 | fn deserialize_i64(self, visitor: V) -> DecodeResult 107 | where 108 | V: Visitor<'de>, 109 | { 110 | visitor.visit_i64(parse_string!(self)?) 111 | } 112 | 113 | fn deserialize_f32(self, visitor: V) -> DecodeResult 114 | where 115 | V: Visitor<'de>, 116 | { 117 | visitor.visit_f32(parse_string!(self)?) 118 | } 119 | 120 | fn deserialize_f64(self, visitor: V) -> DecodeResult 121 | where 122 | V: Visitor<'de>, 123 | { 124 | visitor.visit_f64(parse_string!(self)?) 125 | } 126 | 127 | fn deserialize_char(self, visitor: V) -> DecodeResult 128 | where 129 | V: Visitor<'de>, 130 | { 131 | self.deserialize_string(visitor) 132 | } 133 | 134 | fn deserialize_str(self, _visitor: V) -> DecodeResult 135 | where 136 | V: Visitor<'de>, 137 | { 138 | no_impl!("deserialize_str") 139 | } 140 | 141 | fn deserialize_string(self, visitor: V) -> DecodeResult 142 | where 143 | V: Visitor<'de>, 144 | { 145 | use super::DecoderCursor::*; 146 | let cursor = self.cursor.clone(); 147 | match cursor { 148 | KeyName(index, name) => { 149 | self.cursor = DecoderCursor::KeyVal(index, name.clone()); 150 | visitor.visit_string(name) 151 | } 152 | FieldName(index, name) => { 153 | self.cursor = DecoderCursor::FieldVal(index, name.clone()); 154 | visitor.visit_string(name) 155 | } 156 | FieldVal(..) => visitor.visit_string(self.read_value()?), 157 | _ => Err(DecoderError::NoFieldName), 158 | } 159 | } 160 | 161 | fn deserialize_bytes(self, _visitor: V) -> DecodeResult 162 | where 163 | V: Visitor<'de>, 164 | { 165 | no_impl!("deserialize_bytes") 166 | } 167 | 168 | fn deserialize_byte_buf(self, visitor: V) -> DecodeResult 169 | where 170 | V: Visitor<'de>, 171 | { 172 | visitor.visit_byte_buf(self.read_bytes()?) 173 | } 174 | 175 | fn deserialize_option(self, visitor: V) -> DecodeResult 176 | where 177 | V: Visitor<'de>, 178 | { 179 | let v = { 180 | use super::DecoderCursor::*; 181 | match self.cursor { 182 | Start => return visitor.visit_some(&mut *self), 183 | FieldVal(index, ref name) => self 184 | .key 185 | .get_raw_value(name) 186 | .map_err(DecoderError::IoError) 187 | .and_then(|v| match v { 188 | RegValue { 189 | vtype: crate::enums::RegType::REG_NONE, 190 | .. 191 | } => { 192 | self.cursor = DecoderCursor::Field(index + 1); 193 | Err(DecoderError::DeserializerError("Found REG_NONE".to_owned())) 194 | } 195 | val => Ok(val), 196 | }), 197 | _ => Err(DecoderError::DeserializerError("Nothing found".to_owned())), 198 | } 199 | }; 200 | match v { 201 | Ok(..) => visitor.visit_some(&mut *self), 202 | Err(..) => visitor.visit_none(), 203 | } 204 | } 205 | 206 | fn deserialize_unit(self, _visitor: V) -> DecodeResult 207 | where 208 | V: Visitor<'de>, 209 | { 210 | no_impl!("deserialize_unit") 211 | } 212 | 213 | fn deserialize_unit_struct(self, _name: &'static str, _visitor: V) -> DecodeResult 214 | where 215 | V: Visitor<'de>, 216 | { 217 | no_impl!("deserialize_unit_struct") 218 | } 219 | 220 | fn deserialize_newtype_struct( 221 | self, 222 | _name: &'static str, 223 | _visitor: V, 224 | ) -> DecodeResult 225 | where 226 | V: Visitor<'de>, 227 | { 228 | no_impl!("deserialize_newtype_struct") 229 | } 230 | 231 | fn deserialize_seq(self, _visitor: V) -> DecodeResult 232 | where 233 | V: Visitor<'de>, 234 | { 235 | no_impl!("deserialize_seq") 236 | } 237 | 238 | fn deserialize_tuple(self, _len: usize, _visitor: V) -> DecodeResult 239 | where 240 | V: Visitor<'de>, 241 | { 242 | no_impl!("deserialize_tuple") 243 | } 244 | 245 | fn deserialize_tuple_struct( 246 | self, 247 | _name: &'static str, 248 | _len: usize, 249 | _visitor: V, 250 | ) -> DecodeResult 251 | where 252 | V: Visitor<'de>, 253 | { 254 | no_impl!("deserialize_tuple_struct") 255 | } 256 | 257 | fn deserialize_map(self, visitor: V) -> DecodeResult 258 | where 259 | V: Visitor<'de>, 260 | { 261 | visitor.visit_map(self) 262 | } 263 | 264 | fn deserialize_struct( 265 | self, 266 | _name: &'static str, 267 | _fields: &'static [&'static str], 268 | visitor: V, 269 | ) -> DecodeResult 270 | where 271 | V: Visitor<'de>, 272 | { 273 | visitor.visit_map(self) 274 | } 275 | 276 | fn deserialize_identifier(self, visitor: V) -> DecodeResult 277 | where 278 | V: Visitor<'de>, 279 | { 280 | self.deserialize_string(visitor) 281 | } 282 | 283 | fn deserialize_enum( 284 | self, 285 | _name: &'static str, 286 | _variants: &'static [&'static str], 287 | _visitor: V, 288 | ) -> DecodeResult 289 | where 290 | V: Visitor<'de>, 291 | { 292 | no_impl!("deserialize_enum") 293 | } 294 | 295 | fn deserialize_ignored_any(self, visitor: V) -> DecodeResult 296 | where 297 | V: Visitor<'de>, 298 | { 299 | self.deserialize_any(visitor) 300 | } 301 | } 302 | 303 | impl<'de> MapAccess<'de> for Decoder { 304 | type Error = DecoderError; 305 | fn next_key_seed(&mut self, seed: K) -> Result, Self::Error> 306 | where 307 | K: DeserializeSeed<'de>, 308 | { 309 | use super::DecoderCursor::*; 310 | match self.cursor { 311 | Start => { 312 | self.cursor = Key(0); 313 | self.next_key_seed(seed) 314 | } 315 | Key(index) => match self.key.enum_key(index) { 316 | Some(res) => { 317 | self.cursor = KeyName(index, res?); 318 | seed.deserialize(&mut *self).map(Some) 319 | } 320 | None => { 321 | self.cursor = Field(0); 322 | self.next_key_seed(seed) 323 | } 324 | }, 325 | Field(index) => { 326 | let next_value = self.key.enum_value(index); 327 | match next_value { 328 | Some(res) => { 329 | self.cursor = FieldName(index, res?.0); 330 | seed.deserialize(&mut *self).map(Some) 331 | } 332 | None => Ok(None), 333 | } 334 | } 335 | _ => no_impl!("Wrong cursor state (key)"), 336 | } 337 | } 338 | 339 | fn next_value_seed(&mut self, seed: V) -> Result 340 | where 341 | V: DeserializeSeed<'de>, 342 | { 343 | use super::DecoderCursor::*; 344 | match self.cursor { 345 | KeyVal(index, ref name) => match self.key.open_subkey_with_flags(name, DECODER_SAM) { 346 | Ok(subkey) => { 347 | let mut nested = Decoder::new(subkey); 348 | self.cursor = Key(index + 1); 349 | seed.deserialize(&mut nested) 350 | } 351 | Err(err) => Err(DecoderError::IoError(err)), 352 | }, 353 | FieldVal(..) => seed.deserialize(&mut *self), 354 | _ => no_impl!("Wrong cursor state (field)"), 355 | } 356 | } 357 | } 358 | -------------------------------------------------------------------------------- /src/encoder/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Igor Shaula 2 | // Licensed under the MIT License . This file 4 | // may not be copied, modified, or distributed 5 | // except according to those terms. 6 | use self::EncoderState::*; 7 | use crate::enums::*; 8 | use crate::reg_key::RegKey; 9 | use crate::transaction::Transaction; 10 | use std::error::Error; 11 | use std::fmt; 12 | use std::io; 13 | 14 | macro_rules! emit_value { 15 | ($s:ident, $v:ident) => { 16 | match mem::replace(&mut $s.state, Start) { 17 | NextKey(ref s) => $s.keys[$s.keys.len() - 1] 18 | .set_value(s, &$v) 19 | .map_err(EncoderError::IoError), 20 | Start => Err(EncoderError::NoFieldName), 21 | } 22 | }; 23 | } 24 | 25 | macro_rules! no_impl { 26 | ($e:expr) => { 27 | Err(EncoderError::EncodeNotImplemented($e.to_owned())) 28 | }; 29 | } 30 | 31 | #[cfg(feature = "serialization-serde")] 32 | mod serialization_serde; 33 | 34 | #[derive(Debug)] 35 | pub enum EncoderError { 36 | EncodeNotImplemented(String), 37 | SerializerError(String), 38 | IoError(io::Error), 39 | NoFieldName, 40 | KeyMustBeAString, 41 | } 42 | 43 | impl fmt::Display for EncoderError { 44 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 45 | write!(f, "{:?}", self) 46 | } 47 | } 48 | 49 | impl Error for EncoderError {} 50 | 51 | pub type EncodeResult = Result; 52 | 53 | impl From for EncoderError { 54 | fn from(err: io::Error) -> EncoderError { 55 | EncoderError::IoError(err) 56 | } 57 | } 58 | 59 | #[derive(Debug)] 60 | enum EncoderState { 61 | Start, 62 | NextKey(String), 63 | // NextMapKey, 64 | } 65 | 66 | #[derive(Debug)] 67 | pub struct Encoder> { 68 | keys: Vec, 69 | tr: Tr, 70 | state: EncoderState, 71 | } 72 | 73 | const ENCODER_SAM: u32 = KEY_CREATE_SUB_KEY | KEY_SET_VALUE; 74 | 75 | impl Encoder { 76 | pub fn from_key(key: &RegKey) -> EncodeResult> { 77 | let tr = Transaction::new()?; 78 | key.open_subkey_transacted_with_flags("", &tr, ENCODER_SAM) 79 | .map(|k| Encoder::new(k, tr)) 80 | .map_err(EncoderError::IoError) 81 | } 82 | 83 | fn new(key: RegKey, tr: Transaction) -> Encoder { 84 | let mut keys = Vec::with_capacity(5); 85 | keys.push(key); 86 | Encoder { 87 | keys, 88 | tr, 89 | state: Start, 90 | } 91 | } 92 | 93 | pub fn commit(self) -> EncodeResult<()> { 94 | self.tr.commit().map_err(EncoderError::IoError) 95 | } 96 | } 97 | 98 | impl Encoder<&Transaction> { 99 | pub fn from_key_transacted<'a>( 100 | key: &RegKey, 101 | tr: &'a Transaction, 102 | ) -> EncodeResult> { 103 | key.open_subkey_transacted_with_flags("", tr, ENCODER_SAM) 104 | .map(|k| Encoder::new_transacted(k, tr)) 105 | .map_err(EncoderError::IoError) 106 | } 107 | 108 | fn new_transacted(key: RegKey, tr: &Transaction) -> Encoder<&Transaction> { 109 | let mut keys = Vec::with_capacity(5); 110 | keys.push(key); 111 | Encoder { 112 | keys, 113 | tr, 114 | state: Start, 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/encoder/serialization_serde.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017, Igor Shaula 2 | // Licensed under the MIT License . This file 4 | // may not be copied, modified, or distributed 5 | // except according to those terms. 6 | use super::EncoderState::*; 7 | use super::{EncodeResult, Encoder, EncoderError, ENCODER_SAM}; 8 | use crate::enums::RegType; 9 | use crate::transaction::Transaction; 10 | use crate::RegValue; 11 | use serde::ser::*; 12 | use std::fmt; 13 | use std::mem; 14 | 15 | impl Error for EncoderError { 16 | fn custom(msg: T) -> Self { 17 | EncoderError::SerializerError(format!("{}", msg)) 18 | } 19 | } 20 | 21 | impl<'a, Tr: AsRef> Serializer for &'a mut Encoder { 22 | type Ok = (); 23 | type Error = EncoderError; 24 | 25 | type SerializeSeq = SeqEncoder; 26 | type SerializeTuple = TupleEncoder; 27 | type SerializeTupleStruct = TupleStructEncoder; 28 | type SerializeTupleVariant = TupleVariantEncoder; 29 | type SerializeMap = StructMapEncoder<'a, Tr>; 30 | type SerializeStruct = StructMapEncoder<'a, Tr>; 31 | type SerializeStructVariant = StructVariantEncoder; 32 | 33 | fn serialize_bool(self, value: bool) -> EncodeResult { 34 | self.serialize_u32(value as u32) 35 | } 36 | 37 | fn serialize_i8(self, value: i8) -> EncodeResult { 38 | self.serialize_i64(value as i64) 39 | } 40 | 41 | fn serialize_i16(self, value: i16) -> EncodeResult { 42 | self.serialize_i64(value as i64) 43 | } 44 | 45 | fn serialize_i32(self, value: i32) -> EncodeResult { 46 | self.serialize_i64(value as i64) 47 | } 48 | 49 | fn serialize_i64(self, value: i64) -> EncodeResult { 50 | let s = value.to_string(); 51 | emit_value!(self, s) 52 | } 53 | 54 | fn serialize_u8(self, value: u8) -> EncodeResult { 55 | self.serialize_u32(value as u32) 56 | } 57 | 58 | fn serialize_u16(self, value: u16) -> EncodeResult { 59 | self.serialize_u32(value as u32) 60 | } 61 | 62 | fn serialize_u32(self, value: u32) -> EncodeResult { 63 | emit_value!(self, value) 64 | } 65 | 66 | fn serialize_u64(self, value: u64) -> EncodeResult { 67 | emit_value!(self, value) 68 | } 69 | 70 | fn serialize_f32(self, value: f32) -> EncodeResult { 71 | let s = value.to_string(); 72 | emit_value!(self, s) 73 | } 74 | 75 | fn serialize_f64(self, value: f64) -> EncodeResult { 76 | let s = value.to_string(); 77 | emit_value!(self, s) 78 | } 79 | 80 | fn serialize_char(self, value: char) -> EncodeResult { 81 | let mut s = String::new(); 82 | s.push(value); 83 | emit_value!(self, s) 84 | } 85 | 86 | fn serialize_str(self, value: &str) -> EncodeResult { 87 | emit_value!(self, value) 88 | } 89 | 90 | fn serialize_bytes(self, value: &[u8]) -> EncodeResult { 91 | match mem::replace(&mut self.state, Start) { 92 | NextKey(ref s) => { 93 | let vec = Vec::from(value); 94 | self.keys[self.keys.len() - 1] 95 | .set_raw_value( 96 | s, 97 | &RegValue { 98 | bytes: vec, 99 | vtype: RegType::REG_BINARY, 100 | }, 101 | ) 102 | .map_err(EncoderError::IoError) 103 | } 104 | Start => Err(EncoderError::NoFieldName), 105 | } 106 | } 107 | 108 | fn serialize_none(self) -> EncodeResult { 109 | match mem::replace(&mut self.state, Start) { 110 | NextKey(..) => Ok(()), 111 | Start => Err(EncoderError::NoFieldName), 112 | } 113 | } 114 | 115 | fn serialize_some(self, value: &T) -> EncodeResult { 116 | value.serialize(self) 117 | } 118 | 119 | fn serialize_unit(self) -> EncodeResult { 120 | no_impl!("serialize_unit") 121 | } 122 | 123 | fn serialize_unit_struct(self, _name: &'static str) -> EncodeResult { 124 | no_impl!("serialize_unit_struct") 125 | } 126 | 127 | fn serialize_unit_variant( 128 | self, 129 | _name: &'static str, 130 | _variant_index: u32, 131 | _variant: &'static str, 132 | ) -> EncodeResult { 133 | no_impl!("serialize_unit_variant") 134 | } 135 | 136 | fn serialize_newtype_struct( 137 | self, 138 | _name: &'static str, 139 | _value: &T, 140 | ) -> EncodeResult { 141 | no_impl!("serialize_newtype_struct") 142 | } 143 | 144 | fn serialize_newtype_variant( 145 | self, 146 | _name: &'static str, 147 | _variant_index: u32, 148 | _variant: &'static str, 149 | _value: &T, 150 | ) -> EncodeResult { 151 | no_impl!("serialize_newtype_variant") 152 | } 153 | 154 | fn serialize_seq(self, _len: Option) -> EncodeResult { 155 | no_impl!("serialize_seq") 156 | } 157 | 158 | fn serialize_tuple(self, _len: usize) -> EncodeResult { 159 | no_impl!("serialize_tuple") 160 | } 161 | 162 | fn serialize_tuple_struct( 163 | self, 164 | _name: &'static str, 165 | _len: usize, 166 | ) -> EncodeResult { 167 | no_impl!("serialize_tuple_struct") 168 | } 169 | 170 | fn serialize_tuple_variant( 171 | self, 172 | _name: &'static str, 173 | _variant_index: u32, 174 | _variant: &'static str, 175 | _len: usize, 176 | ) -> EncodeResult { 177 | no_impl!("serialize_tuple_variant") 178 | } 179 | 180 | fn serialize_map(self, _len: Option) -> EncodeResult { 181 | match mem::replace(&mut self.state, Start) { 182 | // --- 183 | Start => { 184 | // root structure 185 | Ok(StructMapEncoder { 186 | enc: self, 187 | is_root: true, 188 | }) 189 | } 190 | NextKey(ref s) => { 191 | // nested structure 192 | match self.keys[self.keys.len() - 1].create_subkey_transacted_with_flags( 193 | s, 194 | self.tr.as_ref(), 195 | ENCODER_SAM, 196 | ) { 197 | Ok((subkey, _disp)) => { 198 | self.keys.push(subkey); 199 | Ok(StructMapEncoder { 200 | enc: self, 201 | is_root: true, 202 | }) 203 | } 204 | Err(err) => Err(EncoderError::IoError(err)), 205 | } 206 | } 207 | } 208 | } 209 | 210 | fn serialize_struct( 211 | self, 212 | _name: &'static str, 213 | len: usize, 214 | ) -> EncodeResult { 215 | self.serialize_map(Some(len)) 216 | } 217 | 218 | fn serialize_struct_variant( 219 | self, 220 | _name: &'static str, 221 | _variant_index: u32, 222 | _variant: &'static str, 223 | _len: usize, 224 | ) -> EncodeResult { 225 | no_impl!("serialize_struct_variant") 226 | } 227 | } 228 | 229 | pub struct SeqEncoder {} 230 | 231 | impl SerializeSeq for SeqEncoder { 232 | type Ok = (); 233 | type Error = EncoderError; 234 | fn serialize_element(&mut self, _value: &T) -> EncodeResult { 235 | no_impl!("SerializeSeq::serialize_element") 236 | } 237 | fn end(self) -> EncodeResult { 238 | no_impl!("SerializeSeq::end") 239 | } 240 | } 241 | 242 | pub struct TupleEncoder {} 243 | 244 | impl SerializeTuple for TupleEncoder { 245 | type Ok = (); 246 | type Error = EncoderError; 247 | 248 | fn serialize_element(&mut self, _value: &T) -> EncodeResult { 249 | no_impl!("SerializeTuple::serialize_element") 250 | } 251 | 252 | fn end(self) -> EncodeResult { 253 | no_impl!("SerializeTuple::end") 254 | } 255 | } 256 | 257 | pub struct TupleStructEncoder {} 258 | 259 | impl SerializeTupleStruct for TupleStructEncoder { 260 | type Ok = (); 261 | type Error = EncoderError; 262 | 263 | fn serialize_field(&mut self, _value: &T) -> EncodeResult { 264 | no_impl!("SerializeTupleStruct::serialize_field") 265 | } 266 | 267 | fn end(self) -> EncodeResult { 268 | no_impl!("SerializeTupleStruct::end") 269 | } 270 | } 271 | 272 | pub struct TupleVariantEncoder {} 273 | 274 | impl SerializeTupleVariant for TupleVariantEncoder { 275 | type Ok = (); 276 | type Error = EncoderError; 277 | 278 | fn serialize_field(&mut self, _value: &T) -> EncodeResult { 279 | no_impl!("SerializeTupleVariant::serialize_field") 280 | } 281 | 282 | fn end(self) -> EncodeResult { 283 | no_impl!("SerializeTupleVariant::end") 284 | } 285 | } 286 | 287 | struct MapKeySerializer; 288 | 289 | impl serde::Serializer for MapKeySerializer { 290 | type Ok = String; 291 | type Error = EncoderError; 292 | 293 | type SerializeSeq = Impossible; 294 | type SerializeTuple = Impossible; 295 | type SerializeTupleStruct = Impossible; 296 | type SerializeTupleVariant = Impossible; 297 | type SerializeMap = Impossible; 298 | type SerializeStruct = Impossible; 299 | type SerializeStructVariant = Impossible; 300 | 301 | #[inline] 302 | fn serialize_unit_variant( 303 | self, 304 | _name: &'static str, 305 | _variant_index: u32, 306 | variant: &'static str, 307 | ) -> EncodeResult { 308 | Ok(variant.to_owned()) 309 | } 310 | 311 | #[inline] 312 | fn serialize_newtype_struct(self, _name: &'static str, value: &T) -> EncodeResult 313 | where 314 | T: ?Sized + Serialize, 315 | { 316 | value.serialize(self) 317 | } 318 | 319 | fn serialize_bool(self, _value: bool) -> EncodeResult { 320 | Err(EncoderError::KeyMustBeAString) 321 | } 322 | 323 | fn serialize_i8(self, value: i8) -> EncodeResult { 324 | Ok(value.to_string()) 325 | } 326 | 327 | fn serialize_i16(self, value: i16) -> EncodeResult { 328 | Ok(value.to_string()) 329 | } 330 | 331 | fn serialize_i32(self, value: i32) -> EncodeResult { 332 | Ok(value.to_string()) 333 | } 334 | 335 | fn serialize_i64(self, value: i64) -> EncodeResult { 336 | Ok(value.to_string()) 337 | } 338 | 339 | fn serialize_u8(self, value: u8) -> EncodeResult { 340 | Ok(value.to_string()) 341 | } 342 | 343 | fn serialize_u16(self, value: u16) -> EncodeResult { 344 | Ok(value.to_string()) 345 | } 346 | 347 | fn serialize_u32(self, value: u32) -> EncodeResult { 348 | Ok(value.to_string()) 349 | } 350 | 351 | fn serialize_u64(self, value: u64) -> EncodeResult { 352 | Ok(value.to_string()) 353 | } 354 | 355 | fn serialize_f32(self, _value: f32) -> EncodeResult { 356 | Err(EncoderError::KeyMustBeAString) 357 | } 358 | 359 | fn serialize_f64(self, _value: f64) -> EncodeResult { 360 | Err(EncoderError::KeyMustBeAString) 361 | } 362 | 363 | #[inline] 364 | fn serialize_char(self, value: char) -> EncodeResult { 365 | Ok({ 366 | let mut s = String::new(); 367 | s.push(value); 368 | s 369 | }) 370 | } 371 | 372 | #[inline] 373 | fn serialize_str(self, value: &str) -> EncodeResult { 374 | Ok(value.to_owned()) 375 | } 376 | 377 | fn serialize_bytes(self, _value: &[u8]) -> EncodeResult { 378 | Err(EncoderError::KeyMustBeAString) 379 | } 380 | 381 | fn serialize_unit(self) -> EncodeResult { 382 | Err(EncoderError::KeyMustBeAString) 383 | } 384 | 385 | fn serialize_unit_struct(self, _name: &'static str) -> EncodeResult { 386 | Err(EncoderError::KeyMustBeAString) 387 | } 388 | 389 | fn serialize_newtype_variant( 390 | self, 391 | _name: &'static str, 392 | _variant_index: u32, 393 | _variant: &'static str, 394 | _value: &T, 395 | ) -> EncodeResult 396 | where 397 | T: ?Sized + Serialize, 398 | { 399 | Err(EncoderError::KeyMustBeAString) 400 | } 401 | 402 | fn serialize_none(self) -> EncodeResult { 403 | Err(EncoderError::KeyMustBeAString) 404 | } 405 | 406 | fn serialize_some(self, _value: &T) -> EncodeResult 407 | where 408 | T: ?Sized + Serialize, 409 | { 410 | Err(EncoderError::KeyMustBeAString) 411 | } 412 | 413 | fn serialize_seq(self, _len: Option) -> EncodeResult { 414 | Err(EncoderError::KeyMustBeAString) 415 | } 416 | 417 | fn serialize_tuple(self, _len: usize) -> EncodeResult { 418 | Err(EncoderError::KeyMustBeAString) 419 | } 420 | 421 | fn serialize_tuple_struct( 422 | self, 423 | _name: &'static str, 424 | _len: usize, 425 | ) -> EncodeResult { 426 | Err(EncoderError::KeyMustBeAString) 427 | } 428 | 429 | fn serialize_tuple_variant( 430 | self, 431 | _name: &'static str, 432 | _variant_index: u32, 433 | _variant: &'static str, 434 | _len: usize, 435 | ) -> EncodeResult { 436 | Err(EncoderError::KeyMustBeAString) 437 | } 438 | 439 | fn serialize_map(self, _len: Option) -> EncodeResult { 440 | Err(EncoderError::KeyMustBeAString) 441 | } 442 | 443 | fn serialize_struct( 444 | self, 445 | _name: &'static str, 446 | _len: usize, 447 | ) -> EncodeResult { 448 | Err(EncoderError::KeyMustBeAString) 449 | } 450 | 451 | fn serialize_struct_variant( 452 | self, 453 | _name: &'static str, 454 | _variant_index: u32, 455 | _variant: &'static str, 456 | _len: usize, 457 | ) -> EncodeResult { 458 | Err(EncoderError::KeyMustBeAString) 459 | } 460 | 461 | fn collect_str(self, value: &T) -> EncodeResult 462 | where 463 | T: fmt::Display + ?Sized, 464 | { 465 | Ok(value.to_string()) 466 | } 467 | } 468 | 469 | pub struct StructMapEncoder<'a, Tr: AsRef> { 470 | enc: &'a mut Encoder, 471 | is_root: bool, 472 | } 473 | 474 | impl> SerializeStruct for StructMapEncoder<'_, Tr> { 475 | type Ok = (); 476 | type Error = EncoderError; 477 | 478 | fn serialize_field( 479 | &mut self, 480 | key: &'static str, 481 | value: &T, 482 | ) -> EncodeResult { 483 | self.enc.state = NextKey(String::from(key)); 484 | value.serialize(&mut *self.enc) 485 | } 486 | 487 | fn end(self) -> EncodeResult { 488 | if self.is_root { 489 | self.enc.keys.pop(); 490 | } 491 | Ok(()) 492 | } 493 | } 494 | 495 | impl> SerializeMap for StructMapEncoder<'_, Tr> { 496 | type Ok = (); 497 | type Error = EncoderError; 498 | 499 | fn serialize_key(&mut self, key: &T) -> EncodeResult { 500 | self.enc.state = NextKey(key.serialize(MapKeySerializer)?); 501 | Ok(()) 502 | } 503 | 504 | fn serialize_value(&mut self, value: &T) -> EncodeResult { 505 | value.serialize(&mut *self.enc) 506 | } 507 | 508 | fn end(self) -> EncodeResult { 509 | if self.is_root { 510 | self.enc.keys.pop(); 511 | } 512 | Ok(()) 513 | } 514 | } 515 | 516 | pub struct StructVariantEncoder {} 517 | 518 | impl SerializeStructVariant for StructVariantEncoder { 519 | type Ok = (); 520 | type Error = EncoderError; 521 | 522 | fn serialize_field( 523 | &mut self, 524 | _key: &'static str, 525 | _value: &T, 526 | ) -> EncodeResult { 527 | no_impl!("SerializeStructVariant::serialize_field") 528 | } 529 | 530 | fn end(self) -> EncodeResult { 531 | no_impl!("SerializeStructVariant::end") 532 | } 533 | } 534 | -------------------------------------------------------------------------------- /src/enums.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Igor Shaula 2 | // Licensed under the MIT License . This file 4 | // may not be copied, modified, or distributed 5 | // except according to those terms. 6 | 7 | //! `use winreg::enums::*;` to import all needed enumerations and constants 8 | pub use windows_sys::Win32::System::Registry::{ 9 | HKEY_CLASSES_ROOT, HKEY_CURRENT_CONFIG, HKEY_CURRENT_USER, HKEY_CURRENT_USER_LOCAL_SETTINGS, 10 | HKEY_DYN_DATA, HKEY_LOCAL_MACHINE, HKEY_PERFORMANCE_DATA, HKEY_PERFORMANCE_NLSTEXT, 11 | HKEY_PERFORMANCE_TEXT, HKEY_USERS, KEY_ALL_ACCESS, KEY_CREATE_LINK, KEY_CREATE_SUB_KEY, 12 | KEY_ENUMERATE_SUB_KEYS, KEY_EXECUTE, KEY_NOTIFY, KEY_QUERY_VALUE, KEY_READ, KEY_SET_VALUE, 13 | KEY_WOW64_32KEY, KEY_WOW64_64KEY, KEY_WOW64_RES, KEY_WRITE, REG_OPTION_BACKUP_RESTORE, 14 | REG_OPTION_CREATE_LINK, REG_OPTION_DONT_VIRTUALIZE, REG_OPTION_NON_VOLATILE, 15 | REG_OPTION_OPEN_LINK, REG_OPTION_RESERVED, REG_OPTION_VOLATILE, REG_PROCESS_APPKEY, 16 | }; 17 | 18 | macro_rules! winapi_enum{ 19 | ($t:ident, $doc:expr => [$($v:ident),*]) => ( 20 | #[doc=$doc] 21 | #[allow(non_camel_case_types)] 22 | #[derive(Debug,Clone,PartialEq)] 23 | pub enum $t { 24 | $( $v = windows_sys::Win32::System::Registry::$v as isize ),* 25 | } 26 | ) 27 | } 28 | 29 | winapi_enum!(RegType, "Enumeration of possible registry value types" => [ 30 | REG_NONE, 31 | REG_SZ, 32 | REG_EXPAND_SZ, 33 | REG_BINARY, 34 | REG_DWORD, 35 | REG_DWORD_BIG_ENDIAN, 36 | REG_LINK, 37 | REG_MULTI_SZ, 38 | REG_RESOURCE_LIST, 39 | REG_FULL_RESOURCE_DESCRIPTOR, 40 | REG_RESOURCE_REQUIREMENTS_LIST, 41 | REG_QWORD 42 | ]); 43 | pub use self::RegType::*; 44 | 45 | winapi_enum!(RegDisposition, "Enumeration of possible disposition values" => [ 46 | REG_CREATED_NEW_KEY, 47 | REG_OPENED_EXISTING_KEY 48 | ]); 49 | pub use self::RegDisposition::*; 50 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Igor Shaula 2 | // Licensed under the MIT License . This file 4 | // may not be copied, modified, or distributed 5 | // except according to those terms. 6 | 7 | //! Crate for accessing MS Windows registry 8 | //! 9 | //!## Usage 10 | //! 11 | //!### Basic usage 12 | //! 13 | //!```toml,ignore 14 | //!# Cargo.toml 15 | //![dependencies] 16 | //!winreg = "0.55" 17 | //!``` 18 | //! 19 | //!```no_run 20 | //!use std::io; 21 | //!use std::path::Path; 22 | //!use winreg::enums::*; 23 | //!use winreg::RegKey; 24 | //! 25 | //!fn main() -> io::Result<()> { 26 | //! println!("Reading some system info..."); 27 | //! let hklm = RegKey::predef(HKEY_LOCAL_MACHINE); 28 | //! let cur_ver = hklm.open_subkey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion")?; 29 | //! let pf: String = cur_ver.get_value("ProgramFilesDir")?; 30 | //! let dp: String = cur_ver.get_value("DevicePath")?; 31 | //! println!("ProgramFiles = {}\nDevicePath = {}", pf, dp); 32 | //! let info = cur_ver.query_info()?; 33 | //! println!("info = {:?}", info); 34 | //! let mt = info.get_last_write_time_system(); 35 | //! println!( 36 | //! "last_write_time as windows_sys::Win32::Foundation::SYSTEMTIME = {}-{:02}-{:02} {:02}:{:02}:{:02}", 37 | //! mt.wYear, mt.wMonth, mt.wDay, mt.wHour, mt.wMinute, mt.wSecond 38 | //! ); 39 | //! 40 | //! // enable `chrono` feature on `winreg` to make this work 41 | //! // println!( 42 | //! // "last_write_time as chrono::NaiveDateTime = {}", 43 | //! // info.get_last_write_time_chrono() 44 | //! // ); 45 | //! 46 | //! println!("And now lets write something..."); 47 | //! let hkcu = RegKey::predef(HKEY_CURRENT_USER); 48 | //! let path = Path::new("Software").join("WinregRsExample1"); 49 | //! let (key, disp) = hkcu.create_subkey(&path)?; 50 | //! 51 | //! match disp { 52 | //! REG_CREATED_NEW_KEY => println!("A new key has been created"), 53 | //! REG_OPENED_EXISTING_KEY => println!("An existing key has been opened"), 54 | //! } 55 | //! 56 | //! key.set_value("TestSZ", &"written by Rust")?; 57 | //! let sz_val: String = key.get_value("TestSZ")?; 58 | //! key.delete_value("TestSZ")?; 59 | //! println!("TestSZ = {}", sz_val); 60 | //! 61 | //! key.set_value("TestMultiSZ", &vec!["written", "by", "Rust"])?; 62 | //! let multi_sz_val: Vec = key.get_value("TestMultiSZ")?; 63 | //! key.delete_value("TestMultiSZ")?; 64 | //! println!("TestMultiSZ = {:?}", multi_sz_val); 65 | //! 66 | //! key.set_value("TestDWORD", &1234567890u32)?; 67 | //! let dword_val: u32 = key.get_value("TestDWORD")?; 68 | //! println!("TestDWORD = {}", dword_val); 69 | //! 70 | //! key.set_value("TestQWORD", &1234567891011121314u64)?; 71 | //! let qword_val: u64 = key.get_value("TestQWORD")?; 72 | //! println!("TestQWORD = {}", qword_val); 73 | //! 74 | //! key.create_subkey("sub\\key")?; 75 | //! hkcu.delete_subkey_all(&path)?; 76 | //! 77 | //! println!("Trying to open nonexistent key..."); 78 | //! hkcu.open_subkey(&path).unwrap_or_else(|e| match e.kind() { 79 | //! io::ErrorKind::NotFound => panic!("Key doesn't exist"), 80 | //! io::ErrorKind::PermissionDenied => panic!("Access denied"), 81 | //! _ => panic!("{:?}", e), 82 | //! }); 83 | //! Ok(()) 84 | //!} 85 | //!``` 86 | //! 87 | //!### Iterators 88 | //! 89 | //!```no_run 90 | //!use std::io; 91 | //!use winreg::RegKey; 92 | //!use winreg::enums::*; 93 | //! 94 | //!fn main() -> io::Result<()> { 95 | //! println!("File extensions, registered in system:"); 96 | //! for i in RegKey::predef(HKEY_CLASSES_ROOT) 97 | //! .enum_keys().map(|x| x.unwrap()) 98 | //! .filter(|x| x.starts_with(".")) 99 | //! { 100 | //! println!("{}", i); 101 | //! } 102 | //! 103 | //! let system = RegKey::predef(HKEY_LOCAL_MACHINE) 104 | //! .open_subkey("HARDWARE\\DESCRIPTION\\System")?; 105 | //! for (name, value) in system.enum_values().map(|x| x.unwrap()) { 106 | //! println!("{} = {:?}", name, value); 107 | //! } 108 | //! 109 | //! Ok(()) 110 | //!} 111 | //!``` 112 | //! 113 | cfg_if::cfg_if! { 114 | if #[cfg(not(windows))] { 115 | compile_error!("OS not supported. if your application is multi-platform, use `[target.'cfg(windows)'.dependencies] winreg = \"...\"`"); 116 | } else { 117 | pub use crate::reg_key::{EnumKeys, EnumValues, RegKey, HKEY}; 118 | pub use crate::reg_key_metadata::RegKeyMetadata; 119 | pub use crate::reg_value::RegValue; 120 | 121 | mod common; 122 | #[cfg(feature = "serialization-serde")] 123 | pub mod decoder; 124 | #[cfg(feature = "serialization-serde")] 125 | pub mod encoder; 126 | pub mod enums; 127 | pub mod reg_key; 128 | pub mod reg_key_metadata; 129 | pub mod reg_value; 130 | #[cfg(feature = "transactions")] 131 | pub mod transaction; 132 | pub mod types; 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/reg_key.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Igor Shaula 2 | // Licensed under the MIT License . This file 4 | // may not be copied, modified, or distributed 5 | // except according to those terms. 6 | use crate::common::*; 7 | use crate::enums::{self, *}; 8 | use crate::reg_key_metadata::RegKeyMetadata; 9 | use crate::reg_value::RegValue; 10 | #[cfg(feature = "transactions")] 11 | use crate::transaction::Transaction; 12 | use crate::types::{FromRegValue, ToRegValue}; 13 | use std::default::Default; 14 | use std::ffi::OsStr; 15 | use std::io; 16 | use std::mem::transmute; 17 | use std::ptr; 18 | use windows_sys::Win32::Foundation; 19 | use windows_sys::Win32::System::Registry; 20 | pub use windows_sys::Win32::System::Registry::HKEY; 21 | 22 | /// Handle of opened registry key 23 | #[derive(Debug)] 24 | pub struct RegKey { 25 | hkey: HKEY, 26 | } 27 | 28 | unsafe impl Send for RegKey {} 29 | 30 | impl RegKey { 31 | /// Open one of predefined keys: 32 | /// 33 | /// * `HKEY_CLASSES_ROOT` 34 | /// * `HKEY_CURRENT_USER` 35 | /// * `HKEY_LOCAL_MACHINE` 36 | /// * `HKEY_USERS` 37 | /// * `HKEY_PERFORMANCE_DATA` 38 | /// * `HKEY_PERFORMANCE_TEXT` 39 | /// * `HKEY_PERFORMANCE_NLSTEXT` 40 | /// * `HKEY_CURRENT_CONFIG` 41 | /// * `HKEY_DYN_DATA` 42 | /// * `HKEY_CURRENT_USER_LOCAL_SETTINGS` 43 | /// 44 | /// # Examples 45 | /// 46 | /// ```no_run 47 | /// # use winreg::RegKey; 48 | /// # use winreg::enums::*; 49 | /// let hklm = RegKey::predef(HKEY_LOCAL_MACHINE); 50 | /// ``` 51 | pub const fn predef(hkey: HKEY) -> RegKey { 52 | RegKey { hkey } 53 | } 54 | 55 | /// Load a registry hive from a file as an application hive. 56 | /// If `lock` is set to `true`, then the hive cannot be loaded again until 57 | /// it's unloaded (i.e. all keys from it go out of scope). 58 | /// 59 | /// # Examples 60 | /// 61 | /// ```no_run 62 | /// # use std::error::Error; 63 | /// # use winreg::RegKey; 64 | /// # use winreg::enums::*; 65 | /// # fn main() -> Result<(), Box> { 66 | /// let handle = RegKey::load_app_key("C:\\myhive.dat", false)?; 67 | /// # Ok(()) 68 | /// # } 69 | /// ``` 70 | pub fn load_app_key>(filename: N, lock: bool) -> io::Result { 71 | let options = if lock { 72 | Registry::REG_PROCESS_APPKEY 73 | } else { 74 | 0 75 | }; 76 | RegKey::load_app_key_with_flags(filename, enums::KEY_ALL_ACCESS, options) 77 | } 78 | 79 | /// Load a registry hive from a file as an application hive with desired 80 | /// permissions and options. If `options` is set to `REG_PROCESS_APPKEY`, 81 | /// then the hive cannot be loaded again until it's unloaded (i.e. all keys 82 | /// from it go out of scope). 83 | /// # Examples 84 | /// 85 | /// ```no_run 86 | /// # use std::error::Error; 87 | /// # use winreg::RegKey; 88 | /// # use winreg::enums::*; 89 | /// # fn main() -> Result<(), Box> { 90 | /// let handle = RegKey::load_app_key_with_flags("C:\\myhive.dat", KEY_READ, 0)?; 91 | /// # Ok(()) 92 | /// # } 93 | /// ``` 94 | pub fn load_app_key_with_flags>( 95 | filename: N, 96 | perms: Registry::REG_SAM_FLAGS, 97 | options: u32, 98 | ) -> io::Result { 99 | let c_filename = to_utf16(filename); 100 | let mut new_hkey: HKEY = std::ptr::null_mut(); 101 | match unsafe { 102 | Registry::RegLoadAppKeyW(c_filename.as_ptr(), &mut new_hkey, perms, options, 0) 103 | } { 104 | 0 => Ok(RegKey { hkey: new_hkey }), 105 | err => werr!(err), 106 | } 107 | } 108 | 109 | /// Return inner winapi HKEY of a key: 110 | /// 111 | /// # Examples 112 | /// 113 | /// ```no_run 114 | /// # use std::error::Error; 115 | /// # use winreg::RegKey; 116 | /// # use winreg::enums::*; 117 | /// # fn main() -> Result<(), Box> { 118 | /// let hklm = RegKey::predef(HKEY_LOCAL_MACHINE); 119 | /// let soft = hklm.open_subkey("SOFTWARE")?; 120 | /// let handle = soft.raw_handle(); 121 | /// # Ok(()) 122 | /// # } 123 | /// ``` 124 | pub const fn raw_handle(&self) -> HKEY { 125 | self.hkey 126 | } 127 | 128 | /// Open subkey with `KEY_READ` permissions. 129 | /// Will open another handle to itself if `path` is an empty string. 130 | /// To open with different permissions use `open_subkey_with_flags`. 131 | /// You can also use `create_subkey` to open with `KEY_ALL_ACCESS` permissions. 132 | /// 133 | /// # Examples 134 | /// 135 | /// ```no_run 136 | /// # use std::error::Error; 137 | /// # use winreg::RegKey; 138 | /// # use winreg::enums::*; 139 | /// # fn main() -> Result<(), Box> { 140 | /// let soft = RegKey::predef(HKEY_CURRENT_USER) 141 | /// .open_subkey("Software")?; 142 | /// # Ok(()) 143 | /// # } 144 | /// ``` 145 | pub fn open_subkey>(&self, path: P) -> io::Result { 146 | self.open_subkey_with_flags(path, enums::KEY_READ) 147 | } 148 | 149 | /// Open subkey with desired permissions. 150 | /// Will open another handle to itself if `path` is an empty string. 151 | /// 152 | /// # Examples 153 | /// 154 | /// ```no_run 155 | /// # use std::error::Error; 156 | /// # use winreg::RegKey; 157 | /// # use winreg::enums::*; 158 | /// # fn main() -> Result<(), Box> { 159 | /// let hklm = RegKey::predef(HKEY_LOCAL_MACHINE); 160 | /// hklm.open_subkey_with_flags("SOFTWARE\\Microsoft", KEY_READ)?; 161 | /// # Ok(()) 162 | /// # } 163 | /// ``` 164 | pub fn open_subkey_with_flags>( 165 | &self, 166 | path: P, 167 | perms: Registry::REG_SAM_FLAGS, 168 | ) -> io::Result { 169 | self.open_subkey_with_options_flags(path, 0, perms) 170 | } 171 | 172 | /// Open subkey with desired permissions and options. 173 | /// Will open another handle to itself if `path` is an empty string. 174 | /// 175 | /// # Examples 176 | /// 177 | /// ```no_run 178 | /// # use std::error::Error; 179 | /// # use winreg::RegKey; 180 | /// # use winreg::enums::*; 181 | /// # fn main() -> Result<(), Box> { 182 | /// let hklm = RegKey::predef(HKEY_LOCAL_MACHINE); 183 | /// hklm.open_subkey_with_options_flags("SOFTWARE\\LinkKey", REG_OPTION_OPEN_LINK, KEY_READ)?; 184 | /// # Ok(()) 185 | /// # } 186 | /// ``` 187 | pub fn open_subkey_with_options_flags>( 188 | &self, 189 | path: P, 190 | options: Registry::REG_OPEN_CREATE_OPTIONS, 191 | perms: Registry::REG_SAM_FLAGS, 192 | ) -> io::Result { 193 | let c_path = to_utf16(path); 194 | let mut new_hkey: HKEY = std::ptr::null_mut(); 195 | match unsafe { 196 | Registry::RegOpenKeyExW(self.hkey, c_path.as_ptr(), options, perms, &mut new_hkey) 197 | } { 198 | 0 => Ok(RegKey { hkey: new_hkey }), 199 | err => werr!(err), 200 | } 201 | } 202 | 203 | /// Part of `transactions` feature. 204 | #[cfg(feature = "transactions")] 205 | pub fn open_subkey_transacted>( 206 | &self, 207 | path: P, 208 | t: &Transaction, 209 | ) -> io::Result { 210 | self.open_subkey_transacted_with_flags(path, t, Registry::KEY_READ) 211 | } 212 | 213 | /// Part of `transactions` feature. 214 | #[cfg(feature = "transactions")] 215 | pub fn open_subkey_transacted_with_flags>( 216 | &self, 217 | path: P, 218 | t: &Transaction, 219 | perms: Registry::REG_SAM_FLAGS, 220 | ) -> io::Result { 221 | self.open_subkey_transacted_with_options_flags(path, t, 0, perms) 222 | } 223 | 224 | /// Part of `transactions` feature. 225 | #[cfg(feature = "transactions")] 226 | pub fn open_subkey_transacted_with_options_flags>( 227 | &self, 228 | path: P, 229 | t: &Transaction, 230 | options: Registry::REG_OPEN_CREATE_OPTIONS, 231 | perms: Registry::REG_SAM_FLAGS, 232 | ) -> io::Result { 233 | let c_path = to_utf16(path); 234 | let mut new_hkey: HKEY = std::ptr::null_mut(); 235 | match unsafe { 236 | Registry::RegOpenKeyTransactedW( 237 | self.hkey, 238 | c_path.as_ptr(), 239 | options, 240 | perms, 241 | &mut new_hkey, 242 | t.handle, 243 | ptr::null_mut(), 244 | ) 245 | } { 246 | 0 => Ok(RegKey { hkey: new_hkey }), 247 | err => werr!(err), 248 | } 249 | } 250 | 251 | /// Create subkey (and all missing parent keys) 252 | /// and open it with `KEY_ALL_ACCESS` permissions. 253 | /// Will just open key if it already exists. 254 | /// If succeeds returns a tuple with the created subkey and its disposition, 255 | /// which can be `REG_CREATED_NEW_KEY` or `REG_OPENED_EXISTING_KEY`. 256 | /// Will open another handle to itself if `path` is an empty string. 257 | /// To create with different permissions use `create_subkey_with_flags`. 258 | /// 259 | /// # Examples 260 | /// 261 | /// ```no_run 262 | /// # use std::error::Error; 263 | /// # use winreg::RegKey; 264 | /// use winreg::enums::*; 265 | /// # fn main() -> Result<(), Box> { 266 | /// let hkcu = RegKey::predef(HKEY_CURRENT_USER); 267 | /// let (settings, disp) = hkcu.create_subkey("Software\\MyProduct\\Settings")?; 268 | /// 269 | /// match disp { 270 | /// REG_CREATED_NEW_KEY => println!("A new key has been created"), 271 | /// REG_OPENED_EXISTING_KEY => println!("An existing key has been opened") 272 | /// } 273 | /// # Ok(()) 274 | /// # } 275 | /// ``` 276 | pub fn create_subkey>(&self, path: P) -> io::Result<(RegKey, RegDisposition)> { 277 | self.create_subkey_with_flags(path, enums::KEY_ALL_ACCESS) 278 | } 279 | 280 | pub fn create_subkey_with_flags>( 281 | &self, 282 | path: P, 283 | perms: Registry::REG_SAM_FLAGS, 284 | ) -> io::Result<(RegKey, RegDisposition)> { 285 | self.create_subkey_with_options_flags(path, REG_OPTION_NON_VOLATILE, perms) 286 | } 287 | 288 | pub fn create_subkey_with_options_flags>( 289 | &self, 290 | path: P, 291 | options: Registry::REG_OPEN_CREATE_OPTIONS, 292 | perms: Registry::REG_SAM_FLAGS, 293 | ) -> io::Result<(RegKey, RegDisposition)> { 294 | let c_path = to_utf16(path); 295 | let mut new_hkey: HKEY = std::ptr::null_mut(); 296 | let mut disp_buf: u32 = 0; 297 | match unsafe { 298 | Registry::RegCreateKeyExW( 299 | self.hkey, 300 | c_path.as_ptr(), 301 | 0, 302 | ptr::null_mut(), 303 | options, 304 | perms, 305 | ptr::null_mut(), 306 | &mut new_hkey, 307 | &mut disp_buf, 308 | ) 309 | } { 310 | 0 => { 311 | let disp: RegDisposition = unsafe { transmute(disp_buf as u8) }; 312 | Ok((RegKey { hkey: new_hkey }, disp)) 313 | } 314 | err => werr!(err), 315 | } 316 | } 317 | 318 | /// Part of `transactions` feature. 319 | #[cfg(feature = "transactions")] 320 | pub fn create_subkey_transacted>( 321 | &self, 322 | path: P, 323 | t: &Transaction, 324 | ) -> io::Result<(RegKey, RegDisposition)> { 325 | self.create_subkey_transacted_with_flags(path, t, Registry::KEY_ALL_ACCESS) 326 | } 327 | 328 | /// Part of `transactions` feature. 329 | #[cfg(feature = "transactions")] 330 | pub fn create_subkey_transacted_with_flags>( 331 | &self, 332 | path: P, 333 | t: &Transaction, 334 | perms: Registry::REG_SAM_FLAGS, 335 | ) -> io::Result<(RegKey, RegDisposition)> { 336 | self.create_subkey_transacted_with_options_flags(path, t, REG_OPTION_NON_VOLATILE, perms) 337 | } 338 | 339 | /// Part of `transactions` feature. 340 | #[cfg(feature = "transactions")] 341 | pub fn create_subkey_transacted_with_options_flags>( 342 | &self, 343 | path: P, 344 | t: &Transaction, 345 | options: Registry::REG_OPEN_CREATE_OPTIONS, 346 | perms: Registry::REG_SAM_FLAGS, 347 | ) -> io::Result<(RegKey, RegDisposition)> { 348 | let c_path = to_utf16(path); 349 | let mut new_hkey: HKEY = std::ptr::null_mut(); 350 | let mut disp_buf: u32 = 0; 351 | match unsafe { 352 | Registry::RegCreateKeyTransactedW( 353 | self.hkey, 354 | c_path.as_ptr(), 355 | 0, 356 | ptr::null_mut(), 357 | options, 358 | perms, 359 | ptr::null_mut(), 360 | &mut new_hkey, 361 | &mut disp_buf, 362 | t.handle, 363 | ptr::null_mut(), 364 | ) 365 | } { 366 | 0 => { 367 | let disp: RegDisposition = unsafe { transmute(disp_buf as u8) }; 368 | Ok((RegKey { hkey: new_hkey }, disp)) 369 | } 370 | err => werr!(err), 371 | } 372 | } 373 | 374 | /// Rename a subkey 375 | /// 376 | /// # Examples 377 | /// ```no_run 378 | /// # use std::error::Error; 379 | /// # use winreg::RegKey; 380 | /// # use winreg::enums::*; 381 | /// # fn main() -> Result<(), Box> { 382 | /// let items = RegKey::predef(HKEY_CURRENT_USER).open_subkey(r"Software\MyProduct\Items")?; 383 | /// items.rename_subkey("itemA", "itemB")?; 384 | /// # Ok(()) 385 | /// # } 386 | /// ``` 387 | pub fn rename_subkey, NN: AsRef>( 388 | &self, 389 | old_name: ON, 390 | new_name: NN, 391 | ) -> io::Result<()> { 392 | let c_old_name = to_utf16(old_name); 393 | let c_new_name = to_utf16(new_name); 394 | match unsafe { Registry::RegRenameKey(self.hkey, c_old_name.as_ptr(), c_new_name.as_ptr()) } 395 | { 396 | 0 => Ok(()), 397 | err => werr!(err), 398 | } 399 | } 400 | 401 | /// Copy all the values and subkeys from `path` to `dest` key. 402 | /// Will copy the content of `self` if `path` is an empty string. 403 | /// 404 | /// # Examples 405 | /// 406 | /// ```no_run 407 | /// # use std::error::Error; 408 | /// # use winreg::RegKey; 409 | /// # use winreg::enums::*; 410 | /// # fn main() -> Result<(), Box> { 411 | /// let hkcu = RegKey::predef(HKEY_CURRENT_USER); 412 | /// let src = hkcu.open_subkey_with_flags("Software\\MyProduct", KEY_READ)?; 413 | /// let (dst, dst_disp) = hkcu.create_subkey("Software\\MyProduct\\Section2")?; 414 | /// src.copy_tree("Section1", &dst)?; 415 | /// # Ok(()) 416 | /// # } 417 | /// ``` 418 | pub fn copy_tree>(&self, path: P, dest: &RegKey) -> io::Result<()> { 419 | let c_path = to_utf16(path); 420 | match unsafe { Registry::RegCopyTreeW(self.hkey, c_path.as_ptr(), dest.hkey) } { 421 | 0 => Ok(()), 422 | err => werr!(err), 423 | } 424 | } 425 | 426 | pub fn query_info(&self) -> io::Result { 427 | let mut info: RegKeyMetadata = RegKeyMetadata::default(); 428 | match unsafe { 429 | Registry::RegQueryInfoKeyW( 430 | self.hkey, 431 | ptr::null_mut(), // Class: winapi::LPWSTR, 432 | ptr::null_mut(), // ClassLen: u32, 433 | ptr::null_mut(), // Reserved 434 | &mut info.sub_keys, 435 | &mut info.max_sub_key_len, 436 | &mut info.max_class_len, 437 | &mut info.values, 438 | &mut info.max_value_name_len, 439 | &mut info.max_value_len, 440 | ptr::null_mut(), // lpcbSecurityDescriptor: winapi::LPDWORD, 441 | &mut info.last_write_time.0, 442 | ) 443 | } { 444 | 0 => Ok(info), 445 | err => werr!(err), 446 | } 447 | } 448 | 449 | /// Return an iterator over subkeys names. 450 | /// 451 | /// # Examples 452 | /// 453 | /// ```no_run 454 | /// # use winreg::RegKey; 455 | /// # use winreg::enums::*; 456 | /// println!("File extensions, registered in this system:"); 457 | /// for i in RegKey::predef(HKEY_CLASSES_ROOT) 458 | /// .enum_keys().map(|x| x.unwrap()) 459 | /// .filter(|x| x.starts_with(".")) 460 | /// { 461 | /// println!("{}", i); 462 | /// } 463 | /// ``` 464 | pub const fn enum_keys(&self) -> EnumKeys { 465 | EnumKeys { 466 | key: self, 467 | index: 0, 468 | } 469 | } 470 | 471 | /// Return an iterator over values. 472 | /// 473 | /// # Examples 474 | /// 475 | /// ```no_run 476 | /// # use std::error::Error; 477 | /// # use winreg::RegKey; 478 | /// # use winreg::enums::*; 479 | /// # fn main() -> Result<(), Box> { 480 | /// let system = RegKey::predef(HKEY_LOCAL_MACHINE) 481 | /// .open_subkey_with_flags("HARDWARE\\DESCRIPTION\\System", KEY_READ)?; 482 | /// for (name, value) in system.enum_values().map(|x| x.unwrap()) { 483 | /// println!("{} = {:?}", name, value); 484 | /// } 485 | /// # Ok(()) 486 | /// # } 487 | /// ``` 488 | pub const fn enum_values(&self) -> EnumValues { 489 | EnumValues { 490 | key: self, 491 | index: 0, 492 | } 493 | } 494 | 495 | /// Delete key. Key names are not case sensitive. 496 | /// Cannot delete if it has subkeys. 497 | /// Use `delete_subkey_all` for that. 498 | /// 499 | /// # Examples 500 | /// 501 | /// ```no_run 502 | /// # use std::error::Error; 503 | /// # use winreg::RegKey; 504 | /// # use winreg::enums::*; 505 | /// # fn main() -> Result<(), Box> { 506 | /// RegKey::predef(HKEY_CURRENT_USER) 507 | /// .delete_subkey(r"Software\MyProduct\History")?; 508 | /// # Ok(()) 509 | /// # } 510 | /// ``` 511 | pub fn delete_subkey>(&self, path: P) -> io::Result<()> { 512 | self.delete_subkey_with_flags(path, 0) 513 | } 514 | 515 | /// Delete key from the desired platform-specific view of the registry. 516 | /// Key names are not case sensitive. 517 | /// 518 | /// # Examples 519 | /// ```no_run 520 | /// # use std::error::Error; 521 | /// # use winreg::RegKey; 522 | /// # use winreg::enums::*; 523 | /// # fn main() -> Result<(), Box> { 524 | /// // delete the key from the 32-bit registry view 525 | /// RegKey::predef(HKEY_LOCAL_MACHINE) 526 | /// .delete_subkey_with_flags(r"Software\MyProduct\History", KEY_WOW64_32KEY)?; 527 | /// # Ok(()) 528 | /// # } 529 | /// ``` 530 | pub fn delete_subkey_with_flags>( 531 | &self, 532 | path: P, 533 | perms: Registry::REG_SAM_FLAGS, 534 | ) -> io::Result<()> { 535 | let c_path = to_utf16(path); 536 | match unsafe { 537 | Registry::RegDeleteKeyExW( 538 | self.hkey, 539 | c_path.as_ptr(), // This parameter cannot be NULL. 540 | perms, 541 | 0, 542 | ) 543 | } { 544 | 0 => Ok(()), 545 | err => werr!(err), 546 | } 547 | } 548 | 549 | /// Part of `transactions` feature. 550 | #[cfg(feature = "transactions")] 551 | pub fn delete_subkey_transacted>( 552 | &self, 553 | path: P, 554 | t: &Transaction, 555 | ) -> io::Result<()> { 556 | self.delete_subkey_transacted_with_flags(path, t, 0) 557 | } 558 | 559 | /// Part of `transactions` feature. 560 | #[cfg(feature = "transactions")] 561 | pub fn delete_subkey_transacted_with_flags>( 562 | &self, 563 | path: P, 564 | t: &Transaction, 565 | perms: Registry::REG_SAM_FLAGS, 566 | ) -> io::Result<()> { 567 | let c_path = to_utf16(path); 568 | match unsafe { 569 | Registry::RegDeleteKeyTransactedW( 570 | self.hkey, 571 | c_path.as_ptr(), // This parameter cannot be NULL. 572 | perms, 573 | 0, 574 | t.handle, 575 | ptr::null_mut(), 576 | ) 577 | } { 578 | 0 => Ok(()), 579 | err => werr!(err), 580 | } 581 | } 582 | 583 | /// Recursively delete subkey with all its subkeys and values. 584 | /// If `path` is an empty string, the subkeys and values of this key are deleted. 585 | /// 586 | /// # Examples 587 | /// 588 | /// ```no_run 589 | /// # use std::error::Error; 590 | /// # use winreg::RegKey; 591 | /// # use winreg::enums::*; 592 | /// # fn main() -> Result<(), Box> { 593 | /// RegKey::predef(HKEY_CURRENT_USER) 594 | /// .delete_subkey_all("Software\\MyProduct")?; 595 | /// # Ok(()) 596 | /// # } 597 | /// ``` 598 | pub fn delete_subkey_all>(&self, path: P) -> io::Result<()> { 599 | let c_path; 600 | let path_ptr = if path.as_ref().is_empty() { 601 | ptr::null() 602 | } else { 603 | c_path = to_utf16(path); 604 | c_path.as_ptr() 605 | }; 606 | match unsafe { 607 | Registry::RegDeleteTreeW( 608 | self.hkey, 609 | path_ptr, //If this parameter is NULL, the subkeys and values of this key are deleted. 610 | ) 611 | } { 612 | 0 => Ok(()), 613 | err => werr!(err), 614 | } 615 | } 616 | 617 | /// Get a value from registry and seamlessly convert it to the specified rust type 618 | /// with `FromRegValue` implemented (currently `String`, `u32` and `u64`). 619 | /// Will get the `Default` value if `name` is an empty string. 620 | /// 621 | /// # Examples 622 | /// 623 | /// ```no_run 624 | /// # use std::error::Error; 625 | /// # use winreg::RegKey; 626 | /// # use winreg::enums::*; 627 | /// # fn main() -> Result<(), Box> { 628 | /// let hkcu = RegKey::predef(HKEY_CURRENT_USER); 629 | /// let settings = hkcu.open_subkey("Software\\MyProduct\\Settings")?; 630 | /// let server: String = settings.get_value("server")?; 631 | /// let port: u32 = settings.get_value("port")?; 632 | /// # Ok(()) 633 | /// # } 634 | /// ``` 635 | pub fn get_value>(&self, name: N) -> io::Result { 636 | match self.get_raw_value(name) { 637 | Ok(ref val) => FromRegValue::from_reg_value(val), 638 | Err(err) => Err(err), 639 | } 640 | } 641 | 642 | /// Get raw bytes from registry value. 643 | /// Will get the `Default` value if `name` is an empty string. 644 | /// 645 | /// # Examples 646 | /// 647 | /// ```no_run 648 | /// # use std::error::Error; 649 | /// # use winreg::RegKey; 650 | /// # use winreg::enums::*; 651 | /// # fn main() -> Result<(), Box> { 652 | /// let hkcu = RegKey::predef(HKEY_CURRENT_USER); 653 | /// let settings = hkcu.open_subkey("Software\\MyProduct\\Settings")?; 654 | /// let data = settings.get_raw_value("data")?; 655 | /// println!("Bytes: {:?}", data.bytes); 656 | /// # Ok(()) 657 | /// # } 658 | /// ``` 659 | pub fn get_raw_value>(&self, name: N) -> io::Result { 660 | let c_name = to_utf16(name); 661 | let mut buf_len: u32 = 2048; 662 | let mut buf_type: u32 = 0; 663 | let mut buf: Vec = Vec::with_capacity(buf_len as usize); 664 | loop { 665 | match unsafe { 666 | Registry::RegQueryValueExW( 667 | self.hkey, 668 | c_name.as_ptr(), 669 | ptr::null_mut(), 670 | &mut buf_type, 671 | buf.as_mut_ptr(), 672 | &mut buf_len, 673 | ) 674 | } { 675 | 0 => { 676 | unsafe { 677 | buf.set_len(buf_len as usize); 678 | } 679 | // minimal check before transmute to RegType 680 | if buf_type > Registry::REG_QWORD { 681 | return werr!(Foundation::ERROR_BAD_FILE_TYPE); 682 | } 683 | let t: RegType = unsafe { transmute(buf_type as u8) }; 684 | return Ok(RegValue { 685 | bytes: buf, 686 | vtype: t, 687 | }); 688 | } 689 | Foundation::ERROR_MORE_DATA => { 690 | buf.reserve(buf_len as usize); 691 | } 692 | err => return werr!(err), 693 | } 694 | } 695 | } 696 | 697 | /// Seamlessly convert a value from a rust type and write it to the registry value 698 | /// with `ToRegValue` trait implemented (currently `String`, `&str`, `u32` and `u64`). 699 | /// Will set the `Default` value if `name` is an empty string. 700 | /// 701 | /// # Examples 702 | /// 703 | /// ```no_run 704 | /// # use std::error::Error; 705 | /// # use winreg::RegKey; 706 | /// # use winreg::enums::*; 707 | /// # fn main() -> Result<(), Box> { 708 | /// let hkcu = RegKey::predef(HKEY_CURRENT_USER); 709 | /// let (settings, disp) = hkcu.create_subkey("Software\\MyProduct\\Settings")?; 710 | /// settings.set_value("server", &"www.example.com")?; 711 | /// settings.set_value("port", &8080u32)?; 712 | /// # Ok(()) 713 | /// # } 714 | /// ``` 715 | pub fn set_value>(&self, name: N, value: &T) -> io::Result<()> { 716 | self.set_raw_value(name, &value.to_reg_value()) 717 | } 718 | 719 | /// Write raw bytes from `RegValue` struct to a registry value. 720 | /// Will set the `Default` value if `name` is an empty string. 721 | /// 722 | /// # Examples 723 | /// 724 | /// ```no_run 725 | /// # use std::error::Error; 726 | /// use winreg::{RegKey, RegValue}; 727 | /// use winreg::enums::*; 728 | /// # fn main() -> Result<(), Box> { 729 | /// let hkcu = RegKey::predef(HKEY_CURRENT_USER); 730 | /// let settings = hkcu.open_subkey("Software\\MyProduct\\Settings")?; 731 | /// let bytes: Vec = vec![1, 2, 3, 5, 8, 13, 21, 34, 55, 89]; 732 | /// let data = RegValue{ vtype: REG_BINARY, bytes: bytes}; 733 | /// settings.set_raw_value("data", &data)?; 734 | /// println!("Bytes: {:?}", data.bytes); 735 | /// # Ok(()) 736 | /// # } 737 | /// ``` 738 | pub fn set_raw_value>(&self, name: N, value: &RegValue) -> io::Result<()> { 739 | let c_name = to_utf16(name); 740 | let t = value.vtype.clone() as u32; 741 | match unsafe { 742 | Registry::RegSetValueExW( 743 | self.hkey, 744 | c_name.as_ptr(), 745 | 0, 746 | t, 747 | value.bytes.as_ptr(), 748 | value.bytes.len() as u32, 749 | ) 750 | } { 751 | 0 => Ok(()), 752 | err => werr!(err), 753 | } 754 | } 755 | 756 | /// Delete specified value from registry. 757 | /// Will delete the `Default` value if `name` is an empty string. 758 | /// 759 | /// # Examples 760 | /// 761 | /// ```no_run 762 | /// # use std::error::Error; 763 | /// # use winreg::RegKey; 764 | /// # use winreg::enums::*; 765 | /// # fn main() -> Result<(), Box> { 766 | /// let hkcu = RegKey::predef(HKEY_CURRENT_USER); 767 | /// let settings = hkcu.open_subkey("Software\\MyProduct\\Settings")?; 768 | /// settings.delete_value("data")?; 769 | /// # Ok(()) 770 | /// # } 771 | /// ``` 772 | pub fn delete_value>(&self, name: N) -> io::Result<()> { 773 | let c_name = to_utf16(name); 774 | match unsafe { Registry::RegDeleteValueW(self.hkey, c_name.as_ptr()) } { 775 | 0 => Ok(()), 776 | err => werr!(err), 777 | } 778 | } 779 | 780 | /// Save `Encodable` type to a registry key. 781 | /// This will create a new transaction for this operation. 782 | /// Part of `serialization-serde` feature. 783 | /// 784 | /// # Examples 785 | /// 786 | /// ```no_run 787 | /// # use std::error::Error; 788 | /// use serde_derive::Serialize; 789 | /// use winreg::RegKey; 790 | /// use winreg::enums::*; 791 | /// 792 | /// #[derive(Serialize)] 793 | /// struct Rectangle{ 794 | /// x: u32, 795 | /// y: u32, 796 | /// w: u32, 797 | /// h: u32, 798 | /// } 799 | /// 800 | /// #[derive(Serialize)] 801 | /// struct Settings{ 802 | /// current_dir: String, 803 | /// window_pos: Rectangle, 804 | /// show_in_tray: bool, 805 | /// } 806 | /// 807 | /// # fn main() -> Result<(), Box> { 808 | /// let s: Settings = Settings{ 809 | /// current_dir: "C:\\".to_owned(), 810 | /// window_pos: Rectangle{ x:200, y: 100, w: 800, h: 500 }, 811 | /// show_in_tray: false, 812 | /// }; 813 | /// let s_key = RegKey::predef(HKEY_CURRENT_USER) 814 | /// .open_subkey("Software\\MyProduct\\Settings")?; 815 | /// s_key.encode(&s)?; 816 | /// # Ok(()) 817 | /// # } 818 | /// ``` 819 | #[cfg(feature = "serialization-serde")] 820 | pub fn encode(&self, value: &T) -> crate::encoder::EncodeResult<()> { 821 | let mut encoder = crate::encoder::Encoder::from_key(self)?; 822 | value.serialize(&mut encoder)?; 823 | encoder.commit() 824 | } 825 | 826 | /// Save `Encodable` type to a registry key using an existing transaction. 827 | /// Part of `serialization-serde` feature. 828 | /// 829 | /// # Examples 830 | /// 831 | /// ```no_run 832 | /// # use std::error::Error; 833 | /// use serde_derive::Serialize; 834 | /// use winreg::transaction::Transaction; 835 | /// use winreg::RegKey; 836 | /// use winreg::enums::*; 837 | /// 838 | /// #[derive(Serialize)] 839 | /// struct Rectangle{ 840 | /// x: u32, 841 | /// y: u32, 842 | /// w: u32, 843 | /// h: u32, 844 | /// } 845 | /// 846 | /// #[derive(Serialize)] 847 | /// struct Settings{ 848 | /// current_dir: String, 849 | /// window_pos: Rectangle, 850 | /// show_in_tray: bool, 851 | /// } 852 | /// 853 | /// # fn main() -> Result<(), Box> { 854 | /// let s: Settings = Settings{ 855 | /// current_dir: "C:\\".to_owned(), 856 | /// window_pos: Rectangle{ x:200, y: 100, w: 800, h: 500 }, 857 | /// show_in_tray: false, 858 | /// }; 859 | /// 860 | /// let transaction = Transaction::new()?; 861 | /// 862 | /// let s_key = RegKey::predef(HKEY_CURRENT_USER) 863 | /// .open_subkey_transacted("Software\\MyProduct\\Settings", &transaction)?; 864 | /// s_key.encode_transacted(&s, &transaction)?; 865 | /// 866 | /// transaction.commit()?; 867 | /// # Ok(()) 868 | /// # } 869 | /// ``` 870 | #[cfg(feature = "serialization-serde")] 871 | pub fn encode_transacted( 872 | &self, 873 | value: &T, 874 | tr: &Transaction, 875 | ) -> crate::encoder::EncodeResult<()> { 876 | let mut encoder = crate::encoder::Encoder::from_key_transacted(self, tr)?; 877 | value.serialize(&mut encoder) 878 | } 879 | 880 | /// Load `Decodable` type from a registry key. 881 | /// Part of `serialization-serde` feature. 882 | /// 883 | /// # Examples 884 | /// 885 | /// ```no_run 886 | /// # use std::error::Error; 887 | /// use serde_derive::Deserialize; 888 | /// use winreg::RegKey; 889 | /// use winreg::enums::*; 890 | /// 891 | /// #[derive(Deserialize)] 892 | /// struct Rectangle{ 893 | /// x: u32, 894 | /// y: u32, 895 | /// w: u32, 896 | /// h: u32, 897 | /// } 898 | /// 899 | /// #[derive(Deserialize)] 900 | /// struct Settings{ 901 | /// current_dir: String, 902 | /// window_pos: Rectangle, 903 | /// show_in_tray: bool, 904 | /// } 905 | /// 906 | /// # fn main() -> Result<(), Box> { 907 | /// let s_key = RegKey::predef(HKEY_CURRENT_USER) 908 | /// .open_subkey("Software\\MyProduct\\Settings")?; 909 | /// let s: Settings = s_key.decode()?; 910 | /// # Ok(()) 911 | /// # } 912 | /// ``` 913 | #[cfg(feature = "serialization-serde")] 914 | pub fn decode<'de, T: serde::Deserialize<'de>>(&self) -> crate::decoder::DecodeResult { 915 | let mut decoder = crate::decoder::Decoder::from_key(self)?; 916 | T::deserialize(&mut decoder) 917 | } 918 | 919 | fn close_(&mut self) -> io::Result<()> { 920 | // don't try to close predefined keys 921 | // The root hkey overflows with windows-sys, where HKEY is an alias for isize. 922 | // Cast to u32 to keep comparisons intact. 923 | if self.hkey as usize >= enums::HKEY_CLASSES_ROOT as usize { 924 | return Ok(()); 925 | }; 926 | match unsafe { Registry::RegCloseKey(self.hkey) } { 927 | 0 => Ok(()), 928 | err => werr!(err), 929 | } 930 | } 931 | 932 | pub(crate) fn enum_key(&self, index: u32) -> Option> { 933 | let mut name_len = 2048; 934 | #[allow(clippy::unnecessary_cast)] 935 | let mut name = [0 as u16; 2048]; 936 | match unsafe { 937 | Registry::RegEnumKeyExW( 938 | self.hkey, 939 | index, 940 | name.as_mut_ptr(), 941 | &mut name_len, 942 | ptr::null_mut(), // reserved 943 | ptr::null_mut(), // lpClass: LPWSTR, 944 | ptr::null_mut(), // lpcClass: LPDWORD, 945 | ptr::null_mut(), // lpftLastWriteTime: PFILETIME, 946 | ) 947 | } { 948 | 0 => match String::from_utf16(&name[..name_len as usize]) { 949 | Ok(s) => Some(Ok(s)), 950 | Err(_) => Some(werr!(Foundation::ERROR_INVALID_BLOCK)), 951 | }, 952 | Foundation::ERROR_NO_MORE_ITEMS => None, 953 | err => Some(werr!(err)), 954 | } 955 | } 956 | 957 | pub(crate) fn enum_value(&self, index: u32) -> Option> { 958 | let mut name_len = 2048; 959 | #[allow(clippy::unnecessary_cast)] 960 | let mut name = [0 as u16; 2048]; 961 | 962 | let mut buf_len: u32 = 2048; 963 | let mut buf_type: u32 = 0; 964 | let mut buf: Vec = Vec::with_capacity(buf_len as usize); 965 | loop { 966 | match unsafe { 967 | Registry::RegEnumValueW( 968 | self.hkey, 969 | index, 970 | name.as_mut_ptr(), 971 | &mut name_len, 972 | ptr::null_mut(), // reserved 973 | &mut buf_type, 974 | buf.as_mut_ptr(), 975 | &mut buf_len, 976 | ) 977 | } { 978 | 0 => { 979 | let name = match String::from_utf16(&name[..name_len as usize]) { 980 | Ok(s) => s, 981 | Err(_) => return Some(werr!(Foundation::ERROR_INVALID_DATA)), 982 | }; 983 | unsafe { 984 | buf.set_len(buf_len as usize); 985 | } 986 | // minimal check before transmute to RegType 987 | if buf_type > Registry::REG_QWORD { 988 | return Some(werr!(Foundation::ERROR_BAD_FILE_TYPE)); 989 | } 990 | let t: RegType = unsafe { transmute(buf_type as u8) }; 991 | let value = RegValue { 992 | bytes: buf, 993 | vtype: t, 994 | }; 995 | return Some(Ok((name, value))); 996 | } 997 | Foundation::ERROR_MORE_DATA => { 998 | name_len += 1; //for NULL char 999 | buf.reserve(buf_len as usize); 1000 | } 1001 | Foundation::ERROR_NO_MORE_ITEMS => return None, 1002 | err => return Some(werr!(err)), 1003 | } 1004 | } 1005 | } 1006 | } 1007 | 1008 | impl Drop for RegKey { 1009 | fn drop(&mut self) { 1010 | self.close_().unwrap_or(()); 1011 | } 1012 | } 1013 | 1014 | /// Iterator over subkeys names 1015 | pub struct EnumKeys<'key> { 1016 | key: &'key RegKey, 1017 | index: u32, 1018 | } 1019 | 1020 | impl Iterator for EnumKeys<'_> { 1021 | type Item = io::Result; 1022 | 1023 | fn next(&mut self) -> Option> { 1024 | match self.key.enum_key(self.index) { 1025 | v @ Some(_) => { 1026 | self.index += 1; 1027 | v 1028 | } 1029 | e @ None => e, 1030 | } 1031 | } 1032 | 1033 | fn nth(&mut self, n: usize) -> Option { 1034 | self.index += n as u32; 1035 | self.next() 1036 | } 1037 | } 1038 | 1039 | /// Iterator over values 1040 | pub struct EnumValues<'key> { 1041 | key: &'key RegKey, 1042 | index: u32, 1043 | } 1044 | 1045 | impl Iterator for EnumValues<'_> { 1046 | type Item = io::Result<(String, RegValue)>; 1047 | 1048 | fn next(&mut self) -> Option> { 1049 | match self.key.enum_value(self.index) { 1050 | v @ Some(_) => { 1051 | self.index += 1; 1052 | v 1053 | } 1054 | e @ None => e, 1055 | } 1056 | } 1057 | 1058 | fn nth(&mut self, n: usize) -> Option { 1059 | self.index += n as u32; 1060 | self.next() 1061 | } 1062 | } 1063 | -------------------------------------------------------------------------------- /src/reg_key_metadata.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Igor Shaula 2 | // Licensed under the MIT License . This file 4 | // may not be copied, modified, or distributed 5 | // except according to those terms. 6 | use std::fmt; 7 | use std::ops::Deref; 8 | use windows_sys::Win32::Foundation::FILETIME; 9 | use windows_sys::Win32::Foundation::SYSTEMTIME; 10 | use windows_sys::Win32::System::Time::FileTimeToSystemTime; 11 | 12 | pub struct FileTime(pub(crate) FILETIME); 13 | 14 | impl Default for FileTime { 15 | fn default() -> Self { 16 | Self(FILETIME { 17 | dwLowDateTime: 0, 18 | dwHighDateTime: 0, 19 | }) 20 | } 21 | } 22 | 23 | impl fmt::Debug for FileTime { 24 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 25 | f.debug_struct("FILETIME") 26 | .field("dwLowDateTime", &self.dwLowDateTime) 27 | .field("dwHighDateTime", &self.dwHighDateTime) 28 | .finish() 29 | } 30 | } 31 | 32 | impl Deref for FileTime { 33 | type Target = FILETIME; 34 | 35 | fn deref(&self) -> &Self::Target { 36 | &self.0 37 | } 38 | } 39 | 40 | /// Metadata returned by `RegKey::query_info` 41 | #[derive(Debug, Default)] 42 | pub struct RegKeyMetadata { 43 | // pub Class: winapi::LPWSTR, 44 | // pub ClassLen: u32, 45 | pub sub_keys: u32, 46 | pub max_sub_key_len: u32, 47 | pub max_class_len: u32, 48 | pub values: u32, 49 | pub max_value_name_len: u32, 50 | pub max_value_len: u32, 51 | // pub SecurityDescriptor: u32, 52 | pub last_write_time: FileTime, 53 | } 54 | 55 | impl RegKeyMetadata { 56 | /// Returns `last_write_time` field as `windows_sys::Win32::Foundation::SYSTEMTIME` 57 | pub fn get_last_write_time_system(&self) -> SYSTEMTIME { 58 | let mut st: SYSTEMTIME = unsafe { ::std::mem::zeroed() }; 59 | unsafe { 60 | FileTimeToSystemTime(&self.last_write_time.0, &mut st); 61 | } 62 | st 63 | } 64 | 65 | /// Returns `last_write_time` field as `chrono::NaiveDateTime`. 66 | /// Part of `chrono` feature. 67 | #[cfg(feature = "chrono")] 68 | pub fn get_last_write_time_chrono(&self) -> chrono::NaiveDateTime { 69 | let st = self.get_last_write_time_system(); 70 | 71 | chrono::NaiveDate::from_ymd_opt(st.wYear.into(), st.wMonth.into(), st.wDay.into()) 72 | .expect("out-of-range date, invalid month and/or day") 73 | .and_hms_opt(st.wHour.into(), st.wMinute.into(), st.wSecond.into()) 74 | .expect("invalid hour, minute and/or second") 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/reg_value.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Igor Shaula 2 | // Licensed under the MIT License . This file 4 | // may not be copied, modified, or distributed 5 | // except according to those terms. 6 | use crate::enums::*; 7 | use crate::types::FromRegValue; 8 | use std::fmt; 9 | 10 | /// Raw registry value 11 | #[derive(PartialEq)] 12 | pub struct RegValue { 13 | pub bytes: Vec, 14 | pub vtype: RegType, 15 | } 16 | 17 | macro_rules! format_reg_value { 18 | ($e:expr => $t:ident) => { 19 | match $t::from_reg_value($e) { 20 | Ok(val) => format!("{}", val), 21 | Err(_) => return Err(fmt::Error), 22 | } 23 | }; 24 | } 25 | 26 | impl fmt::Display for RegValue { 27 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 28 | let f_val = match self.vtype { 29 | REG_SZ | REG_EXPAND_SZ | REG_MULTI_SZ => format_reg_value!(self => String), 30 | REG_DWORD => format_reg_value!(self => u32), 31 | REG_QWORD => format_reg_value!(self => u64), 32 | _ => format!("{:?}", self.bytes), //TODO: implement more types 33 | }; 34 | write!(f, "{}", f_val) 35 | } 36 | } 37 | 38 | impl fmt::Debug for RegValue { 39 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 40 | write!(f, "RegValue({:?}: {})", self.vtype, self) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/transaction.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Igor Shaula 2 | // Licensed under the MIT License . This file 4 | // may not be copied, modified, or distributed 5 | // except according to those terms. 6 | #![allow(clippy::needless_doctest_main)] 7 | 8 | //! Structure for a registry transaction. 9 | //! Part of `transactions` feature. 10 | //! 11 | //!```no_run 12 | //!use std::io; 13 | //!use winreg::enums::*; 14 | //!use winreg::transaction::Transaction; 15 | //!use winreg::RegKey; 16 | //! 17 | //!fn main() -> io::Result<()> { 18 | //! let t = Transaction::new()?; 19 | //! let hkcu = RegKey::predef(HKEY_CURRENT_USER); 20 | //! let (key, _disp) = hkcu.create_subkey_transacted("Software\\RustTransaction", &t)?; 21 | //! key.set_value("TestQWORD", &1_234_567_891_011_121_314u64)?; 22 | //! key.set_value("TestDWORD", &1_234_567_890u32)?; 23 | //! 24 | //! println!("Commit transaction? [y/N]:"); 25 | //! let mut input = String::new(); 26 | //! io::stdin().read_line(&mut input)?; 27 | //! input = input.trim_end().to_owned(); 28 | //! if input == "y" || input == "Y" { 29 | //! t.commit()?; 30 | //! println!("Transaction committed."); 31 | //! } else { 32 | //! // this is optional, if transaction wasn't committed, 33 | //! // it will be rolled back on disposal 34 | //! t.rollback()?; 35 | //! 36 | //! println!("Transaction wasn't committed, it will be rolled back."); 37 | //! } 38 | //! 39 | //! Ok(()) 40 | //!} 41 | //!``` 42 | use std::io; 43 | use std::ptr; 44 | use windows_sys::Win32::Foundation; 45 | use windows_sys::Win32::Storage::FileSystem; 46 | 47 | #[derive(Debug)] 48 | pub struct Transaction { 49 | pub handle: Foundation::HANDLE, 50 | } 51 | 52 | impl Transaction { 53 | //TODO: add arguments 54 | pub fn new() -> io::Result { 55 | unsafe { 56 | let handle = FileSystem::CreateTransaction( 57 | ptr::null_mut(), 58 | ptr::null_mut(), 59 | 0, 60 | 0, 61 | 0, 62 | 0, 63 | ptr::null_mut(), 64 | ); 65 | if handle == Foundation::INVALID_HANDLE_VALUE { 66 | return Err(io::Error::last_os_error()); 67 | }; 68 | Ok(Transaction { handle }) 69 | } 70 | } 71 | 72 | pub fn commit(self) -> io::Result<()> { 73 | unsafe { 74 | match FileSystem::CommitTransaction(self.handle) { 75 | 0 => Err(io::Error::last_os_error()), 76 | _ => Ok(()), 77 | } 78 | } 79 | } 80 | 81 | pub fn rollback(self) -> io::Result<()> { 82 | unsafe { 83 | match FileSystem::RollbackTransaction(self.handle) { 84 | 0 => Err(io::Error::last_os_error()), 85 | _ => Ok(()), 86 | } 87 | } 88 | } 89 | 90 | fn close_(&mut self) -> io::Result<()> { 91 | unsafe { 92 | match Foundation::CloseHandle(self.handle) { 93 | 0 => Err(io::Error::last_os_error()), 94 | _ => Ok(()), 95 | } 96 | } 97 | } 98 | } 99 | 100 | impl Drop for Transaction { 101 | fn drop(&mut self) { 102 | self.close_().unwrap_or(()); 103 | } 104 | } 105 | 106 | impl AsRef for Transaction { 107 | fn as_ref(&self) -> &Transaction { 108 | self 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/types.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Igor Shaula 2 | // Licensed under the MIT License . This file 4 | // may not be copied, modified, or distributed 5 | // except according to those terms. 6 | 7 | //! Traits for loading/saving Registry values 8 | use crate::common::*; 9 | use crate::enums::*; 10 | use crate::RegValue; 11 | use std::convert::TryInto; 12 | use std::ffi::{OsStr, OsString}; 13 | use std::io; 14 | use std::os::windows::ffi::OsStringExt; 15 | use std::slice; 16 | use windows_sys::Win32::Foundation; 17 | 18 | /// A trait for types that can be loaded from registry values. 19 | /// 20 | /// **NOTE:** Uses `from_utf16_lossy` when converting to `String`. 21 | /// 22 | /// **NOTE:** When converting to `String` or `OsString`, trailing `NULL` characters are trimmed 23 | /// and line separating `NULL` characters in `REG_MULTI_SZ` are replaced by `\n` 24 | /// effectively representing the value as a multiline string. 25 | /// When converting to `Vec` or `Vec` `NULL` is used as a strings separator. 26 | pub trait FromRegValue: Sized { 27 | fn from_reg_value(val: &RegValue) -> io::Result; 28 | } 29 | 30 | impl FromRegValue for String { 31 | fn from_reg_value(val: &RegValue) -> io::Result { 32 | match val.vtype { 33 | REG_SZ | REG_EXPAND_SZ | REG_MULTI_SZ => { 34 | let words = unsafe { 35 | #[allow(clippy::cast_ptr_alignment)] 36 | slice::from_raw_parts(val.bytes.as_ptr() as *const u16, val.bytes.len() / 2) 37 | }; 38 | let mut s = String::from_utf16_lossy(words); 39 | while s.ends_with('\u{0}') { 40 | s.pop(); 41 | } 42 | if val.vtype == REG_MULTI_SZ { 43 | return Ok(s.replace('\u{0}', "\n")); 44 | } 45 | Ok(s) 46 | } 47 | _ => werr!(Foundation::ERROR_BAD_FILE_TYPE), 48 | } 49 | } 50 | } 51 | 52 | impl FromRegValue for Vec { 53 | fn from_reg_value(val: &RegValue) -> io::Result> { 54 | match val.vtype { 55 | REG_MULTI_SZ => { 56 | let words = unsafe { 57 | slice::from_raw_parts(val.bytes.as_ptr() as *const u16, val.bytes.len() / 2) 58 | }; 59 | let mut s = String::from_utf16_lossy(words); 60 | while s.ends_with('\u{0}') { 61 | s.pop(); 62 | } 63 | let v: Vec = s.split('\u{0}').map(|x| x.to_owned()).collect(); 64 | Ok(v) 65 | } 66 | _ => werr!(Foundation::ERROR_BAD_FILE_TYPE), 67 | } 68 | } 69 | } 70 | 71 | impl FromRegValue for OsString { 72 | fn from_reg_value(val: &RegValue) -> io::Result { 73 | match val.vtype { 74 | REG_SZ | REG_EXPAND_SZ | REG_MULTI_SZ => { 75 | let mut words = unsafe { 76 | #[allow(clippy::cast_ptr_alignment)] 77 | slice::from_raw_parts(val.bytes.as_ptr() as *const u16, val.bytes.len() / 2) 78 | }; 79 | while let Some(0) = words.last() { 80 | words = &words[0..words.len() - 1]; 81 | } 82 | let s = OsString::from_wide(words); 83 | Ok(s) 84 | } 85 | _ => werr!(Foundation::ERROR_BAD_FILE_TYPE), 86 | } 87 | } 88 | } 89 | 90 | impl FromRegValue for Vec { 91 | fn from_reg_value(val: &RegValue) -> io::Result> { 92 | match val.vtype { 93 | REG_MULTI_SZ => { 94 | let mut words = unsafe { 95 | slice::from_raw_parts(val.bytes.as_ptr() as *const u16, val.bytes.len() / 2) 96 | }; 97 | while let Some(0) = words.last() { 98 | words = &words[0..words.len() - 1]; 99 | } 100 | let v: Vec = words 101 | .split(|ch| *ch == 0u16) 102 | .map(OsString::from_wide) 103 | .collect(); 104 | Ok(v) 105 | } 106 | _ => werr!(Foundation::ERROR_BAD_FILE_TYPE), 107 | } 108 | } 109 | } 110 | 111 | macro_rules! try_from_reg_value_int { 112 | ($val:expr, $map:expr) => { 113 | $val.bytes 114 | .as_slice() 115 | .try_into() 116 | .map($map) 117 | .map_err(|_| io::Error::from_raw_os_error(Foundation::ERROR_INVALID_DATA as i32)) 118 | }; 119 | } 120 | 121 | impl FromRegValue for u32 { 122 | fn from_reg_value(val: &RegValue) -> io::Result { 123 | match val.vtype { 124 | REG_DWORD => try_from_reg_value_int!(val, u32::from_ne_bytes), 125 | REG_DWORD_BIG_ENDIAN => try_from_reg_value_int!(val, u32::from_be_bytes), 126 | _ => werr!(Foundation::ERROR_BAD_FILE_TYPE), 127 | } 128 | } 129 | } 130 | 131 | impl FromRegValue for u64 { 132 | fn from_reg_value(val: &RegValue) -> io::Result { 133 | match val.vtype { 134 | REG_QWORD => try_from_reg_value_int!(val, u64::from_ne_bytes), 135 | _ => werr!(Foundation::ERROR_BAD_FILE_TYPE), 136 | } 137 | } 138 | } 139 | 140 | /// A trait for types that can be written into registry values. 141 | /// 142 | /// **NOTE:** Adds trailing `NULL` character to `str`, `String`, `OsStr` and `OsString` values 143 | pub trait ToRegValue { 144 | fn to_reg_value(&self) -> RegValue; 145 | } 146 | 147 | macro_rules! to_reg_value_sz { 148 | ($t:ty$(, $l:lifetime)*) => { 149 | impl<$($l,)*> ToRegValue for $t { 150 | fn to_reg_value(&self) -> RegValue { 151 | RegValue { 152 | bytes: v16_to_v8(&to_utf16(self)), 153 | vtype: REG_SZ, 154 | } 155 | } 156 | } 157 | } 158 | } 159 | 160 | to_reg_value_sz!(String); 161 | to_reg_value_sz!(&'a str, 'a); 162 | to_reg_value_sz!(OsString); 163 | to_reg_value_sz!(&'a OsStr, 'a); 164 | 165 | macro_rules! to_reg_value_multi_sz { 166 | ($t:ty$(, $l:lifetime)*) => { 167 | impl<$($l,)*> ToRegValue for Vec<$t> { 168 | fn to_reg_value(&self) -> RegValue { 169 | let mut os_strings = self 170 | .into_iter() 171 | .map(to_utf16) 172 | .collect::>() 173 | .concat(); 174 | os_strings.push(0); 175 | RegValue { 176 | bytes: v16_to_v8(&os_strings), 177 | vtype: REG_MULTI_SZ, 178 | } 179 | } 180 | } 181 | } 182 | } 183 | 184 | to_reg_value_multi_sz!(String); 185 | to_reg_value_multi_sz!(&'a str, 'a); 186 | to_reg_value_multi_sz!(OsString); 187 | to_reg_value_multi_sz!(&'a OsStr, 'a); 188 | 189 | impl ToRegValue for u32 { 190 | fn to_reg_value(&self) -> RegValue { 191 | let bytes: Vec = 192 | unsafe { slice::from_raw_parts((self as *const u32) as *const u8, 4).to_vec() }; 193 | RegValue { 194 | bytes, 195 | vtype: REG_DWORD, 196 | } 197 | } 198 | } 199 | 200 | impl ToRegValue for u64 { 201 | fn to_reg_value(&self) -> RegValue { 202 | let bytes: Vec = 203 | unsafe { slice::from_raw_parts((self as *const u64) as *const u8, 8).to_vec() }; 204 | RegValue { 205 | bytes, 206 | vtype: REG_QWORD, 207 | } 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /tests/common/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Igor Shaula 2 | // Licensed under the MIT License . This file 4 | // may not be copied, modified, or distributed 5 | // except according to those terms. 6 | #![macro_use] 7 | 8 | macro_rules! with_key { 9 | ($k:ident, $path:expr => $b:block) => {{ 10 | let mut path = "Software\\WinRegRsTest".to_owned(); 11 | path.push_str($path); 12 | let ($k, _disp) = winreg::RegKey::predef(winreg::enums::HKEY_CURRENT_USER) 13 | .create_subkey(&path).unwrap(); 14 | $b 15 | winreg::RegKey::predef(winreg::enums::HKEY_CURRENT_USER) 16 | .delete_subkey_all(path).unwrap(); 17 | }} 18 | } 19 | -------------------------------------------------------------------------------- /tests/reg_key.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Igor Shaula 2 | // Licensed under the MIT License . This file 4 | // may not be copied, modified, or distributed 5 | // except according to those terms. 6 | use rand::Rng; 7 | use std::collections::HashMap; 8 | use std::ffi::{OsStr, OsString}; 9 | use tempfile::tempdir; 10 | use windows_sys::Win32::Foundation; 11 | use winreg::enums::*; 12 | use winreg::types::FromRegValue; 13 | use winreg::{RegKey, RegValue}; 14 | 15 | mod common; 16 | 17 | #[test] 18 | fn test_raw_handle() { 19 | let hklm = RegKey::predef(HKEY_LOCAL_MACHINE); 20 | let handle = hklm.raw_handle(); 21 | assert_eq!(HKEY_LOCAL_MACHINE, handle); 22 | } 23 | 24 | #[test] 25 | fn test_load_appkey() { 26 | let val_name = "LoadKeyTest"; 27 | let dir = tempdir().unwrap(); 28 | let file_path = dir.path().join("RustLoadAppkeyTest.dat"); 29 | let val1 = "Test123".to_owned(); 30 | { 31 | let key1 = RegKey::load_app_key(&file_path, true).unwrap(); 32 | key1.set_value(val_name, &val1).unwrap(); 33 | // this fails on Windows 7 with ERROR_ALREADY_EXISTS 34 | let key_err = RegKey::load_app_key_with_flags(&file_path, KEY_READ, 0).unwrap_err(); 35 | assert_eq!( 36 | key_err.raw_os_error(), 37 | Some(Foundation::ERROR_SHARING_VIOLATION as i32) 38 | ); 39 | } 40 | let val2: String = { 41 | // this fails on Windows 7 with ERROR_ALREADY_EXISTS 42 | let key2 = RegKey::load_app_key_with_flags(&file_path, KEY_READ, 1).unwrap(); 43 | key2.get_value(val_name).unwrap() 44 | }; 45 | assert_eq!(val1, val2); 46 | } 47 | 48 | #[test] 49 | fn test_open_subkey_with_flags_query_info() { 50 | let hklm = RegKey::predef(HKEY_LOCAL_MACHINE); 51 | let win = hklm 52 | .open_subkey_with_flags("Software\\Microsoft\\Windows", KEY_READ) 53 | .unwrap(); 54 | 55 | let info = win.query_info().unwrap(); 56 | info.get_last_write_time_system(); 57 | #[cfg(feature = "chrono")] 58 | info.get_last_write_time_chrono(); 59 | 60 | assert!(win 61 | .open_subkey_with_flags("CurrentVersion\\", KEY_READ) 62 | .is_ok()); 63 | assert!(hklm 64 | .open_subkey_with_flags("i\\just\\hope\\nobody\\created\\that\\key", KEY_READ) 65 | .is_err()); 66 | } 67 | 68 | #[test] 69 | fn test_create_subkey_disposition() { 70 | let hkcu = RegKey::predef(HKEY_CURRENT_USER); 71 | let path = "Software\\WinRegRsTestCreateSubkey"; 72 | let (_subkey, disp) = hkcu.create_subkey(path).unwrap(); 73 | assert_eq!(disp, REG_CREATED_NEW_KEY); 74 | let (_subkey2, disp2) = hkcu.create_subkey(path).unwrap(); 75 | assert_eq!(disp2, REG_OPENED_EXISTING_KEY); 76 | hkcu.delete_subkey_all(path).unwrap(); 77 | } 78 | 79 | #[test] 80 | fn test_delete_subkey() { 81 | let path = "Software\\WinRegRsTestDeleteSubkey"; 82 | RegKey::predef(HKEY_CURRENT_USER) 83 | .create_subkey(path) 84 | .unwrap(); 85 | assert!(RegKey::predef(HKEY_CURRENT_USER) 86 | .delete_subkey(path) 87 | .is_ok()); 88 | } 89 | 90 | #[test] 91 | fn test_delete_subkey_with_flags() { 92 | let path = "Software\\Classes\\WinRegRsTestDeleteSubkeyWithFlags"; 93 | RegKey::predef(HKEY_CURRENT_USER) 94 | .create_subkey_with_flags(path, KEY_WOW64_32KEY) 95 | .unwrap(); 96 | assert!(RegKey::predef(HKEY_CURRENT_USER) 97 | .delete_subkey_with_flags(path, KEY_WOW64_32KEY) 98 | .is_ok()); 99 | } 100 | 101 | #[test] 102 | fn test_rename_subkey() { 103 | with_key!(key, "RenameSubkey" => { 104 | let old_name = "SubkeyA"; 105 | let new_name = "SubkeyB"; 106 | key.create_subkey(old_name).unwrap(); 107 | assert!(key.rename_subkey(old_name, new_name).is_ok()); 108 | assert!(key.open_subkey(new_name).is_ok()); 109 | }); 110 | } 111 | 112 | #[test] 113 | fn test_copy_tree() { 114 | with_key!(key, "CopyTree" => { 115 | let (sub_tree, _sub_tree_disp) = key.create_subkey("Src\\Sub\\Tree").unwrap(); 116 | for v in &["one", "two", "three"] { 117 | sub_tree.set_value(v, v).unwrap(); 118 | } 119 | let (dst, _dst_disp) = key.create_subkey("Dst").unwrap(); 120 | assert!(key.copy_tree("Src", &dst).is_ok()); 121 | }); 122 | } 123 | 124 | #[test] 125 | fn test_long_value() { 126 | with_key!(key, "LongValue" => { 127 | let name = "RustLongVal"; 128 | let val1 = RegValue { vtype: REG_BINARY, bytes: (0..6000).map(|_| rand::random::()).collect() }; 129 | key.set_raw_value(name, &val1).unwrap(); 130 | let val2 = key.get_raw_value(name).unwrap(); 131 | assert_eq!(val1, val2); 132 | }); 133 | } 134 | 135 | macro_rules! test_value_sz { 136 | ($fname:ident, $kname:expr, $conv:expr => $tout:ty) => { 137 | #[test] 138 | fn $fname() { 139 | with_key!(key, $kname => { 140 | let name = "RustSzVal"; 141 | let val1 = $conv("Test123 \n$%^&|+-*/\\()"); 142 | key.set_value(name, &val1).unwrap(); 143 | let val2: $tout = key.get_value(name).unwrap(); 144 | assert_eq!(val1, val2); 145 | }); 146 | } 147 | } 148 | } 149 | 150 | test_value_sz!(test_string_value, "StringValue", str::to_owned => String); 151 | test_value_sz!(test_str_value, "StrValue", |x|x => String); 152 | test_value_sz!(test_os_string_value, "OsStringValue", OsString::from => OsString); 153 | test_value_sz!(test_os_str_value, "OsStrValue", OsStr::new => OsString); 154 | 155 | #[test] 156 | fn test_long_string_value() { 157 | with_key!(key, "LongStringValue" => { 158 | let name = "RustLongStringVal"; 159 | let val1 : String = rand::thread_rng().gen_ascii_chars().take(7000).collect(); 160 | key.set_value(name, &val1).unwrap(); 161 | let val2: String = key.get_value(name).unwrap(); 162 | assert_eq!(val1, val2); 163 | }); 164 | } 165 | 166 | #[test] 167 | fn test_long_os_string_value() { 168 | with_key!(key, "LongOsStringValue" => { 169 | let name = "RustLongOsStringVal"; 170 | let val1 = rand::thread_rng().gen_ascii_chars().take(7000).collect::(); 171 | let val1 = OsStr::new(&val1); 172 | key.set_value(name, &val1).unwrap(); 173 | let val2: OsString = key.get_value(name).unwrap(); 174 | assert_eq!(val1, val2); 175 | }); 176 | } 177 | 178 | macro_rules! test_value_multi_sz { 179 | ($fname:ident, $kname:expr, $conv:expr => $tout:ty) => { 180 | #[test] 181 | fn $fname() { 182 | with_key!(key, $kname => { 183 | let name = "RustMultiSzVal"; 184 | 185 | let val1 = vec![ 186 | $conv("lorem ipsum\ndolor"), 187 | $conv("sit amet") 188 | ]; 189 | key.set_value(name, &val1).unwrap(); 190 | let val2: Vec<$tout> = key.get_value(name).unwrap(); 191 | 192 | assert_eq!(val1, val2); 193 | }); 194 | } 195 | } 196 | } 197 | 198 | test_value_multi_sz!(test_vec_string_value, "StringVectorValue", str::to_owned => String); 199 | test_value_multi_sz!(test_vec_str_value, "StrVectorValue", |x|x => String); 200 | test_value_multi_sz!(test_vec_os_string_value, "OsStringVectorValue", OsString::from => OsString); 201 | test_value_multi_sz!(test_vec_os_str_value, "OsStrVectorValue", OsStr::new => OsString); 202 | 203 | #[test] 204 | fn test_u32_value() { 205 | with_key!(key, "U32Value" => { 206 | let name = "RustU32Val"; 207 | let val1 = 1_234_567_890u32; 208 | key.set_value(name, &val1).unwrap(); 209 | let val2: u32 = key.get_value(name).unwrap(); 210 | assert_eq!(val1, val2); 211 | }); 212 | } 213 | 214 | #[test] 215 | fn test_u64_value() { 216 | with_key!(key, "U64Value" => { 217 | let name = "RustU64Val"; 218 | let val1 = 1_234_567_891_011_121_314u64; 219 | key.set_value(name, &val1).unwrap(); 220 | let val2: u64 = key.get_value(name).unwrap(); 221 | assert_eq!(val1, val2); 222 | }); 223 | } 224 | 225 | #[test] 226 | fn test_delete_value() { 227 | with_key!(key, "DeleteValue" => { 228 | let name = "WinregRsTestVal"; 229 | key.set_value(name, &"Qwerty123").unwrap(); 230 | assert!(key.delete_value(name).is_ok()); 231 | }); 232 | } 233 | 234 | #[test] 235 | fn test_enum_keys() { 236 | with_key!(key, "EnumKeys" => { 237 | let mut keys1 = vec!("qwerty", "asdf", "1", "2", "3", "5", "8", "йцукен"); 238 | keys1.sort_unstable(); 239 | for i in &keys1 { 240 | key.create_subkey(i).unwrap(); 241 | } 242 | let keys2: Vec<_> = key.enum_keys().map(|x| x.unwrap()).collect(); 243 | assert_eq!(keys1, keys2); 244 | }); 245 | } 246 | 247 | #[test] 248 | fn test_enum_values() { 249 | with_key!(key, "EnumValues" => { 250 | let mut vals1 = vec!("qwerty", "asdf", "1", "2", "3", "5", "8", "йцукен"); 251 | vals1.sort_unstable(); 252 | for i in &vals1 { 253 | key.set_value(i,i).unwrap(); 254 | } 255 | let mut vals2: Vec = Vec::with_capacity(vals1.len()); 256 | let mut vals3: Vec = Vec::with_capacity(vals1.len()); 257 | for (name, val) in key.enum_values() 258 | .map(|x| x.unwrap()) 259 | { 260 | vals2.push(name); 261 | vals3.push(String::from_reg_value(&val).unwrap()); 262 | } 263 | assert_eq!(vals1, vals2); 264 | assert_eq!(vals1, vals3); 265 | }); 266 | } 267 | 268 | #[test] 269 | fn test_enum_long_values() { 270 | with_key!(key, "EnumLongValues" => { 271 | let mut vals = HashMap::with_capacity(3); 272 | 273 | for i in &[5500, 9500, 15000] { 274 | let name: String = format!("val{}", i); 275 | let val = RegValue { vtype: REG_BINARY, bytes: (0..*i).map(|_| rand::random::()).collect() }; 276 | vals.insert(name, val); 277 | } 278 | 279 | for (name, val) in key.enum_values() 280 | .map(|x| x.unwrap()) 281 | { 282 | assert_eq!(val.bytes, vals[&name].bytes); 283 | } 284 | }); 285 | } 286 | -------------------------------------------------------------------------------- /tests/reg_value.rs: -------------------------------------------------------------------------------- 1 | use winreg::{types::ToRegValue, RegValue}; 2 | 3 | macro_rules! test_display { 4 | ($f:ident, $v:expr) => { 5 | #[test] 6 | fn $f() { 7 | let val = $v; 8 | let rval: RegValue = val.to_reg_value(); 9 | assert_eq!(val.to_string(), rval.to_string()); 10 | } 11 | }; 12 | } 13 | 14 | test_display!(test_display_string, "Test\\123"); 15 | test_display!(test_display_u32, 1234u32); 16 | test_display!(test_display_u64, 1234567890u64); 17 | -------------------------------------------------------------------------------- /tests/serialization.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023, Igor Shaula 2 | // Licensed under the MIT License . This file 4 | // may not be copied, modified, or distributed 5 | // except according to those terms. 6 | #![cfg(feature = "serialization-serde")] 7 | use serde_derive::{Deserialize, Serialize}; 8 | use std::collections::HashMap; 9 | 10 | mod common; 11 | 12 | #[derive(Debug, Serialize, Deserialize, PartialEq)] 13 | struct Coords { 14 | x: u32, 15 | y: u32, 16 | } 17 | 18 | #[derive(Debug, Serialize, Deserialize, PartialEq)] 19 | struct Size { 20 | w: u32, 21 | h: u32, 22 | } 23 | 24 | #[derive(Debug, Serialize, Deserialize, PartialEq)] 25 | struct Rectangle { 26 | coords: Option, 27 | size: Size, 28 | } 29 | 30 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 31 | struct AllFields { 32 | t_bool: bool, 33 | t_u8: u8, 34 | t_u16: u16, 35 | t_u32: u32, 36 | t_u64: u64, 37 | t_usize: usize, 38 | t_rect_no_coords: Rectangle, 39 | t_rect_with_coords: Rectangle, 40 | t_string: String, 41 | t_map: HashMap>, 42 | t_optional_u32_none: Option, 43 | t_optional_u32_some: Option, 44 | t_optional_map_none: Option>, 45 | t_optional_map_some: Option>, 46 | t_i8: i8, 47 | t_i16: i16, 48 | t_i32: i32, 49 | t_i64: i64, 50 | t_isize: isize, 51 | t_f64: f64, 52 | t_f32: f32, 53 | t_char: char, 54 | #[serde(with = "serde_bytes")] 55 | t_bytes: Vec, 56 | } 57 | 58 | impl AllFields { 59 | pub fn test_val() -> Self { 60 | let mut k1 = HashMap::new(); 61 | k1.insert("val1".to_owned(), 32); 62 | k1.insert("val2".to_owned(), 64); 63 | k1.insert("val3".to_owned(), 128); 64 | 65 | let mut k2 = HashMap::new(); 66 | k2.insert("val1".to_owned(), 256); 67 | k2.insert("val2".to_owned(), 512); 68 | k2.insert("val3".to_owned(), 1024); 69 | 70 | let mut map = HashMap::new(); 71 | map.insert("key1".to_owned(), k1.clone()); 72 | map.insert("key2".to_owned(), k2); 73 | 74 | AllFields { 75 | t_bool: false, 76 | t_u8: 127, 77 | t_u16: 32768, 78 | t_u32: 123_456_789, 79 | t_u64: 123_456_789_101_112, 80 | t_usize: 1_234_567_891, 81 | t_rect_no_coords: Rectangle { 82 | coords: None, 83 | size: Size { w: 320, h: 240 }, 84 | }, 85 | t_rect_with_coords: Rectangle { 86 | coords: Some(Coords { x: 55, y: 77 }), 87 | size: Size { w: 500, h: 300 }, 88 | }, 89 | t_map: map, 90 | t_optional_u32_none: None, 91 | t_optional_u32_some: Some(1234), 92 | t_optional_map_none: None, 93 | t_optional_map_some: Some(k1), 94 | t_string: "Test123 \n$%^&|+-*/\\()".to_owned(), 95 | t_i8: -123, 96 | t_i16: -2049, 97 | t_i32: 20100, 98 | t_i64: -12_345_678_910, 99 | t_isize: -1_234_567_890, 100 | t_f64: -0.01, 101 | t_f32: 3.15, 102 | t_char: 'a', 103 | t_bytes: vec![0xDE, 0xAD, 0xBE, 0xEF], 104 | } 105 | } 106 | } 107 | 108 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 109 | struct SomeFields { 110 | t_usize: usize, 111 | t_rect_no_coords: Rectangle, 112 | t_string: String, 113 | t_u32: Option, 114 | t_none: Option, 115 | } 116 | 117 | impl PartialEq for SomeFields { 118 | fn eq(&self, other: &AllFields) -> bool { 119 | *self.t_string == other.t_string 120 | && self.t_usize == other.t_usize 121 | && self.t_rect_no_coords == other.t_rect_no_coords 122 | && self.t_u32 == Some(other.t_u32) 123 | && self.t_none.is_none() 124 | } 125 | } 126 | 127 | #[test] 128 | fn test_serialization_some() { 129 | let v1 = AllFields::test_val(); 130 | 131 | with_key!(key, "SerializationSome" => { 132 | key.encode(&v1).unwrap(); 133 | let v2: SomeFields = key.decode().unwrap(); 134 | assert_eq!(v2, v1); 135 | }); 136 | } 137 | 138 | #[test] 139 | fn test_serialization_all() { 140 | let v1 = AllFields::test_val(); 141 | 142 | with_key!(key, "SerializationAll" => { 143 | key.encode(&v1).unwrap(); 144 | let v2: AllFields = key.decode().unwrap(); 145 | assert_eq!(v2, v1); 146 | }); 147 | } 148 | 149 | #[test] 150 | fn test_serialization_some_transacted() { 151 | let v1 = AllFields::test_val(); 152 | 153 | with_key!(key, "SerializationSomeTransacted" => { 154 | let transaction = winreg::transaction::Transaction::new().unwrap(); 155 | key.encode_transacted(&v1, &transaction).unwrap(); 156 | transaction.commit().unwrap(); 157 | let v2: SomeFields = key.decode().unwrap(); 158 | assert_eq!(v2, v1); 159 | }); 160 | } 161 | 162 | #[test] 163 | fn test_serialization_all_transacted() { 164 | let v1 = AllFields::test_val(); 165 | 166 | with_key!(key, "SerializationAllTransacted" => { 167 | let transaction = winreg::transaction::Transaction::new().unwrap(); 168 | key.encode_transacted(&v1, &transaction).unwrap(); 169 | transaction.commit().unwrap(); 170 | let v2: AllFields = key.decode().unwrap(); 171 | assert_eq!(v2, v1); 172 | }); 173 | } 174 | --------------------------------------------------------------------------------