Skip to main content

win_handles_sys/
handle_information.rs

1// Copyright (c) 2026 Mike Grier
2//! What a handle can be asked about itself, and told: `GetHandleInformation` and
3//! `SetHandleInformation`.
4
5use std::io;
6use std::ops::BitOr;
7use std::os::windows::io::{AsHandle, AsRawHandle};
8
9use windows_sys::Win32::Foundation::{
10    FALSE, GetHandleInformation, HANDLE_FLAG_INHERIT, HANDLE_FLAG_PROTECT_FROM_CLOSE,
11    SetHandleInformation,
12};
13
14/// The flags a handle carries: whether a child process created with handle inheritance gets a copy of
15/// it, and whether closing it is refused.
16///
17/// A newtype over the Win32 bits, combined with `|`, because Windows defines the two the page
18/// documents and this crate does not narrow what the call reports: a bit it has never heard of is
19/// kept by [`from_bits`](Self::from_bits) and [`bits`](Self::bits) and is not an error.
20///
21/// # Example
22///
23/// ```
24/// use win_handles_sys::HandleFlags;
25///
26/// let both = HandleFlags::INHERIT | HandleFlags::PROTECT_FROM_CLOSE;
27/// assert!(both.contains(HandleFlags::INHERIT));
28/// assert!(both.contains(HandleFlags::PROTECT_FROM_CLOSE));
29/// assert!(!HandleFlags::INHERIT.contains(HandleFlags::PROTECT_FROM_CLOSE));
30/// assert_eq!(HandleFlags::default(), HandleFlags::NONE);
31/// ```
32#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
33pub struct HandleFlags(u32);
34
35impl HandleFlags {
36    /// Neither flag: the handle is not inherited and may be closed.
37    pub const NONE: Self = Self(0);
38
39    /// `HANDLE_FLAG_INHERIT`: a child process created with handle inheritance gets a copy of the handle.
40    pub const INHERIT: Self = Self(HANDLE_FLAG_INHERIT);
41
42    /// `HANDLE_FLAG_PROTECT_FROM_CLOSE`: closing the handle is refused.
43    pub const PROTECT_FROM_CLOSE: Self = Self(HANDLE_FLAG_PROTECT_FROM_CLOSE);
44
45    /// The flags from their Win32 bits.
46    #[must_use]
47    pub const fn from_bits(bits: u32) -> Self {
48        Self(bits)
49    }
50
51    /// The flags' Win32 bits.
52    #[must_use]
53    pub const fn bits(self) -> u32 {
54        self.0
55    }
56
57    /// Whether every flag in `other` is in `self`.
58    #[must_use]
59    pub const fn contains(self, other: Self) -> bool {
60        self.0 & other.0 == other.0
61    }
62}
63
64impl BitOr for HandleFlags {
65    type Output = Self;
66
67    fn bitor(self, other: Self) -> Self {
68        Self(self.0 | other.0)
69    }
70}
71
72/// The flags `handle` carries: `GetHandleInformation`.
73///
74/// # Example
75///
76/// A handle `std` opens is not inheritable:
77///
78/// ```
79/// use std::fs::File;
80///
81/// use win_handles_sys::{HandleFlags, get_handle_information};
82///
83/// let path = std::env::temp_dir().join(format!("wh-info-{}.tmp", std::process::id()));
84/// let file = File::create(&path)?;
85///
86/// assert!(!get_handle_information(&file)?.contains(HandleFlags::INHERIT));
87/// # drop(file);
88/// # std::fs::remove_file(&path)?;
89/// # Ok::<(), std::io::Error>(())
90/// ```
91///
92/// # Errors
93///
94/// The error from `GetHandleInformation`. A handle that is open for the borrow, which `AsHandle`
95/// guarantees, is not refused, so no test reaches it.
96pub fn get_handle_information(handle: impl AsHandle) -> io::Result<HandleFlags> {
97    let mut flags = 0u32;
98    // SAFETY: the handle is open for the borrow and `flags` is a valid out pointer.
99    let ok = unsafe { GetHandleInformation(handle.as_handle().as_raw_handle(), &mut flags) };
100    if ok == FALSE {
101        return Err(io::Error::last_os_error());
102    }
103    Ok(HandleFlags(flags))
104}
105
106/// Changes the flags `handle` carries: `SetHandleInformation`.
107///
108/// `mask` names the flags to change, and `flags` gives each of them its new value: a flag in `mask` is set if it is
109/// in `flags` and cleared if it is not. The page says "To set or clear the associated bit flag in dwFlags, you must
110/// set a change mask bit flag in dwMask", so a flag in `flags` that is not in `mask` is left as it was, and so is
111/// every flag the handle carries that is not in `mask`. The flags are the handle's and not its object's: a duplicate
112/// of it has its own.
113///
114/// Nothing is refused before the call: the page calls no value of either argument invalid, and a bit it does not
115/// name is the call's to judge ([`HandleFlags`]).
116///
117/// A handle given [`PROTECT_FROM_CLOSE`](HandleFlags::PROTECT_FROM_CLOSE) is not closed by `CloseHandle`, which is
118/// what dropping an `OwnedHandle` calls, so one dropped while protected stays open for the life of the process.
119/// Clear the flag first.
120///
121/// # Example
122///
123/// ```
124/// use std::fs::File;
125///
126/// use win_handles_sys::{HandleFlags, get_handle_information, set_handle_information};
127///
128/// let path = std::env::temp_dir().join(format!("wh-set-{}.tmp", std::process::id()));
129/// let file = File::create(&path)?;
130///
131/// // Make the handle inheritable, and leave every other flag as it was.
132/// set_handle_information(&file, HandleFlags::INHERIT, HandleFlags::INHERIT)?;
133/// assert!(get_handle_information(&file)?.contains(HandleFlags::INHERIT));
134///
135/// // A flag in `flags` but not in the mask is not changed.
136/// set_handle_information(&file, HandleFlags::NONE, HandleFlags::PROTECT_FROM_CLOSE)?;
137/// assert!(!get_handle_information(&file)?.contains(HandleFlags::PROTECT_FROM_CLOSE));
138///
139/// // A flag in the mask and not in `flags` is cleared.
140/// set_handle_information(&file, HandleFlags::INHERIT, HandleFlags::NONE)?;
141/// assert_eq!(get_handle_information(&file)?, HandleFlags::NONE);
142/// # drop(file);
143/// # std::fs::remove_file(&path)?;
144/// # Ok::<(), std::io::Error>(())
145/// ```
146///
147/// # Errors
148///
149/// The error from `SetHandleInformation`, which the page does not list. The current process's pseudo-handle, which
150/// `std` allows a `BorrowedHandle` to hold, was refused with `ERROR_ACCESS_DENIED` where it was measured
151/// (WH-D-3).
152pub fn set_handle_information(
153    handle: impl AsHandle,
154    mask: HandleFlags,
155    flags: HandleFlags,
156) -> io::Result<()> {
157    // SAFETY: the handle is open for the borrow.
158    let ok = unsafe { SetHandleInformation(handle.as_handle().as_raw_handle(), mask.0, flags.0) };
159    if ok == FALSE {
160        return Err(io::Error::last_os_error());
161    }
162    Ok(())
163}
164
165#[cfg(test)]
166mod tests;