running_process/
environment.rs1#[cfg(windows)]
8use std::ffi::c_void;
9use std::ffi::OsString;
10use std::io;
11
12pub fn user_baseline_environment() -> io::Result<Vec<(OsString, OsString)>> {
18 #[cfg(windows)]
19 {
20 let block = user_baseline_environment_block()?;
21 Ok(parse_windows_environment_block(&block))
22 }
23 #[cfg(not(windows))]
24 {
25 Ok(std::env::vars_os().collect())
26 }
27}
28
29#[cfg(windows)]
34pub fn user_baseline_environment_block() -> io::Result<Vec<u16>> {
35 use windows_sys::Win32::Foundation::CloseHandle;
36 use windows_sys::Win32::Security::TOKEN_QUERY;
37 use windows_sys::Win32::System::Environment::{
38 CreateEnvironmentBlock, DestroyEnvironmentBlock,
39 };
40 use windows_sys::Win32::System::Threading::{GetCurrentProcess, OpenProcessToken};
41
42 let mut token = std::ptr::null_mut();
43 let opened = unsafe { OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut token) };
44 if opened == 0 {
45 return Err(io::Error::last_os_error());
46 }
47
48 let mut raw_block: *mut c_void = std::ptr::null_mut();
49 let created = unsafe { CreateEnvironmentBlock(&mut raw_block, token, 0) };
50 let create_error = if created == 0 {
51 Some(io::Error::last_os_error())
52 } else {
53 None
54 };
55 unsafe {
56 CloseHandle(token);
57 }
58 if let Some(error) = create_error {
59 return Err(error);
60 }
61
62 let copied = unsafe { copy_windows_environment_block(raw_block.cast::<u16>()) };
63 unsafe {
64 DestroyEnvironmentBlock(raw_block);
65 }
66 Ok(copied)
67}
68
69#[cfg(windows)]
70unsafe fn copy_windows_environment_block(cursor: *const u16) -> Vec<u16> {
71 let mut len = 0usize;
72 loop {
73 if *cursor.add(len) == 0 && *cursor.add(len + 1) == 0 {
74 len += 2;
75 break;
76 }
77 len += 1;
78 }
79 std::slice::from_raw_parts(cursor, len).to_vec()
80}
81
82#[cfg(windows)]
83fn parse_windows_environment_block(block: &[u16]) -> Vec<(OsString, OsString)> {
84 use std::os::windows::ffi::OsStringExt;
85
86 let mut env = Vec::new();
87 let mut offset = 0usize;
88 while offset < block.len() && block[offset] != 0 {
89 let Some(relative_end) = block[offset..].iter().position(|value| *value == 0) else {
90 break;
91 };
92 let end = offset + relative_end;
93 let entry = &block[offset..end];
94 if let Some(separator) = entry
98 .iter()
99 .enumerate()
100 .skip(1)
101 .find_map(|(index, value)| (*value == b'=' as u16).then_some(index))
102 {
103 let key = OsString::from_wide(&entry[..separator]);
104 let value = OsString::from_wide(&entry[separator + 1..]);
105 env.push((key, value));
106 }
107 offset = end + 1;
108 }
109 env
110}
111
112#[cfg(all(test, windows))]
113mod tests {
114 use super::*;
115 use std::ffi::OsStr;
116 use std::os::windows::ffi::OsStrExt;
117
118 #[test]
119 fn parser_preserves_drive_current_directory_entries() {
120 let block: Vec<u16> = OsStr::new("=C:=C:\\work")
121 .encode_wide()
122 .chain(std::iter::once(0))
123 .chain(OsStr::new("Path=C:\\Windows").encode_wide())
124 .chain(std::iter::once(0))
125 .chain(std::iter::once(0))
126 .collect();
127 assert_eq!(
128 parse_windows_environment_block(&block),
129 vec![
130 (OsString::from("=C:"), OsString::from("C:\\work")),
131 (OsString::from("Path"), OsString::from("C:\\Windows")),
132 ]
133 );
134 }
135
136 #[test]
137 fn live_user_baseline_is_double_nul_terminated() {
138 let block = user_baseline_environment_block().unwrap();
139 assert!(block.len() >= 2);
140 assert_eq!(&block[block.len() - 2..], &[0, 0]);
141 }
142}