Search code examples
windows-rs

How to use HANDLE from windows-rs and rust


I'm trying to call LockFileEx on a std::fs:File and struggle since type HANDLE has multiple implementations:

windows::Win32::Foundation::HANDLE
std::os::windows::io::BorrowedHandle
std::os::windows::raw::HANDLE

My current attempt is as follows:

fn unsafe lock(file: &mut File) -> BOOL {
    let mut overlapped = std::mem::zeroed();
    LockFileEx(
        file.as_handle(),
        LOCKFILE_EXCLUSIVE_LOCK,
        0,
        !0,
        !0
        &mut overlapped)
}

This fails with

the trait `From<BorrowedHandler<'_>>` is not implemented for `HANDLE`

I'm kind of puzzled because of the different HANDLE types and was wondering how to make them fit together.


Solution

  • A HANDLE in Windows is a moniker that identifies a kernel object. Its native type is void* with its value carrying no inherent meaning. Its sole purpose is to refer that object in communication with the OS, and any library (such as Rust's Standard Library) that needs to interact with the OS will provide a way to represent HANDLE values.

    At the ABI, where rubber meets the road, all libraries (necessarily have to) agree on a specific data type (or at least a type that's binary layout-compatible), yet Rust's type system leaves a lot of headroom to encode circumstantial information:

    • std::os::windows::raw::HANDLE is the low-level, ABI-compatible type alias. It's literally just a *mut c_void, Rust's way of expressing C's void*.
    • std::os::windows::io::BorrowedHandle is a #[repr(transparent)] type over the aforementioned HANDLE type with lifetime annotations. It is valid for as long as the referenced File is.
      It just landed in Rust 1.63, so there's not a lot of real world evidence how this pans out.
    • windows::Win32::Foundation::HANDLE is very similar to std::od::windows::raw::HANDLE in that it transparently wraps a void*, but stores it as an isize instead.

    While it has been discussed whether windows's HANDLE should follow the route of lifetime-annotated BorrowedHandle's this has not been implemented (as of 0.39.0). At this time, the easiest solution would be to just pull out an as_raw_handle() and feed that into a HANDLE, e.g.:

    unsafe fn lock(file: &mut File) -> BOOL {
        let mut overlapped = Default::default();
        LockFileEx(
            HANDLE(file.as_raw_handle() as isize),
            LOCKFILE_EXCLUSIVE_LOCK,
            0,
            !0,
            !0,
            &mut overlapped,
        )
    }
    

    As for the specific error diagnostic, the function signature of LockFileEx is:

    pub unsafe fn LockFileEx<'a, P0>(
        hfile: P0,
        dwflags: LOCK_FILE_FLAGS,
        dwreserved: u32,
        nnumberofbytestolocklow: u32,
        nnumberofbytestolockhigh: u32,
        lpoverlapped: *mut OVERLAPPED
    ) -> BOOL where
        P0: Into<HANDLE>,
    

    rustc is attempting to invoke an Into (or From) trait implementation on the hfile argument, but cannot find one that matches. This is because it doesn't exist, not just because the caller failed to use the specific trait implementation.