Reputation: 393
I'm creating a windows app that can change system color.
I saw this code, where we are accessing and reading from registry with RegGetValueW()
. Then I copied and changed it a little with the help of compiler:
use windows::{Win32::System::Registry::RegGetValueW, core::PWSTR};
use winreg::enums::*;
pub fn is_light_theme() -> bool {
// based on https://stackoverflow.com/a/51336913/709884
let mut buffer: [u8; 4] = [0; 4];
let mut cb_data: u32 = (buffer.len()).try_into().unwrap();
let res = unsafe {
RegGetValueW(
HKEY_CURRENT_USER,
w!(r#"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize"#),
w!("AppsUseLightTheme"),
RRF_RT_REG_DWORD,
Some(std::ptr::null_mut()),
Some(buffer.as_mut_ptr() as _),
Some(&mut cb_data as *mut u32),
)
};
assert_eq!(
res,
ERROR_SUCCESS,
format!("failed to read key from registry: err_code= {}", res).as_str(),
);
// REG_DWORD is signed 32-bit, using little endian
let light_mode = i32::from_le_bytes(buffer) == 1;
light_mode
}
pub fn is_dark_theme() -> bool {
!is_light_theme()
}
// convert &str to Win32 PWSTR
#[derive(Default)]
pub struct WideString(pub Vec<u16>);
pub trait ToWide {
fn to_wide(&self) -> WideString;
}
impl ToWide for &str {
fn to_wide(&self) -> WideString {
let mut result: Vec<u16> = self.encode_utf16().collect();
result.push(0);
WideString(result)
}
}
impl ToWide for String {
fn to_wide(&self) -> WideString {
let mut result: Vec<u16> = self.encode_utf16().collect();
result.push(0);
WideString(result)
}
}
impl WideString {
pub fn as_pwstr(&self) -> PWSTR {
PWSTR(self.0.as_ptr() as *mut _)
}
}
From where is it getting HKEY_CURRENT_USER
, RRF_RT_REG_DWORD
, ERROR_SUCCESS
?
I'm getting HKEY_CURRENT_USER
with help of winreg
crate, but I assume you can get HKEY_CURRENT_USER
, RRF_RT_REG_DWORD
from windows-rs
directly, couldn't find this info.
I know that this code will not change system colors, reading theme value from registry is the closest target for me right now.
Upvotes: 0
Views: 274
Reputation: 51394
The first question revolves around discoverability. The current situation isn't great and you will have to look up information in two places:
The first resource answers the question of where to find any given symbol. Both HKEY_CURRENT_USER
and RRF_RT_REG_DWORD
are exported from the windows::Win32::System::Registry
module. (I'd link to the documentation, but those links may no longer be correct when the next version is published1.)
ERROR_SUCCESS
is exported from the windows::Win32::Foundation
module, a foundational module used across the entire API.
Once you know which modules to use
you have to make sure to enable the respective features. That's what the second resource is for. The only feature you need here is "Win32_System_Registry"
; the windows::Win32::Foundation
module is no longer feature-gated.
The second question is concerned with string representations. Rust's internal string representation is unfortunately alien to Windows' and work is required to bridge the impedance mismatch. The windows
crate provides basic translation primitives. For string literals, you can use the w!
macro that re-encodes the source string as UTF-16, appends a NUL character and returns a PCWSTR
ready for use.
The following compiles and runs as expected:
main.rs
use windows::{
core::w,
Win32::{Foundation::*, System::Registry::*},
};
pub fn is_light_theme() -> bool {
// based on https://stackoverflow.com/a/51336913/709884
let mut buffer: [u8; 4] = [0; 4];
let mut cb_data: u32 = (buffer.len()).try_into().unwrap();
let res = unsafe {
RegGetValueW(
HKEY_CURRENT_USER,
w!(r#"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize"#),
w!("AppsUseLightTheme"),
RRF_RT_REG_DWORD,
Some(std::ptr::null_mut()),
Some(buffer.as_mut_ptr() as _),
Some(&mut cb_data as *mut u32),
)
};
assert_eq!(res, ERROR_SUCCESS);
// REG_DWORD is signed 32-bit, using little endian
let light_mode = i32::from_le_bytes(buffer) == 1;
light_mode
}
fn main() {
let light_theme = is_light_theme();
println!("Light theme: {light_theme:?}");
}
Cargo.toml
[package]
name = "is_light_theme"
version = "0.0.0"
edition = "2021"
[dependencies.windows]
version = "0.54.0"
features = ["Win32_System_Registry"]
There's no need to use the winreg
crate.
As an alternative, you could use the windows-registry
instead. It is part of the same repository as the windows
crate. This vastly cuts down on the amount of code you need to write.
main.rs
use windows_registry::*;
pub fn is_light_theme() -> bool {
let val = CURRENT_USER
.open(r#"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize"#)
.expect("Failed to get registry key")
.get_u32("AppsUseLightTheme")
.expect("Failed to get registry value");
val != 0
}
fn main() {
let light_theme = is_light_theme();
println!("Light theme: {light_theme:?}");
}
Cargo.toml
[package]
name = "is_light_theme"
version = "0.0.0"
edition = "2021"
[dependencies]
windows-registry = { version = "0.1.0" }
1 Unfortunately, docs.rs
is unable to handle the windows
crate, and the crate maintainers are forced to entertain a custom hosting solution with a fire-and-forget data retention policy.
Upvotes: 0