Skip to main content

CapturedHandle

Struct CapturedHandle 

Source
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

Source

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);
Source

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.

Source

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);
Source

pub fn into_owned_handle(self) -> OwnedHandle

Releases the duplicate to the caller.

The handle stays open; ownership moves.

Trait Implementations§

Source§

impl AsHandle for CapturedHandle

Source§

fn as_handle(&self) -> BorrowedHandle<'_>

Borrows the handle. Read more
Source§

impl Debug for CapturedHandle

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl From<CapturedHandle> for OwnedHandle

Source§

fn from(captured: CapturedHandle) -> Self

Converts to this type from the input type.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.