10个版本
0.6.3 | 2023年12月1日 |
---|---|
0.6.2 | 2023年11月25日 |
0.6.1 | 2023年2月13日 |
0.5.0 | 2023年2月12日 |
0.2.3 | 2023年2月5日 |
#24 in Windows API
每月47次下载
60KB
990 行
捕获所有键盘和鼠标输入,无论在活动窗口桌面上应用程序是否具有焦点
此包提供的内容
此仅限Windows的包提供了一种安全、正确的方法来监听键盘和鼠标事件,无论应用程序是否具有焦点。应用程序可以是命令行界面或带有窗口的。
在底层,此包利用Windows低级钩子。您可以在MSDN上了解更多关于这个主题的信息。[链接](https://learn.microsoft.com/en-us/windows/win32/winmsg/about-hooks?redirectedfrom=MSDN)。此包主要是为了学习和我的个人项目而创建的,但我们将看看它将走向何方。
此包的设计目标是:正确、不易误用和不易出错。考虑到这一点,实现尽可能地避免任何恐慌。在最坏的情况下,它应该只是返回不完整的事件(例如,缺少键盘键码)。
此包不提供的内容
此包旨在为钩子提供“只读”访问。它不支持注入输入事件或修改它们。如果您正在寻找此类功能,可以尝试mki。与mki包相比,它也支持Linux,但不会清理低级钩子及其线程(通过取消钩子)以及它们(通过与它们连接)。这可能不会成为您的问题。向[willhook]添加“注入”和“修改”输入事件的可能性是存在的,尽管它不是首要任务。
警告:当前状态
目前它支持监听鼠标和键盘操作,详细信息请参阅[事件]模块。没有复杂的逻辑来解释事件 - 使用这个crate,您只需接收它们并对这些信息做您想做的事情。在这方面,我认为它功能完善。有一些集成测试应该覆盖所有现实场景。还有一些单元测试覆盖了一些不太现实的案例,例如Windows OS发送无效输入的情况。我认为这个crate的测试相当充分,但请注意,这个crate也是“年轻的”。注意:集成测试注入鼠标和键盘事件,而且它们需要按顺序运行(没有多线程)。有一些测试在GitHub Actions上无法通过,已被忽略。考虑到这一点,请使用以下命令运行测试:cargo test --tests -- --test-threads=1 --include-ignored
。 强烈建议在使用此crate进行任何非业余项目之前,至少快速审查一下代码,至少在当前状态下是这样。
待办事项
- 在我忘记所有怪癖之前,先记录不安全代码 :-)
- 可能编写更多单元测试
- 可能改进crate的模块化,而不会破坏API
- 可能重新设计底层通道,以便它们在钩子中被丢弃(现在它们只是被排空)
- 可能添加事件注入
- 可能添加阻塞事件,如果可能的话
- 可能添加操纵事件,如果可能的话
它是如何工作的
简而言之,有几个方便的函数可以请求钩子:[keyboard_hook]、[mouse_hook]和[wilhook]。当它们被调用时
- 为每个低级钩子启动后台线程,在这些线程中
- 注册鼠标和/或键盘低级钩子
- 启动Windows消息队列并等待消息结束执行
- 创建(如果尚未创建),用于将事件传递给“客户端”线程的通道
- 返回底层低级钩子的句柄作为hook::Hook
当hook::Hook超出作用域时,支持低级钩子的底层资源将被丢弃
- 每个底层低级钩子都将从Windows内核中取消挂钩
- 每个后台线程都将适当地加入
- 所有挂起的事件都将被丢弃(后台通道将被排空)
当hook::Hook处于活动状态(在作用域内/未被丢弃)时,可以通过hook::Hook::try_recv接收记录的event::InputEvent。它的工作方式类似于std::sync::mpsc::Receiver::try_recv。
快速示例
use willhook::willhook;
use std::sync::{Arc, atomic::{Ordering, AtomicBool}};
fn main() {
let is_running = Arc::new(AtomicBool::new(true));
let set_running = is_running.clone();
let h = willhook().unwrap();
ctrlc::set_handler(move || {
set_running.store(false, Ordering::SeqCst);
})
.expect("Error setting Ctrl-C handler");
while is_running.load(Ordering::SeqCst) {
if let Ok(ie) = h.try_recv() {
match ie {
willhook::InputEvent::Keyboard(ke) => println!("{:?}", ke),
willhook::InputEvent::Mouse(me) => println!("{:?}", me),
_ => println!("Input event: {:?}", ie),
}
} else {
std::thread::yield_now();
}
};
}
示例输出
PS ~ cargo run --example showcase
Finished dev [unoptimized + debuginfo] target(s) in 0.03s
Running `target\debug\examples\showcase.exe`
KeyboardEvent { pressed: Down(Normal), key: Some(A), is_injected: Some(NotInjected) }
KeyboardEvent { pressed: Up(Normal), key: Some(A), is_injected: Some(NotInjected) }
KeyboardEvent { pressed: Down(Normal), key: Some(Q), is_injected: Some(NotInjected) }
KeyboardEvent { pressed: Up(Normal), key: Some(Q), is_injected: Some(NotInjected) }
KeyboardEvent { pressed: Down(System), key: Some(LeftAlt), is_injected: Some(NotInjected) }
KeyboardEvent { pressed: Down(System), key: Some(A), is_injected: Some(NotInjected) }
KeyboardEvent { pressed: Up(System), key: Some(A), is_injected: Some(NotInjected) }
MouseEvent { event: Press(MousePressEvent { pressed: Down, button: Left(SingleClick) }), is_injected: Some(NotInjected) }
MouseEvent { event: Press(MousePressEvent { pressed: Up, button: Left(SingleClick) }), is_injected: Some(NotInjected) }
MouseEvent { event: Move(MouseMoveEvent { point: Some(Point { x: 1010, y: 1188 }) }), is_injected: Some(NotInjected) }
MouseEvent { event: Move(MouseMoveEvent { point: Some(Point { x: 1013, y: 1188 }) }), is_injected: Some(NotInjected) }
MouseEvent { event: Wheel(MouseWheelEvent { wheel: Vertical, direction: Some(Backward) }), is_injected: Some(NotInjected) }
MouseEvent { event: Wheel(MouseWheelEvent { wheel: Vertical, direction: Some(Forward) }), is_injected: Some(NotInjected) }
MouseEvent { event: Move(MouseMoveEvent { point: Some(Point { x: 1068, y: 1189 }) }), is_injected: Some(NotInjected) }
MouseEvent { event: Move(MouseMoveEvent { point: Some(Point { x: 1067, y: 1189 }) }), is_injected: Some(NotInjected) }
MouseEvent { event: Press(MousePressEvent { pressed: Down, button: Middle(SingleClick) }), is_injected: Some(NotInjected) }
依赖关系
~26–400KB