interprocess_docfix/os/windows/
mod.rs

1//! Windows-specific functionality for various interprocess communication primitives, as well as Windows-specific ones.
2#![cfg_attr(not(windows), allow(warnings))]
3
4pub mod named_pipe;
5#[cfg(any(doc, feature = "signals"))]
6#[cfg_attr(feature = "doc_cfg", doc(cfg(feature = "signals")))]
7pub mod signal;
8pub mod unnamed_pipe;
9// TODO mailslots
10//pub mod mailslot;
11#[cfg(windows)]
12pub(crate) mod local_socket;
13
14pub(crate) mod imports;
15use imports::*;
16
17use std::{io, mem::ManuallyDrop, ptr};
18
19/// Objects which own handles which can be shared with another processes.
20///
21/// On Windows, like with most other operating systems, handles belong to specific processes. You shouldn't just send the value of a handle to another process (with a named pipe, for example) and expect it to work on the other side. For this to work, you need [`DuplicateHandle`] – the Win32 API function which duplicates a handle into the handle table of the specified process (the reciever is referred to by its handle). This trait exposes the `DuplicateHandle` functionality in a safe manner. If the handle is *inheritable*, however, all child processes of a process inherit the handle and thus can use the same value safely without the need to share it. *All Windows handle objects created by this crate are inheritable.*
22///
23/// **Implemented for all types inside this crate which implement [`AsRawHandle`] and are supposed to be shared between processes.**
24///
25/// [`DuplicateHandle`]: https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-duplicatehandle " "
26/// [`AsRawHandle`]: https://doc.rust-lang.org/std/os/windows/io/trait.AsRawHandle.html " "
27pub trait ShareHandle: AsRawHandle {
28    /// Duplicates the handle to make it accessible in the specified process (taken as a handle to that process) and returns the raw value of the handle which can then be sent via some form of IPC, typically named pipes. This is the only way to use any form of IPC other than named pipes to communicate between two processes which do not have a parent-child relationship or if the handle wasn't created as inheritable, therefore named pipes paired with this are a hard requirement in order to communicate between processes if one wasn't spawned by another.
29    ///
30    /// Backed by [`DuplicateHandle`]. Doesn't require unsafe code since `DuplicateHandle` never leads to undefined behavior if the `lpTargetHandle` parameter is a valid pointer, only creates an error.
31    #[allow(clippy::not_unsafe_ptr_arg_deref)] // Handles are not pointers, they have handle checks
32    fn share(&self, reciever: HANDLE) -> io::Result<HANDLE> {
33        let (success, new_handle) = unsafe {
34            let mut new_handle = INVALID_HANDLE_VALUE;
35            let success = DuplicateHandle(
36                GetCurrentProcess(),
37                self.as_raw_handle(),
38                reciever,
39                &mut new_handle,
40                0,
41                1,
42                0,
43            );
44            (success != 0, new_handle)
45        };
46        if success {
47            Ok(new_handle)
48        } else {
49            Err(io::Error::last_os_error())
50        }
51    }
52}
53#[cfg(windows)]
54impl ShareHandle for crate::unnamed_pipe::UnnamedPipeReader {}
55#[cfg(windows)]
56impl ShareHandle for unnamed_pipe::UnnamedPipeReader {}
57#[cfg(windows)]
58impl ShareHandle for crate::unnamed_pipe::UnnamedPipeWriter {}
59#[cfg(windows)]
60impl ShareHandle for unnamed_pipe::UnnamedPipeWriter {}
61
62/// Newtype wrapper which defines file I/O operations on a `HANDLE` to a file.
63#[repr(transparent)]
64#[derive(Debug)]
65pub(crate) struct FileHandleOps(pub(crate) HANDLE);
66impl FileHandleOps {
67    pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
68        debug_assert!(
69            buf.len() <= DWORD::max_value() as usize,
70            "buffer is bigger than maximum buffer size for ReadFile",
71        );
72        let (success, num_bytes_read) = unsafe {
73            let mut num_bytes_read: DWORD = 0;
74            let result = ReadFile(
75                self.0,
76                buf.as_mut_ptr() as *mut _,
77                buf.len() as DWORD,
78                &mut num_bytes_read as *mut _,
79                ptr::null_mut(),
80            );
81            (result != 0, num_bytes_read as usize)
82        };
83        if success {
84            Ok(num_bytes_read)
85        } else {
86            Err(io::Error::last_os_error())
87        }
88    }
89    pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
90        debug_assert!(
91            buf.len() <= DWORD::max_value() as usize,
92            "buffer is bigger than maximum buffer size for WriteFile",
93        );
94        let (success, bytes_written) = unsafe {
95            let mut number_of_bytes_written: DWORD = 0;
96            let result = WriteFile(
97                self.0,
98                buf.as_ptr() as *mut _,
99                buf.len() as DWORD,
100                &mut number_of_bytes_written as *mut _,
101                ptr::null_mut(),
102            );
103            (result != 0, number_of_bytes_written as usize)
104        };
105        if success {
106            Ok(bytes_written)
107        } else {
108            Err(io::Error::last_os_error())
109        }
110    }
111    pub fn flush(&self) -> io::Result<()> {
112        let success = unsafe { FlushFileBuffers(self.0) != 0 };
113        if success {
114            Ok(())
115        } else {
116            Err(io::Error::last_os_error())
117        }
118    }
119}
120impl Drop for FileHandleOps {
121    fn drop(&mut self) {
122        let _success = unsafe { CloseHandle(self.0) != 0 };
123        debug_assert!(
124            _success,
125            "failed to close file handle: {}",
126            io::Error::last_os_error()
127        );
128    }
129}
130#[cfg(windows)]
131impl AsRawHandle for FileHandleOps {
132    fn as_raw_handle(&self) -> HANDLE {
133        self.0
134    }
135}
136#[cfg(windows)]
137impl IntoRawHandle for FileHandleOps {
138    fn into_raw_handle(self) -> HANDLE {
139        let self_ = ManuallyDrop::new(self);
140        self_.as_raw_handle()
141    }
142}
143#[cfg(windows)]
144impl FromRawHandle for FileHandleOps {
145    unsafe fn from_raw_handle(op: HANDLE) -> Self {
146        Self(op)
147    }
148}
149unsafe impl Send for FileHandleOps {}
150unsafe impl Sync for FileHandleOps {} // WriteFile and ReadFile are thread-safe, apparently