Safe/ergonomic wrapper for `winapi`

Updates

The original post

I'm porting my small python script that calls win32api into rust.
Are there any safe/ergonomic wrapper for winapi?

Here's a very little version of my own:

#[allow(dead_code)]
#[allow(non_snake_case)]
mod winwrap {
    use std::ffi::OsString;
    use std::os::windows::ffi::OsStringExt;
    pub use winapi::shared::minwindef::*;
    pub use winapi::shared::ntdef::*;
    pub use winapi::shared::windef::*;
    use winapi::um::processthreadsapi;
    use winapi::um::psapi;
    use winapi::um::winuser;

    const LEN: usize = 1024;

    fn slice_to_os_string_trancate_nul(text: &[u16]) -> OsString {
        if let Some(new_len) = text.iter().position(|x| *x == 0) {
            OsString::from_wide(&text[0..new_len])
        } else {
            OsString::from_wide(&text)
        }
    }

    ///
    pub fn GetForegroundWindow() -> HWND {
        unsafe { winuser::GetForegroundWindow() }
    }

    /// returns `OsString`, nul char is truncated.
    pub fn GetWindowTextW(hwnd: HWND) -> OsString {
        let text_len = unsafe { winuser::GetWindowTextLengthW(hwnd) };
        // println!("text_len: {}", text_len);

        let mut text = vec![0u16; (text_len + 1) as usize];
        unsafe {
            winuser::GetWindowTextW(hwnd, text.as_mut_ptr(), text_len + 1);
        }
        slice_to_os_string_trancate_nul(&text)
    }

    /// returns `(thread_id, proc_id)` 
    pub fn GetWindowThreadProcessId(hwnd: HWND) -> (DWORD, DWORD) {
        let mut proc_id: DWORD = 0;
        let thread_id = unsafe { winuser::GetWindowThreadProcessId(hwnd, &mut proc_id) };
        (thread_id, proc_id)
    }

    /// returns process handle.
    pub fn OpenProcess(dwDesiredAccess: DWORD, bInheritHandle: BOOL, dwProcessId: DWORD) -> HANDLE {
        unsafe { processthreadsapi::OpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId) }
    }

    /// returns `OsString`, nul char is truncated.
    pub fn GetProcessImageFileNameW(hProcess: HANDLE) -> OsString {
        let mut image_file_name = vec![0u16; LEN];
        let _ = unsafe {
            psapi::GetProcessImageFileNameW(
                hProcess,
                image_file_name.as_mut_ptr(),
                image_file_name.len() as u32,
            )
        };
        slice_to_os_string_trancate_nul(&image_file_name)
    }
}

Caller site looks like this:

    let hwnd = winwrap::GetForegroundWindow();
    println!("hwnd: {:?}", hwnd);

    let text = winwrap::GetWindowTextW(hwnd);
    println!("text: {:?}", text);

    let (thread_id, proc_id) = winwrap::GetWindowThreadProcessId(hwnd);
    println!("thread_id: {}", thread_id);
    println!("proc_id: {}", proc_id);

    let h_process = winwrap::OpenProcess(
        PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION,
        FALSE,
        proc_id,
    );
    println!("h_process: {:?}", h_process);

    let image_file_name = winwrap::GetProcessImageFileNameW(h_process);
    println!("image_file_name: {:?}", image_file_name);

Output:

hwnd: 0x809aa
text: "MINGW64:/c/workmx3/src/winlog"
thread_id: 9684
proc_id: 9680
h_process: 0x3c
image_file_name: "\\Device\\HarddiskVolume3\\Program Files\\Git\\usr\\bin\\mintty.exe"

Well, I have wrapper for clipboard https://github.com/DoumanAsh/clipboard-win
And some general stuff for my personal use https://github.com/DoumanAsh/windows-win-rs

1 Like

@DoumanAsh Seems nice.
Your crate is providing OO layer and error checks on top of winapi.

@sekineh Well the goal is to make it as easy as possible to use winapi from Rust.
I made multiple iterations until reached more or less stable API for both of these crates.
I also avoid bloating my winapi crates so they are purely dependent on winapi and io::Error to propagate winapi errors

Just a comment in passing on:

I think inevitably someone will mix up which is thread id vs proc id. Maybe put them in a struct with named fields (or newtype the two id types, if that makes more sense).

I agree that the order is source of confusion. But with custom struct, we can't do this:

let (_, proc_id) = GetWindowThreadProcessId(hwnd);

The doc comment will hover on codes and save us. Not all people are using IDE, though.

You can get close:

struct Ids {
    pid: DWORD,
    tid: DWORD,
}

let Ids {pid, ..} = GetWindowThreadProcessId(hwnd);

Yeah, it worked: