ctoolbox/workspace/ipc/platform/
windows.rs

1#[cfg(windows)]
2use anyhow::Context;
3
4#[cfg(windows)]
5use windows_sys::Win32::Foundation::{
6    CloseHandle, GetLastError, HANDLE, INVALID_HANDLE_VALUE,
7};
8
9#[cfg(windows)]
10use windows_sys::Win32::System::Memory::{
11    CreateFileMappingW, FILE_MAP_READ, FILE_MAP_WRITE, MapViewOfFile,
12    PAGE_READWRITE, UnmapViewOfFile,
13};
14
15#[cfg(windows)]
16use windows_sys::Win32::System::Threading::{
17    DUPLICATE_SAME_ACCESS, DuplicateHandle, GetCurrentProcess,
18};
19
20#[allow(unsafe_code)]
21#[cfg(windows)]
22fn last_err(msg: &str) -> Error {
23    let code = unsafe { GetLastError() };
24    anyhow::anyhow!("{msg} (GetLastError={code})").into()
25}
26
27#[cfg(windows)]
28fn to_handle(raw: u64) -> Result<HANDLE, Error> {
29    // HANDLE is pointer-sized; reject truncation on 32-bit.
30    let h = raw as usize;
31    anyhow::ensure!(h as u64 == raw, "HANDLE value does not fit in usize");
32    Ok(h as HANDLE)
33}
34
35#[allow(unsafe_code)]
36#[cfg(windows)]
37pub fn create_file_mapping(size: u64) -> Result<u64, Error> {
38    let high = (size >> 32) as u32;
39    let low = (size & 0xffff_ffff) as u32;
40
41    // Pagefile-backed mapping: INVALID_HANDLE_VALUE.
42    let handle = unsafe {
43        CreateFileMappingW(
44            INVALID_HANDLE_VALUE,
45            std::ptr::null(),
46            PAGE_READWRITE,
47            high,
48            low,
49            std::ptr::null(),
50        )
51    };
52    if handle == 0 {
53        return Err(last_err("CreateFileMappingW failed"));
54    }
55    Ok(handle as u64)
56}
57
58#[allow(unsafe_code)]
59#[cfg(windows)]
60pub fn duplicate_handle_current(handle: u64) -> Result<u64, Error> {
61    let src = unsafe { GetCurrentProcess() };
62    let h = to_handle(handle)?;
63    let mut out: HANDLE = 0;
64
65    let ok = unsafe {
66        DuplicateHandle(
67            src,
68            h,
69            src,
70            &mut out as *mut HANDLE,
71            0,
72            0,
73            DUPLICATE_SAME_ACCESS,
74        )
75    };
76    if ok == 0 {
77        return Err(last_err("DuplicateHandle failed"));
78    }
79    Ok(out as u64)
80}
81
82#[allow(unsafe_code)]
83#[cfg(windows)]
84pub fn close_handle(handle: u64) -> Result<(), Error> {
85    let h = to_handle(handle)?;
86    let ok = unsafe { CloseHandle(h) };
87    if ok == 0 {
88        return Err(last_err("CloseHandle failed"));
89    }
90    Ok(())
91}
92
93#[cfg(windows)]
94pub struct MappingView {
95    handle: HANDLE,
96    ptr: *mut u8,
97    len: usize,
98}
99
100#[cfg(windows)]
101impl MappingView {
102    pub fn as_ptr(&self) -> *const u8 {
103        self.ptr as *const u8
104    }
105}
106
107#[allow(unsafe_code)]
108#[cfg(windows)]
109impl Drop for MappingView {
110    fn drop(&mut self) {
111        unsafe {
112            if !self.ptr.is_null() {
113                let _ = UnmapViewOfFile(self.ptr as *const _);
114            }
115            if self.handle != 0 {
116                let _ = CloseHandle(self.handle);
117            }
118        }
119    }
120}
121
122#[allow(unsafe_code)]
123#[cfg(windows)]
124pub fn map_view_read(handle: u64, len: usize) -> Result<MappingView, Error> {
125    // Duplicate so the returned view owns a handle distinct from any registry
126    // handle retained by the allocator.
127    let dup = duplicate_handle_current(handle)?;
128    let h = to_handle(dup)?;
129
130    let ptr = unsafe { MapViewOfFile(h, FILE_MAP_READ, 0, 0, len) } as *mut u8;
131    if ptr.is_null() {
132        // Ensure the duplicated handle is closed.
133        let _ = unsafe { CloseHandle(h) };
134        return Err(last_err("MapViewOfFile(FILE_MAP_READ) failed"));
135    }
136
137    Ok(MappingView {
138        handle: h,
139        ptr,
140        len,
141    })
142}
143
144#[allow(unsafe_code)]
145#[cfg(windows)]
146pub fn write_mapping(handle: u64, data: &[u8]) -> Result<(), Error> {
147    let len = data.len();
148    let dup = duplicate_handle_current(handle)?;
149    let h = to_handle(dup)?;
150
151    let ptr = unsafe { MapViewOfFile(h, FILE_MAP_WRITE, 0, 0, len) } as *mut u8;
152    if ptr.is_null() {
153        let _ = unsafe { CloseHandle(h) };
154        return Err(last_err("MapViewOfFile(FILE_MAP_WRITE) failed"));
155    }
156
157    // Safety: ptr is valid for `len` bytes for this view; copy in-bounds.
158    unsafe {
159        std::ptr::copy_nonoverlapping(data.as_ptr(), ptr, len);
160        let _ = UnmapViewOfFile(ptr as *const _);
161        let _ = CloseHandle(h);
162    }
163
164    Ok(())
165}