8176135
8176135

Reputation: 4133

How to use SetWindowsHookEx in Rust?

I am trying to figure out how to set global Windows hooks inside of Rust. I can find multiple examples for other languages, but there doesn't seem to be anything for Rust.

What I managed to get so far:

extern crate user32;
extern crate winapi;

const WH_KEYBOARD_LL: i32 = 13;
fn main() {
    let hook_id = user32::SetWindowsHookExA(
        WH_KEYBOARD_LL,
        Some(hook_callback),
        // No idea what goes here ,
        0,
    );
}

fn hook_callback(code: i32, wParam: u64, lParam: i64) -> i64 {
    // ...
}

The compiler complains that it needs a "system" fn for the callback function, but is getting a Rust fn, which makes sense, but I still don't know how to make that work.

From what I gathered from the documentation, the third parameter hMod should point to the same module that has the callback function, and the examples in other languages uses some function that gets the current module handle, but I don't know how to do that in Rust.

Upvotes: 2

Views: 2955

Answers (1)

Simon Whitehead
Simon Whitehead

Reputation: 65069

The compiler complains that it needs a "system" fn for the callback function, but is getting a Rust fn, which makes sense, but I still don't know how to make that work.

The compiler actually gives you exactly what you need ... if you continue reading the error you'll see:

expected type `unsafe extern "system" fn(i32, u64, i64) -> i64`
   found type `fn(i32, u64, i64) -> i64 {hook_callback}`

Adding that, gives:

extern "system" fn hook_callback(code: i32, wParam: u64, lParam: i64) -> i64 {
    0
}

From what I gathered from the documentation, the third parameter hMod should point to the same module that has the callback function, and the examples in other languages uses some function that gets the current module handle, but I don't know how to do that in Rust.

Again, reading further into the WinAPI documentation shows that NULL should be the value of this parameter if the thread ID (the last argument) specifies that its within the same process. Since you've passed zero - which the documentation states is associated with all threads in the current process ... that's what it should be... NULL. So now we get:

let hook_id =
    user32::SetWindowsHookExA(WH_KEYBOARD_LL, Some(hook_callback), std::ptr::null_mut(), 0);

This compiles.

Accounting for the other errors around unsafe that you'll get ... this gives you (full working code):

extern crate user32;
extern crate winapi;

const WH_KEYBOARD_LL: i32 = 13;

fn main() {
    unsafe {
        let hook_id =
            user32::SetWindowsHookExA(WH_KEYBOARD_LL, Some(hook_callback), std::ptr::null_mut(), 0);

        // Don't forget to release the hook eventually
        user32::UnhookWindowsHookEx(hook_id);
    }
}

extern "system" fn hook_callback(code: i32, wParam: u64, lParam: i64) -> i64 {
    0
}

Upvotes: 9

Related Questions