1 //! Implementation of `std::os` functionality for Windows.
3 #![allow(nonstandard_style)]
8 use crate::os::windows::prelude::*;
10 use crate::error::Error as StdError;
11 use crate::ffi::{OsStr, OsString};
14 use crate::os::windows::ffi::EncodeWide;
15 use crate::path::{self, PathBuf};
18 use crate::sys::{c, cvt};
22 pub fn errno() -> i32 {
23 unsafe { c::GetLastError() as i32 }
26 /// Gets a detailed string description for the given error number.
27 pub fn error_string(mut errnum: i32) -> String {
28 // This value is calculated from the macro
29 // MAKELANGID(LANG_SYSTEM_DEFAULT, SUBLANG_SYS_DEFAULT)
30 let langId = 0x0800 as c::DWORD;
32 let mut buf = [0 as c::WCHAR; 2048];
35 let mut module = ptr::null_mut();
38 // NTSTATUS errors may be encoded as HRESULT, which may returned from
39 // GetLastError. For more information about Windows error codes, see
40 // `[MS-ERREF]`: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/0642cb2f-2075-4469-918c-4441e69c548a
41 if (errnum & c::FACILITY_NT_BIT as i32) != 0 {
42 // format according to https://support.microsoft.com/en-us/help/259693
43 const NTDLL_DLL: &[u16] = &[
44 'N' as _, 'T' as _, 'D' as _, 'L' as _, 'L' as _, '.' as _, 'D' as _, 'L' as _,
47 module = c::GetModuleHandleW(NTDLL_DLL.as_ptr());
49 if !module.is_null() {
50 errnum ^= c::FACILITY_NT_BIT as i32;
51 flags = c::FORMAT_MESSAGE_FROM_HMODULE;
55 let res = c::FormatMessageW(
56 flags | c::FORMAT_MESSAGE_FROM_SYSTEM | c::FORMAT_MESSAGE_IGNORE_INSERTS,
61 buf.len() as c::DWORD,
65 // Sometimes FormatMessageW can fail e.g., system doesn't like langId,
67 return format!("OS Error {errnum} (FormatMessageW() returned error {fm_err})");
70 match String::from_utf16(&buf[..res]) {
72 // Trim trailing CRLF inserted by FormatMessageW
73 let len = msg.trim_end().len();
78 "OS Error {} (FormatMessageW() returned \
91 impl Iterator for Env {
92 type Item = (OsString, OsString);
94 fn next(&mut self) -> Option<(OsString, OsString)> {
100 let p = self.cur as *const u16;
102 while *p.add(len) != 0 {
105 let s = slice::from_raw_parts(p, len);
106 self.cur = self.cur.add(len + 1);
108 // Windows allows environment variables to start with an equals
109 // symbol (in any other position, this is the separator between
110 // variable name and value). Since`s` has at least length 1 at
111 // this point (because the empty string terminates the array of
112 // environment variables), we can safely slice.
113 let pos = match s[1..].iter().position(|&u| u == b'=' as u16).map(|p| p + 1) {
118 OsStringExt::from_wide(&s[..pos]),
119 OsStringExt::from_wide(&s[pos + 1..]),
129 c::FreeEnvironmentStringsW(self.base);
134 pub fn env() -> Env {
136 let ch = c::GetEnvironmentStringsW();
138 panic!("failure getting env string from OS: {}", io::Error::last_os_error());
140 Env { base: ch, cur: ch }
144 pub struct SplitPaths<'a> {
145 data: EncodeWide<'a>,
149 pub fn split_paths(unparsed: &OsStr) -> SplitPaths<'_> {
150 SplitPaths { data: unparsed.encode_wide(), must_yield: true }
153 impl<'a> Iterator for SplitPaths<'a> {
155 fn next(&mut self) -> Option<PathBuf> {
156 // On Windows, the PATH environment variable is semicolon separated.
157 // Double quotes are used as a way of introducing literal semicolons
158 // (since c:\some;dir is a valid Windows path). Double quotes are not
159 // themselves permitted in path names, so there is no way to escape a
160 // double quote. Quoted regions can appear in arbitrary locations, so
162 // c:\foo;c:\som"e;di"r;c:\bar
164 // Should parse as [c:\foo, c:\some;dir, c:\bar].
166 // (The above is based on testing; there is no clear reference available
169 let must_yield = self.must_yield;
170 self.must_yield = false;
172 let mut in_progress = Vec::new();
173 let mut in_quote = false;
174 for b in self.data.by_ref() {
176 in_quote = !in_quote;
177 } else if b == ';' as u16 && !in_quote {
178 self.must_yield = true;
185 if !must_yield && in_progress.is_empty() {
188 Some(super::os2path(&in_progress))
194 pub struct JoinPathsError;
196 pub fn join_paths<I, T>(paths: I) -> Result<OsString, JoinPathsError>
198 I: Iterator<Item = T>,
201 let mut joined = Vec::new();
202 let sep = b';' as u16;
204 for (i, path) in paths.enumerate() {
205 let path = path.as_ref();
209 let v = path.encode_wide().collect::<Vec<u16>>();
210 if v.contains(&(b'"' as u16)) {
211 return Err(JoinPathsError);
212 } else if v.contains(&sep) {
213 joined.push(b'"' as u16);
214 joined.extend_from_slice(&v[..]);
215 joined.push(b'"' as u16);
217 joined.extend_from_slice(&v[..]);
221 Ok(OsStringExt::from_wide(&joined[..]))
224 impl fmt::Display for JoinPathsError {
225 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
226 "path segment contains `\"`".fmt(f)
230 impl StdError for JoinPathsError {
232 fn description(&self) -> &str {
233 "failed to join paths"
237 pub fn current_exe() -> io::Result<PathBuf> {
238 super::fill_utf16_buf(
239 |buf, sz| unsafe { c::GetModuleFileNameW(ptr::null_mut(), buf, sz) },
244 pub fn getcwd() -> io::Result<PathBuf> {
245 super::fill_utf16_buf(|buf, sz| unsafe { c::GetCurrentDirectoryW(sz, buf) }, super::os2path)
248 pub fn chdir(p: &path::Path) -> io::Result<()> {
249 let p: &OsStr = p.as_ref();
250 let mut p = p.encode_wide().collect::<Vec<_>>();
253 cvt(unsafe { c::SetCurrentDirectoryW(p.as_ptr()) }).map(drop)
256 pub fn getenv(k: &OsStr) -> Option<OsString> {
257 let k = to_u16s(k).ok()?;
258 super::fill_utf16_buf(
259 |buf, sz| unsafe { c::GetEnvironmentVariableW(k.as_ptr(), buf, sz) },
260 |buf| OsStringExt::from_wide(buf),
265 pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
269 cvt(unsafe { c::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr()) }).map(drop)
272 pub fn unsetenv(n: &OsStr) -> io::Result<()> {
274 cvt(unsafe { c::SetEnvironmentVariableW(v.as_ptr(), ptr::null()) }).map(drop)
277 pub fn temp_dir() -> PathBuf {
278 super::fill_utf16_buf(|buf, sz| unsafe { c::GetTempPath2W(sz, buf) }, super::os2path).unwrap()
281 #[cfg(not(target_vendor = "uwp"))]
282 fn home_dir_crt() -> Option<PathBuf> {
284 // The magic constant -4 can be used as the token passed to GetUserProfileDirectoryW below
285 // instead of us having to go through these multiple steps to get a token. However this is
286 // not implemented on Windows 7, only Windows 8 and up. When we drop support for Windows 7
287 // we can simplify this code. See #90144 for details.
288 use crate::sys::handle::Handle;
290 let me = c::GetCurrentProcess();
291 let mut token = ptr::null_mut();
292 if c::OpenProcessToken(me, c::TOKEN_READ, &mut token) == 0 {
295 let _handle = Handle::from_raw_handle(token);
296 super::fill_utf16_buf(
298 match c::GetUserProfileDirectoryW(token, buf, &mut sz) {
299 0 if c::GetLastError() != c::ERROR_INSUFFICIENT_BUFFER => 0,
301 _ => sz - 1, // sz includes the null terminator
310 #[cfg(target_vendor = "uwp")]
311 fn home_dir_crt() -> Option<PathBuf> {
315 pub fn home_dir() -> Option<PathBuf> {
316 crate::env::var_os("HOME")
317 .or_else(|| crate::env::var_os("USERPROFILE"))
319 .or_else(|| home_dir_crt())
322 pub fn exit(code: i32) -> ! {
323 unsafe { c::ExitProcess(code as c::UINT) }
326 pub fn getpid() -> u32 {
327 unsafe { c::GetCurrentProcessId() as u32 }