minxp/
env.rs

1mod args;
2mod vars;
3mod path;
4pub mod consts;
5
6use crate::io::Error;
7use crate::path::{PathBuf, MAX_PATH_EXTENDED};
8use crate::util::{get_last_windows_error, get_proc_from_module};
9use alloc::string::String;
10use alloc::vec::Vec;
11use alloc::{format, vec};
12use core::iter::once;
13use core::ptr::null_mut;
14use spin::Lazy;
15use windows_sys::Win32::Foundation::{CloseHandle, FALSE};
16use windows_sys::Win32::Security::TOKEN_QUERY;
17use windows_sys::Win32::Storage::FileSystem::GetTempPathW;
18use windows_sys::Win32::System::Environment::{GetCurrentDirectoryW, SetCurrentDirectoryW};
19use windows_sys::Win32::System::LibraryLoader::GetModuleFileNameW;
20use windows_sys::Win32::System::Threading::{GetCurrentProcess, OpenProcessToken};
21use windows_sys::Win32::UI::Shell::GetUserProfileDirectoryW;
22pub use args::*;
23pub use vars::*;
24pub use path::*;
25
26pub fn current_exe() -> crate::io::Result<PathBuf> {
27    let mut path = vec![0u16; MAX_PATH_EXTENDED + 1];
28    let path_len = unsafe { GetModuleFileNameW(null_mut(), path.as_mut_ptr(), path.len() as u32) } as usize;
29    let err = get_last_windows_error();
30    assert_ne!(path_len, 0, "current_exe() failed: {err}");
31    let data = String::from_utf16(&path[..path_len]).expect("GetModuleFileNameW returned non UTF-16");
32
33    Ok(data.into())
34}
35
36pub fn current_dir() -> crate::io::Result<PathBuf> {
37    let mut path = vec![0u16; MAX_PATH_EXTENDED + 1];
38    let path_len = unsafe { GetCurrentDirectoryW(path.len() as u32, path.as_mut_ptr()) } as usize;
39    let err = get_last_windows_error();
40    assert_ne!(path_len, 0, "current_exe() failed: {err}");
41    let data = String::from_utf16(&path[..path_len]).expect("GetCurrentDirectoryW returned non UTF-16");
42
43    Ok(data.into())
44}
45
46pub fn home_dir() -> Option<PathBuf> {
47    if let Ok(v) = var("USERPROFILE") {
48        return Some(v.into())
49    };
50
51    let mut data = vec![0u16; MAX_PATH_EXTENDED + 1];
52    let handle = unsafe { GetCurrentProcess() };
53    let mut token_handle = null_mut();
54    let token = unsafe { OpenProcessToken(handle, TOKEN_QUERY, &mut token_handle) };
55
56    if token == FALSE {
57        return None
58    }
59
60    unsafe {
61        let mut len = data.len() as u32;
62        let success = GetUserProfileDirectoryW(token_handle, data.as_mut_ptr(), &mut len);
63        let err = get_last_windows_error();
64        CloseHandle(token_handle);
65        if success == FALSE {
66            return None
67        }
68    }
69
70    let null = data.iter().position(|s| *s == 0).unwrap_or(data.len());
71    String::from_utf16(&data[..null]).ok().map(Into::into)
72}
73
74pub fn set_current_dir<P: AsRef<str>>(path: P) -> crate::io::Result<()> {
75    let path: Vec<u16> = path.as_ref().encode_utf16().chain(once(0)).collect();
76    let success = unsafe { SetCurrentDirectoryW(path.as_ptr()) };
77    let last_err = get_last_windows_error();
78    match success {
79        0 => Err(Error { reason: format!("Cannot set path: {last_err}") }),
80        _ => Ok(())
81    }
82}
83
84type GetTempPathWFn = unsafe extern "system" fn (len: u32, *mut u16) -> u32;
85
86static GET_TEMP_PATH: Lazy<GetTempPathWFn> = Lazy::new(|| {
87    let get_temp_path: Option<GetTempPathWFn> = get_proc_from_module!(
88        "kernel32.dll",
89        "GetTempPath2W"
90    );
91
92    match get_temp_path {
93        Some(t) => t,
94        None => GetTempPathW
95    }
96});
97
98/// Get a temp directory.
99///
100/// # Compatibility notes
101///
102/// - On Windows 11 or newer, this uses [`GetTempPath2W`]
103/// - On any edition of Windows 10 that received the March 2025 monthly rollup, this also uses [`GetTempPath2W`]
104/// - On other versions of Windows, this uses [`GetTempPathW`]
105///
106/// The difference between `GetTempPath2` and `GetTempPath` is that `GetTempPath2` supports storing
107/// temporary files in a `SystemTemp` directory when running as a SYSTEM process. Otherwise, it will
108/// return the same value as `GetTempPath`.
109///
110/// [`GetTempPathW`]: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppathw
111/// [`GetTempPath2W`]: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppath2w
112pub fn temp_dir() -> PathBuf {
113    let mut buffer = vec![0u16; MAX_PATH_EXTENDED + 1];
114    let length = unsafe { GET_TEMP_PATH(buffer.len() as u32, buffer.as_mut_ptr()) };
115    buffer.truncate(length as usize);
116
117    let data = String::from_utf16(&buffer[..length as usize]).expect("GetTempPath(2)W returned non UTF-16");
118    data.into()
119}