pub struct CapturedHandle { /* private fields */ }Expand description
An owned duplicate of a handle a request names.
§A path is copied; a handle is duplicated
This is the distinction a caller reasoning in value semantics will get wrong, so it is stated rather than implied: a path is a value and is copied; a handle is a reference to a kernel object, and duplicating it shares that object rather than cloning it.
A request holding a CapturedHandle is therefore self-contained with
respect to lifetime – it cannot be left pointing at a handle its
originator closed, because it holds its own reference and closes it on drop
– and is not isolated with respect to state. Measured, not reasoned:
- A duplicate shares directory-enumeration state: it continues where the source stopped rather than starting its own listing. An independent traversal needs a fresh open, not a duplicate.
- Closing the duplicate does not disturb the source. This is what makes the whole design safe: a request may own a duplicate and drop it without damaging the handle its caller kept.
- Single-shot metadata queries disturb nothing, on the source or on a duplicate.
§What is duplicated
The duplicate carries the source’s access rights (DUPLICATE_SAME_ACCESS),
because a request must be able to perform exactly the call the caller opened
the handle for. It is not inheritable, so capturing a handle never
widens what a child process can reach.
§Example
The lifetime guarantee, which is the reason this type exists: the capture keeps working after its source is gone.
use std::fs;
use std::os::windows::io::AsHandle;
use windows_namespace_request_sys::CapturedHandle;
let path = std::env::temp_dir().join(format!("wnrs-dup-{}.tmp", std::process::id()));
fs::write(&path, b"seven..")?;
let captured = {
let file = fs::File::open(&path)?;
CapturedHandle::capture(file.as_handle())?
// `file` is closed here; the duplicate is not.
};
let adopted = fs::File::from(captured.into_owned_handle());
assert_eq!(adopted.metadata()?.len(), 7);§Example: what a duplicate shares
The distinction a caller reasoning in value semantics gets backwards. Closing the duplicate leaves the source perfectly usable, because both are references to one kernel object rather than two copies of it:
use std::fs;
use std::os::windows::io::AsHandle;
use windows_namespace_request_sys::CapturedHandle;
let path = std::env::temp_dir().join(format!("wnrs-dup2-{}.tmp", std::process::id()));
fs::write(&path, b"seven..")?;
let file = fs::File::open(&path)?;
let captured = CapturedHandle::capture(file.as_handle())?;
drop(captured);
// The source is untouched -- which is what makes it safe for a request to
// own a duplicate and drop it.
assert_eq!(file.metadata()?.len(), 7);Implementations§
Source§impl CapturedHandle
impl CapturedHandle
Sourcepub fn capture(source: BorrowedHandle<'_>) -> Result<Self, HandleCaptureError>
pub fn capture(source: BorrowedHandle<'_>) -> Result<Self, HandleCaptureError>
Captures source by duplicating it into this process.
§Errors
Returns a HandleCaptureError when source is null, is
INVALID_HANDLE_VALUE, is a Win32 pseudo-handle, or cannot be
duplicated – which is what an already-closed handle produces.
§Example
use std::fs;
use std::os::windows::io::{AsHandle, AsRawHandle};
use windows_namespace_request_sys::CapturedHandle;
let path = std::env::temp_dir().join(format!("wnrs-cap-{}.tmp", std::process::id()));
fs::write(&path, b"x")?;
let file = fs::File::open(&path)?;
let captured = CapturedHandle::capture(file.as_handle())?;
// A duplicate is a second reference, so it has its own handle value.
assert_ne!(captured.as_handle().as_raw_handle(), file.as_raw_handle());§Example: the failure that would otherwise be silent
INVALID_HANDLE_VALUE and the current-process pseudo-handle are the
same value, so an unchecked CreateFileW failure passed to
DuplicateHandle would succeed and yield a process handle. Capture
refuses it by name:
use windows_namespace_request_sys::handle::HandleCaptureFailure;
use windows_namespace_request_sys::CapturedHandle;
use windows_sys::Win32::Foundation::INVALID_HANDLE_VALUE;
// SAFETY: the value is validated, never dereferenced.
let error = unsafe { CapturedHandle::capture_raw(INVALID_HANDLE_VALUE) }
.expect_err("INVALID_HANDLE_VALUE is never a real handle");
assert_eq!(error.failure(), HandleCaptureFailure::InvalidHandleValue);Sourcepub unsafe fn capture_raw(source: RawHandle) -> Result<Self, HandleCaptureError>
pub unsafe fn capture_raw(source: RawHandle) -> Result<Self, HandleCaptureError>
Captures a raw handle by duplicating it into this process.
Prefer capture where an owned or borrowed handle is
available. This form exists for the common case of a raw HANDLE that
came straight back from a Win32 call and has no Rust owner yet.
§Errors
As capture.
§Safety
source must remain open for the duration of this call. A handle closed
concurrently may have had its value reused by another thread, in which
case this captures a different kernel object rather than failing.
Sourcepub fn try_clone(&self) -> Result<Self, HandleCaptureError>
pub fn try_clone(&self) -> Result<Self, HandleCaptureError>
Captures a second, independently owned duplicate.
This is not Clone because duplication is fallible. The result refers to
the same kernel object, with everything that implies above.
§Errors
As capture, though only
HandleCaptureFailure::DuplicateHandle is reachable: the value being
duplicated is already known to be a real, open handle.
§Example
use std::fs;
use std::os::windows::io::{AsHandle, AsRawHandle};
use windows_namespace_request_sys::CapturedHandle;
let path = std::env::temp_dir().join(format!("wnrs-clone-{}.tmp", std::process::id()));
fs::write(&path, b"x")?;
let file = fs::File::open(&path)?;
let first = CapturedHandle::capture(file.as_handle())?;
let second = first.try_clone()?;
// Two independently owned references to one kernel object, so closing
// one leaves the other usable.
assert_ne!(
first.as_handle().as_raw_handle(),
second.as_handle().as_raw_handle()
);
drop(second);
let adopted = fs::File::from(first.into_owned_handle());
assert_eq!(adopted.metadata()?.len(), 1);Sourcepub fn into_owned_handle(self) -> OwnedHandle
pub fn into_owned_handle(self) -> OwnedHandle
Releases the duplicate to the caller.
The handle stays open; ownership moves.