1 // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10 
11 use std::ffi::{OsStr, OsString};
12 use std::io;
13 use std::ops::RangeFrom;
14 use std::os::raw;
15 use std::os::windows::prelude::*;
16 
17 pub struct RegistryKey(Repr);
18 
19 type HKEY = *mut u8;
20 type DWORD = u32;
21 type LPDWORD = *mut DWORD;
22 type LPCWSTR = *const u16;
23 type LPWSTR = *mut u16;
24 type LONG = raw::c_long;
25 type PHKEY = *mut HKEY;
26 type PFILETIME = *mut u8;
27 type LPBYTE = *mut u8;
28 type REGSAM = u32;
29 
30 const ERROR_SUCCESS: DWORD = 0;
31 const ERROR_NO_MORE_ITEMS: DWORD = 259;
32 const HKEY_LOCAL_MACHINE: HKEY = 0x80000002 as HKEY;
33 const REG_SZ: DWORD = 1;
34 const KEY_READ: DWORD = 0x20019;
35 const KEY_WOW64_32KEY: DWORD = 0x200;
36 
37 #[link(name = "advapi32")]
38 extern "system" {
RegOpenKeyExW( key: HKEY, lpSubKey: LPCWSTR, ulOptions: DWORD, samDesired: REGSAM, phkResult: PHKEY, ) -> LONG39     fn RegOpenKeyExW(
40         key: HKEY,
41         lpSubKey: LPCWSTR,
42         ulOptions: DWORD,
43         samDesired: REGSAM,
44         phkResult: PHKEY,
45     ) -> LONG;
RegEnumKeyExW( key: HKEY, dwIndex: DWORD, lpName: LPWSTR, lpcName: LPDWORD, lpReserved: LPDWORD, lpClass: LPWSTR, lpcClass: LPDWORD, lpftLastWriteTime: PFILETIME, ) -> LONG46     fn RegEnumKeyExW(
47         key: HKEY,
48         dwIndex: DWORD,
49         lpName: LPWSTR,
50         lpcName: LPDWORD,
51         lpReserved: LPDWORD,
52         lpClass: LPWSTR,
53         lpcClass: LPDWORD,
54         lpftLastWriteTime: PFILETIME,
55     ) -> LONG;
RegQueryValueExW( hKey: HKEY, lpValueName: LPCWSTR, lpReserved: LPDWORD, lpType: LPDWORD, lpData: LPBYTE, lpcbData: LPDWORD, ) -> LONG56     fn RegQueryValueExW(
57         hKey: HKEY,
58         lpValueName: LPCWSTR,
59         lpReserved: LPDWORD,
60         lpType: LPDWORD,
61         lpData: LPBYTE,
62         lpcbData: LPDWORD,
63     ) -> LONG;
RegCloseKey(hKey: HKEY) -> LONG64     fn RegCloseKey(hKey: HKEY) -> LONG;
65 }
66 
67 struct OwnedKey(HKEY);
68 
69 enum Repr {
70     Const(HKEY),
71     Owned(OwnedKey),
72 }
73 
74 pub struct Iter<'a> {
75     idx: RangeFrom<DWORD>,
76     key: &'a RegistryKey,
77 }
78 
79 unsafe impl Sync for Repr {}
80 unsafe impl Send for Repr {}
81 
82 pub static LOCAL_MACHINE: RegistryKey = RegistryKey(Repr::Const(HKEY_LOCAL_MACHINE));
83 
84 impl RegistryKey {
raw(&self) -> HKEY85     fn raw(&self) -> HKEY {
86         match self.0 {
87             Repr::Const(val) => val,
88             Repr::Owned(ref val) => val.0,
89         }
90     }
91 
open(&self, key: &OsStr) -> io::Result<RegistryKey>92     pub fn open(&self, key: &OsStr) -> io::Result<RegistryKey> {
93         let key = key.encode_wide().chain(Some(0)).collect::<Vec<_>>();
94         let mut ret = 0 as *mut _;
95         let err = unsafe {
96             RegOpenKeyExW(
97                 self.raw(),
98                 key.as_ptr(),
99                 0,
100                 KEY_READ | KEY_WOW64_32KEY,
101                 &mut ret,
102             )
103         };
104         if err == ERROR_SUCCESS as LONG {
105             Ok(RegistryKey(Repr::Owned(OwnedKey(ret))))
106         } else {
107             Err(io::Error::from_raw_os_error(err as i32))
108         }
109     }
110 
iter(&self) -> Iter111     pub fn iter(&self) -> Iter {
112         Iter {
113             idx: 0..,
114             key: self,
115         }
116     }
117 
query_str(&self, name: &str) -> io::Result<OsString>118     pub fn query_str(&self, name: &str) -> io::Result<OsString> {
119         let name: &OsStr = name.as_ref();
120         let name = name.encode_wide().chain(Some(0)).collect::<Vec<_>>();
121         let mut len = 0;
122         let mut kind = 0;
123         unsafe {
124             let err = RegQueryValueExW(
125                 self.raw(),
126                 name.as_ptr(),
127                 0 as *mut _,
128                 &mut kind,
129                 0 as *mut _,
130                 &mut len,
131             );
132             if err != ERROR_SUCCESS as LONG {
133                 return Err(io::Error::from_raw_os_error(err as i32));
134             }
135             if kind != REG_SZ {
136                 return Err(io::Error::new(
137                     io::ErrorKind::Other,
138                     "registry key wasn't a string",
139                 ));
140             }
141 
142             // The length here is the length in bytes, but we're using wide
143             // characters so we need to be sure to halve it for the capacity
144             // passed in.
145             let mut v = Vec::with_capacity(len as usize / 2);
146             let err = RegQueryValueExW(
147                 self.raw(),
148                 name.as_ptr(),
149                 0 as *mut _,
150                 0 as *mut _,
151                 v.as_mut_ptr() as *mut _,
152                 &mut len,
153             );
154             if err != ERROR_SUCCESS as LONG {
155                 return Err(io::Error::from_raw_os_error(err as i32));
156             }
157             v.set_len(len as usize / 2);
158 
159             // Some registry keys may have a terminating nul character, but
160             // we're not interested in that, so chop it off if it's there.
161             if v[v.len() - 1] == 0 {
162                 v.pop();
163             }
164             Ok(OsString::from_wide(&v))
165         }
166     }
167 }
168 
169 impl Drop for OwnedKey {
drop(&mut self)170     fn drop(&mut self) {
171         unsafe {
172             RegCloseKey(self.0);
173         }
174     }
175 }
176 
177 impl<'a> Iterator for Iter<'a> {
178     type Item = io::Result<OsString>;
179 
next(&mut self) -> Option<io::Result<OsString>>180     fn next(&mut self) -> Option<io::Result<OsString>> {
181         self.idx.next().and_then(|i| unsafe {
182             let mut v = Vec::with_capacity(256);
183             let mut len = v.capacity() as DWORD;
184             let ret = RegEnumKeyExW(
185                 self.key.raw(),
186                 i,
187                 v.as_mut_ptr(),
188                 &mut len,
189                 0 as *mut _,
190                 0 as *mut _,
191                 0 as *mut _,
192                 0 as *mut _,
193             );
194             if ret == ERROR_NO_MORE_ITEMS as LONG {
195                 None
196             } else if ret != ERROR_SUCCESS as LONG {
197                 Some(Err(io::Error::from_raw_os_error(ret as i32)))
198             } else {
199                 v.set_len(len as usize);
200                 Some(Ok(OsString::from_wide(&v)))
201             }
202         })
203     }
204 }
205