SetWinEventHook未捕获Rust应用程序中的任何事件

brccelvz  于 2023-05-29  发布在  其他
关注(0)|答案(1)|浏览(443)

我正在尝试使用Rust来计算在特定窗口内花费的时间。
现在我被Windows钩子卡住了。
我尝试使用windows crate来设置钩子,但我无法使SetWindowsHookExW工作,因为它不允许传递null ptr作为hmod参数。SetWindowsHookW编译后总是返回0,就是这样。现在我切换到winapi机箱,使用SetWinEventHookExW返回类似0x00000000的东西,我认为这是不成功的操作。调用SetWinEventHook时,返回看似正常的值,但由于我不知道的原因,事件没有被捕获。以下是我的当前代码:

// dependencies imports
...

thread_local! {
    static TX: OnceCell<Sender<RawWindowEvent>>= OnceCell::new()
}

extern "system" fn win_event_hook_callback(
    child_id: HWINEVENTHOOK,
    hook_handle: DWORD,
    event_id: HWND,
    window_handle: LONG,
    object_id: LONG,
    thread_id: DWORD,
    timestamp: DWORD,
) -> () {
    println!("EVENT?");
    TX.with(|f| {
        let tx = f.get().unwrap();
        let event = RawWindowEvent {
            child_id,
            hook_handle,
            event_id,
            window_handle,
            object_id,
            thread_id,
            timestamp,
        };
        println!("{:#?}", &event);
        tx.send(event);
    });
}

fn main() {
    println!("Hello, world!");
    let (tx, rx) = channel::<RawWindowEvent>();

    match TX.with(|f| f.set(tx)) {
        Err(err) => panic!("{:#?}", err),
        _ => (),
    };

    unsafe {
        let hook = SetWinEventHook(
            0x0003,
            0x0003,
            ptr::null_mut(),
            Some(win_event_hook_callback),
            0,
            0,
            0,
        );
        println!("{:#?}", hook);

        loop {
            let event = rx.recv().unwrap();
            println!("{:#?}", event);
        }

        UnhookWinEvent(hook);
    }
}


#[derive(Debug)]
pub struct RawWindowEvent {
    pub child_id: HWINEVENTHOOK,
    pub hook_handle: DWORD,
    pub event_id: HWND,
    pub window_handle: LONG,
    pub object_id: LONG,
    pub thread_id: DWORD,
    pub timestamp: DWORD,
}
iszxjhcz

iszxjhcz1#

这里有几个问题,所以让我们快速覆盖那些实际上并不相关的问题:
我不能让SetWindowsHookExW工作,因为它不允许传递null ptr作为hmod参数。
windows crate中SetWindowsHookExW的签名需要一个为hmod参数实现IntoParam<HMODULE>的类型,这允许传递一个有效的HMODULE值或None(转换为空指针)。
现在我切换到winapi机箱
你不该这么做它不再积极维护,据我所知,不提供任何功能,也不能通过windows(或windows-sys)板条箱。
使用SetWinEventHookExW返回类似0x00000000的东西,我认为这是不成功的操作
我不知道SetWinEventHookExW是什么。我假设这是一个错字,应该读SetWindowsHookExW代替。如果是这种情况,则返回值零表示失败。
有了这些,WinEvents是最适合监视前台激活事件的系统服务,而windows是访问它最方便的Rust crate。无论您选择hooks还是WinEvents,您实际上总是必须在安装了钩子的线程上启动一个消息循环(调用GetMessage/DispatchMessage)以接收通知。
下面的实现(使用windows crate)设置了一个WinEvents钩子来接收EVENT_SYSTEM_FOREGROUND通知:

  • 主.rs*
use windows::{
    w,
    Win32::{
        Foundation::HWND,
        UI::{
            Accessibility::{SetWinEventHook, HWINEVENTHOOK},
            WindowsAndMessaging::{
                MessageBoxW, EVENT_SYSTEM_FOREGROUND, MB_OK, WINEVENT_OUTOFCONTEXT,
            },
        },
    },
};

fn main() {
    let hook = unsafe {
        SetWinEventHook(
            EVENT_SYSTEM_FOREGROUND,
            EVENT_SYSTEM_FOREGROUND,
            None,
            Some(win_event_hook_callback),
            0,
            0,
            WINEVENT_OUTOFCONTEXT,
        )
    };
    // Make sure the hook is installed; a real application would want to do more
    // elaborate error handling
    assert!(!hook.is_invalid(), "Failed to install hook");

    // Have the system spin up a message loop (and get a convenient way to exit
    // the application for free)
    let _ = unsafe {
        MessageBoxW(
            None,
            w!("Click OK to terminate"),
            w!("Event hook running"),
            MB_OK,
        )
    };
}

unsafe extern "system" fn win_event_hook_callback(
    _hook_handle: HWINEVENTHOOK,
    _event_id: u32,
    _window_handle: HWND,
    _object_id: i32,
    _child_id: i32,
    _thread_id: u32,
    _timestamp: u32,
) {
    println!("Event received.");
}
  • 货物.toml*
[package]
name = "win_event_hook"
version = "0.0.0"
edition = "2021"

[dependencies.windows]
version = "0.48.0"
features = [
    "Win32_Foundation",
    "Win32_UI_Accessibility",
    "Win32_UI_WindowsAndMessaging",
]

这是利用了这样一个事实,即只要MessageBoxW启动,系统就会在调用线程上分发消息。根据应用程序的需要,您可能希望实现自己的消息循环。这并不严格要求旋转额外的线程; MsgWaitForMultipleObjectsEx通过等待消息到达、内核对象收到信号或I/O完成例程/APC在调用线程上排队来解决这个问题。

相关问题