In this article, we’ll delve into how to interact with the Windows Registry using the WinAPI crate in Rust. The Windows Registry is a database that stores configuration settings and options for the operating system, hardware, and applications. We’ll cover the basics of working with the Windows Registry, such as creating, reading, updating, and deleting keys and values.
Before starting, make sure you have the following installed:
First, create a new Rust project by running the following command in your terminal:
cargo new registry_interactions
Change your working directory to the newly created project:
cd registry_interactions
Now, let’s add the required dependencies to our Cargo.toml
file:
[dependencies]
winapi = "0.3"
Before diving into the Windows Registry functions, let’s create a helper module to simplify the process of dealing with the WinAPI. Create a new file named winapi_helpers.rs
in your src
directory:
use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;
use winapi::um::winnt::WCHAR;
use winapi::shared::minwindef::DWORD;
pub fn to_wstring(value: &str) -> Vec<WCHAR> {
let v: Vec<_> = OsString::from(value).encode_wide().chain(Some(0)).collect();
v
}
pub fn from_wstring(value: &[WCHAR]) -> String {
let len = value.iter().position(|&x| x == 0).unwrap_or(value.len());
OsString::from_wide(&value[..len]).to_string_lossy().into_owned()
}
pub fn get_last_error() -> DWORD {
unsafe { winapi::um::errhandlingapi::GetLastError() }
}
This module provides three helper functions:
to_wstring
: Converts a Rust &str
to a null-terminated wide character string (Vec<WCHAR>
).from_wstring
: Converts a wide character slice (&[WCHAR]
) to a Rust String
.get_last_error
: Gets the last error code returned by a WinAPI function.Now that we have our helper functions, let’s create a new module named registry.rs
in your src
directory:
use winapi::um::winreg::{
RegCloseKey, RegCreateKeyExW, RegDeleteKeyW, RegDeleteValueW, RegOpenKeyExW, RegQueryValueExW,
RegSetValueExW, HKEY_CURRENT_USER, KEY_ALL_ACCESS, KEY_READ, KEY_WRITE, REG_OPTION_NON_VOLATILE,
REG_SZ,
};
use winapi::shared::minwindef::{HKEY, LPCVOID, LPDWORD, LPVOID};
use winapi::shared::ntdef::LPWSTR;
use crate::winapi_helpers::{to_wstring, from_wstring, get_last_error};
// ...your code here...
To open a key, use the RegOpenKeyExW
function:
pub fn open_key(path: &str, writable: bool) -> Result<HKEY, u32> {
let path_w = to_wstring(path);
let mut key: HKEY = std::ptr::null_mut();
let rights = if writable { KEY_WRITE } else { KEY_READ };
let result = unsafe { RegOpenKeyExW(HKEY_CURRENT_USER, path_w.as_ptr(), 0, rights, &mut key) };
if result == 0 {
Ok(key)
} else {
Err(get_last_error())
}
}
To create a key, use the RegCreateKeyExW
function:
pub fn create_key(path: &str) -> Result<HKEY, u32> {
let path_w = to_wstring(path);
let mut key: HKEY = std::ptr::null_mut();
let mut disposition: DWORD = 0;
let result = unsafe {
RegCreateKeyExW(
HKEY_CURRENT_USER,
path_w.as_ptr(),
0,
std::ptr::null_mut(),
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
std::ptr::null_mut(),
&mut key,
&mut disposition,
)
};
if result == 0 {
Ok(key)
} else {
Err(get_last_error())
}
}
To read a value, use the RegQueryValueExW
function:
pub fn read_value(key: HKEY, name: &str) -> Result<String, u32> {
let name_w = to_wstring(name);
let mut value_type: DWORD = 0;
let mut buf_size: DWORD = 0;
let result = unsafe {
RegQueryValueExW(
key,
name_w.as_ptr(),
std::ptr::null_mut(),
&mut value_type,
std::ptr::null_mut(),
&mut buf_size,
)
};
if result != 0 {
return Err(get_last_error());
}
let mut buf = vec![0u16; (buf_size / 2) as usize];
let result = unsafe {
RegQueryValueExW(
key,
name_w.as_ptr(),
std::ptr::null_mut(),
&mut value_type,
buf.as_mut_ptr() as LPVOID,
&mut buf_size,
)
};
if result == 0 {
Ok(from_wstring(&buf))
} else {
Err(get_last_error())
}
}
To write a value, use the RegSetValueExW
function:
pub fn write_value(key: HKEY, name: &str, value: &str) -> Result<(), u32> {
let name_w = to_wstring(name);
let value_w = to_wstring(value);
let result = unsafe {
RegSetValueExW(
key,
name_w.as_ptr(),
0,
REG_SZ,
value_w.as_ptr() as LPCVOID,
(value_w.len() * 2) as u32,
)
};
if result == 0 {
Ok(())
} else {
Err(get_last_error())
}
}
To delete a key, use the RegDeleteKeyW
function:
pub fn delete_key(path: &str) -> Result<(), u32> {
let path_w = to_wstring(path);
let result = unsafe { RegDeleteKeyW(HKEY_CURRENT_USER, path_w.as_ptr()) };
if result == 0 {
Ok(())
} else {
Err(get_last_error())
}
}
To delete a value, use the RegDeleteValueW
function:
pub fn delete_value(key: HKEY, name: &str) -> Result<(), u32> {
let name_w = to_wstring(name);
let result = unsafe { RegDeleteValueW(key, name_w.as_ptr()) };
if result == 0 {
Ok(())
} else {
Err(get_last_error())
}
}
To close a key, use the RegCloseKey
function:
pub fn close_key(key: HKEY) -> Result<(), u32> {
let result = unsafe { RegCloseKey(key) };
if result == 0 {
Ok(())
} else {
Err(get_last_error())
}
}
Now that we have implemented the registry functions, let’s put them to use in our main.rs
:
mod winapi_helpers;
mod registry;
use registry::{create_key, open_key, read_value, write_value, delete_key, delete_value, close_key};
fn main() {
let key_path = "Software\\MyApp";
// Create a key
match create_key(key_path) {
Ok(key) => println!("Key created successfully."),
Err(error) => eprintln!("Error creating key: {}", error),
}
// Open the key
let key = match open_key(key_path, true) {
Ok(key) => key,
Err(error) => {
eprintln!("Error opening key: {}", error);
return;
}
};
// Write a value
match write_value(key, "TestValue", "Hello, World!") {
Ok(_) => println!("Value written successfully."),
Err(error) => eprintln!("Error writing value: {}", error),
}
// Read the value
match read_value(key, "TestValue") {
Ok(value) => println!("Value read: {}", value),
Err(error) => eprintln!("Error reading value: {}", error),
}
// Delete the value
match delete_value(key, "TestValue") {
Ok(_) => println!("Value deleted successfully."),
Err(error) => eprintln!("Error deleting value: {}", error),
}
// Close the key
match close_key(key) {
Ok(_) => println!("Key closed successfully."),
Err(error) => eprintln!("Error closing key: {}", error),
}
// Delete the key
match delete_key(key_path) {
Ok(_) => println!("Key deleted successfully."),
Err(error) => eprintln!("Error deleting key: {}", error),
}
}