Skip to content

Commit a6c84c2

Browse files
authored
Merge pull request RustPython#1793 from RustPython/coolreader18/winreg-module
Add the winreg stdlib module
2 parents 7f7cad3 + eec3333 commit a6c84c2

File tree

5 files changed

+329
-2
lines changed

5 files changed

+329
-2
lines changed

Cargo.lock

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/importlib/_bootstrap_external.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1605,8 +1605,7 @@ def _setup(_bootstrap_module):
16051605
setattr(self_module, '_weakref', weakref_module)
16061606

16071607
# Directly load the winreg module (needed during bootstrap).
1608-
# XXX RustPython TODO: winreg module
1609-
if builtin_os == 'nt' and False:
1608+
if builtin_os == 'nt':
16101609
winreg_module = _bootstrap._builtin_from_name('winreg')
16111610
setattr(self_module, '_winreg', winreg_module)
16121611

vm/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ rustyline = "6.0"
9494
[target.'cfg(not(any(target_arch = "wasm32", target_os = "redox")))'.dependencies]
9595
dns-lookup = "1.0"
9696

97+
[target.'cfg(windows)'.dependencies]
98+
winreg = "0.7"
99+
97100
[target."cfg(windows)".dependencies.winapi]
98101
version = "0.3"
99102
features = ["winsock2", "handleapi", "ws2def", "std", "winbase"]

vm/src/stdlib/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ pub mod signal;
5252
mod subprocess;
5353
#[cfg(windows)]
5454
mod winapi;
55+
#[cfg(windows)]
56+
mod winreg;
5557
#[cfg(not(target_arch = "wasm32"))]
5658
mod zlib;
5759

@@ -135,6 +137,7 @@ pub fn get_module_inits() -> HashMap<String, StdlibInitFunc> {
135137
#[cfg(windows)]
136138
{
137139
modules.insert("_winapi".to_owned(), Box::new(winapi::make_module));
140+
modules.insert("winreg".to_owned(), Box::new(winreg::make_module));
138141
}
139142

140143
modules

vm/src/stdlib/winreg.rs

Lines changed: 312 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,312 @@
1+
#![allow(non_snake_case)]
2+
3+
use std::cell::{Ref, RefCell};
4+
use std::convert::TryInto;
5+
use std::io;
6+
7+
use super::os;
8+
use crate::function::OptionalArg;
9+
use crate::obj::objstr::PyStringRef;
10+
use crate::obj::objtype::PyClassRef;
11+
use crate::pyobject::{PyClassImpl, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject};
12+
use crate::VirtualMachine;
13+
14+
use winapi::shared::winerror;
15+
use winreg::{enums::RegType, RegKey, RegValue};
16+
17+
#[pyclass]
18+
#[derive(Debug)]
19+
struct PyHKEY {
20+
key: RefCell<RegKey>,
21+
}
22+
type PyHKEYRef = PyRef<PyHKEY>;
23+
24+
impl PyValue for PyHKEY {
25+
fn class(vm: &VirtualMachine) -> PyClassRef {
26+
vm.class("winreg", "HKEYType")
27+
}
28+
}
29+
30+
#[pyimpl]
31+
impl PyHKEY {
32+
fn new(key: RegKey) -> Self {
33+
Self {
34+
key: RefCell::new(key),
35+
}
36+
}
37+
38+
fn key(&self) -> Ref<RegKey> {
39+
self.key.borrow()
40+
}
41+
42+
#[pymethod]
43+
fn Close(&self) {
44+
let null_key = RegKey::predef(0 as winreg::HKEY);
45+
let key = self.key.replace(null_key);
46+
drop(key);
47+
}
48+
#[pymethod]
49+
fn Detach(&self) -> usize {
50+
let null_key = RegKey::predef(0 as winreg::HKEY);
51+
let key = self.key.replace(null_key);
52+
let handle = key.raw_handle();
53+
std::mem::forget(key);
54+
handle as usize
55+
}
56+
57+
#[pymethod(magic)]
58+
fn bool(&self) -> bool {
59+
!self.key().raw_handle().is_null()
60+
}
61+
#[pymethod(magic)]
62+
fn enter(zelf: PyRef<Self>) -> PyRef<Self> {
63+
zelf
64+
}
65+
#[pymethod(magic)]
66+
fn exit(&self, _cls: PyObjectRef, _exc: PyObjectRef, _tb: PyObjectRef) {
67+
self.Close();
68+
}
69+
}
70+
71+
enum Hkey {
72+
PyHKEY(PyHKEYRef),
73+
Constant(winreg::HKEY),
74+
}
75+
impl TryFromObject for Hkey {
76+
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
77+
obj.downcast()
78+
.map(Self::PyHKEY)
79+
.or_else(|o| usize::try_from_object(vm, o).map(|i| Self::Constant(i as winreg::HKEY)))
80+
}
81+
}
82+
impl Hkey {
83+
fn with_key<R>(&self, f: impl FnOnce(&RegKey) -> R) -> R {
84+
match self {
85+
Self::PyHKEY(py) => f(&py.key()),
86+
Self::Constant(hkey) => {
87+
let k = RegKey::predef(*hkey);
88+
let res = f(&k);
89+
std::mem::forget(k);
90+
res
91+
}
92+
}
93+
}
94+
}
95+
96+
fn winreg_OpenKey(
97+
key: Hkey,
98+
subkey: Option<PyStringRef>,
99+
reserved: OptionalArg<i32>,
100+
access: OptionalArg<u32>,
101+
vm: &VirtualMachine,
102+
) -> PyResult<PyHKEY> {
103+
let reserved = reserved.unwrap_or(0);
104+
let access = access.unwrap_or(winreg::enums::KEY_READ);
105+
if reserved != 0 {
106+
// RegKey::open_subkey* doesn't have a reserved param, so this'll do
107+
return Err(vm.new_value_error("reserved param must be 0".to_owned()));
108+
}
109+
110+
let subkey = subkey.as_ref().map_or("", |s| s.as_str());
111+
let key = key
112+
.with_key(|k| k.open_subkey_with_flags(subkey, access))
113+
.map_err(|e| os::convert_io_error(vm, e))?;
114+
115+
Ok(PyHKEY::new(key))
116+
}
117+
118+
fn winreg_QueryValue(
119+
key: Hkey,
120+
subkey: Option<PyStringRef>,
121+
vm: &VirtualMachine,
122+
) -> PyResult<String> {
123+
let subkey = subkey.as_ref().map_or("", |s| s.as_str());
124+
key.with_key(|k| k.get_value(subkey))
125+
.map_err(|e| os::convert_io_error(vm, e))
126+
}
127+
128+
fn winreg_QueryValueEx(
129+
key: Hkey,
130+
subkey: Option<PyStringRef>,
131+
vm: &VirtualMachine,
132+
) -> PyResult<(PyObjectRef, usize)> {
133+
let subkey = subkey.as_ref().map_or("", |s| s.as_str());
134+
key.with_key(|k| k.get_raw_value(subkey))
135+
.map_err(|e| os::convert_io_error(vm, e))
136+
.and_then(|regval| {
137+
let ty = regval.vtype.clone() as usize;
138+
Ok((reg_to_py(regval, vm)?, ty))
139+
})
140+
}
141+
142+
fn winreg_EnumKey(key: Hkey, index: u32, vm: &VirtualMachine) -> PyResult<String> {
143+
key.with_key(|k| k.enum_keys().nth(index as usize))
144+
.unwrap_or_else(|| {
145+
Err(io::Error::from_raw_os_error(
146+
winerror::ERROR_NO_MORE_ITEMS as i32,
147+
))
148+
})
149+
.map_err(|e| os::convert_io_error(vm, e))
150+
}
151+
152+
fn winreg_EnumValue(
153+
key: Hkey,
154+
index: u32,
155+
vm: &VirtualMachine,
156+
) -> PyResult<(String, PyObjectRef, usize)> {
157+
key.with_key(|k| k.enum_values().nth(index as usize))
158+
.unwrap_or_else(|| {
159+
Err(io::Error::from_raw_os_error(
160+
winerror::ERROR_NO_MORE_ITEMS as i32,
161+
))
162+
})
163+
.map_err(|e| os::convert_io_error(vm, e))
164+
.and_then(|(name, value)| {
165+
let ty = value.vtype.clone() as usize;
166+
Ok((name, reg_to_py(value, vm)?, ty))
167+
})
168+
}
169+
170+
fn winreg_CloseKey(key: Hkey) {
171+
match key {
172+
Hkey::PyHKEY(py) => py.Close(),
173+
Hkey::Constant(hkey) => drop(RegKey::predef(hkey)),
174+
}
175+
}
176+
177+
fn reg_to_py(value: RegValue, vm: &VirtualMachine) -> PyResult {
178+
macro_rules! bytes_to_int {
179+
($int:ident, $f:ident, $name:ident) => {{
180+
let i = if value.bytes.is_empty() {
181+
Ok(0 as $int)
182+
} else {
183+
(&*value.bytes).try_into().map($int::$f).map_err(|_| {
184+
vm.new_value_error(format!("{} value is wrong length", stringify!(name)))
185+
})
186+
};
187+
i.map(|i| vm.new_int(i))
188+
}};
189+
};
190+
let bytes_to_wide = |b: &[u8]| -> Option<&[u16]> {
191+
if b.len() % 2 == 0 {
192+
Some(unsafe { std::slice::from_raw_parts(b.as_ptr().cast(), b.len() / 2) })
193+
} else {
194+
None
195+
}
196+
};
197+
match value.vtype {
198+
RegType::REG_DWORD => bytes_to_int!(u32, from_ne_bytes, REG_DWORD),
199+
RegType::REG_DWORD_BIG_ENDIAN => bytes_to_int!(u32, from_be_bytes, REG_DWORD_BIG_ENDIAN),
200+
RegType::REG_QWORD => bytes_to_int!(u64, from_ne_bytes, REG_DWORD),
201+
// RegType::REG_QWORD_BIG_ENDIAN => bytes_to_int!(u64, from_be_bytes, REG_DWORD_BIG_ENDIAN),
202+
RegType::REG_SZ | RegType::REG_EXPAND_SZ => {
203+
let wide_slice = bytes_to_wide(&value.bytes).ok_or_else(|| {
204+
vm.new_value_error("REG_SZ string doesn't have an even byte length".to_owned())
205+
})?;
206+
let nul_pos = wide_slice
207+
.iter()
208+
.position(|w| *w == 0)
209+
.unwrap_or_else(|| wide_slice.len());
210+
let s = String::from_utf16_lossy(&wide_slice[..nul_pos]);
211+
Ok(vm.new_str(s))
212+
}
213+
RegType::REG_MULTI_SZ => {
214+
if value.bytes.is_empty() {
215+
return Ok(vm.ctx.new_list(vec![]));
216+
}
217+
let wide_slice = bytes_to_wide(&value.bytes).ok_or_else(|| {
218+
vm.new_value_error(
219+
"REG_MULTI_SZ string doesn't have an even byte length".to_owned(),
220+
)
221+
})?;
222+
let wide_slice = if let Some((0, rest)) = wide_slice.split_last() {
223+
rest
224+
} else {
225+
wide_slice
226+
};
227+
let strings = wide_slice
228+
.split(|c| *c == 0)
229+
.map(|s| vm.new_str(String::from_utf16_lossy(s)))
230+
.collect();
231+
Ok(vm.ctx.new_list(strings))
232+
}
233+
RegType::REG_BINARY | _ => {
234+
if value.bytes.is_empty() {
235+
Ok(vm.get_none())
236+
} else {
237+
Ok(vm.ctx.new_bytes(value.bytes))
238+
}
239+
}
240+
}
241+
}
242+
243+
pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
244+
let ctx = &vm.ctx;
245+
let hkey_type = PyHKEY::make_class(ctx);
246+
let module = py_module!(vm, "winreg", {
247+
"HKEYType" => hkey_type,
248+
"OpenKey" => ctx.new_function(winreg_OpenKey),
249+
"QueryValue" => ctx.new_function(winreg_QueryValue),
250+
"QueryValueEx" => ctx.new_function(winreg_QueryValueEx),
251+
"EnumKey" => ctx.new_function(winreg_EnumKey),
252+
"EnumValue" => ctx.new_function(winreg_EnumValue),
253+
"CloseKey" => ctx.new_function(winreg_CloseKey),
254+
});
255+
256+
macro_rules! add_constants {
257+
(hkey, $($name:ident),*$(,)?) => {
258+
extend_module!(vm, module, {
259+
$((stringify!($name)) => ctx.new_int(winreg::enums::$name as usize)),*
260+
})
261+
};
262+
(winnt, $($name:ident),*$(,)?) => {
263+
extend_module!(vm, module, {
264+
$((stringify!($name)) => ctx.new_int(winapi::um::winnt::$name)),*
265+
})
266+
};
267+
}
268+
269+
add_constants!(
270+
hkey,
271+
HKEY_CLASSES_ROOT,
272+
HKEY_CURRENT_USER,
273+
HKEY_LOCAL_MACHINE,
274+
HKEY_USERS,
275+
HKEY_PERFORMANCE_DATA,
276+
HKEY_CURRENT_CONFIG,
277+
HKEY_DYN_DATA,
278+
);
279+
add_constants!(
280+
winnt,
281+
// access rights
282+
KEY_ALL_ACCESS,
283+
KEY_WRITE,
284+
KEY_READ,
285+
KEY_EXECUTE,
286+
KEY_QUERY_VALUE,
287+
KEY_SET_VALUE,
288+
KEY_CREATE_SUB_KEY,
289+
KEY_ENUMERATE_SUB_KEYS,
290+
KEY_NOTIFY,
291+
KEY_CREATE_LINK,
292+
KEY_WOW64_64KEY,
293+
KEY_WOW64_32KEY,
294+
// value types
295+
REG_BINARY,
296+
REG_DWORD,
297+
REG_DWORD_LITTLE_ENDIAN,
298+
REG_DWORD_BIG_ENDIAN,
299+
REG_EXPAND_SZ,
300+
REG_LINK,
301+
REG_MULTI_SZ,
302+
REG_NONE,
303+
REG_QWORD,
304+
REG_QWORD_LITTLE_ENDIAN,
305+
REG_RESOURCE_LIST,
306+
REG_FULL_RESOURCE_DESCRIPTOR,
307+
REG_RESOURCE_REQUIREMENTS_LIST,
308+
REG_SZ,
309+
);
310+
311+
module
312+
}

0 commit comments

Comments
 (0)