在Tauri应用中用Rust读取HID设备的问题求助
Tauri + hidapi 条码扫描器问题解决方案
解决事件丢失问题
- 等待UI就绪后再发送初始事件:通过监听
tauri://ready全局事件,确保UI完全初始化后再执行设备扫描和事件推送,避免UI未就绪导致事件丢失。 - 替代方案:让UI在初始化完成后主动调用Tauri命令查询设备状态,而非依赖启动时的主动推送。
维持设备连接与持续监听
- 用全局状态管理设备实例:通过Tauri的
manageAPI将HID设备和API实例存入全局状态(使用Arc<Mutex>保证线程安全),这样在setup之后的命令或异步任务中仍能访问设备。 - 异步任务独立监听数据:设备连接成功后,启动一个异步Tokio任务持续读取设备数据,持有App Handle克隆随时发送
device-data事件;设备断开时更新状态并发送断开事件,退出监听任务。
代码示例
全局状态定义与Setup初始化
use tauri::{App, AppHandle, Manager, Runtime}; use hidapi::{HidApi, HidDevice}; use std::sync::{Arc, Mutex}; // 全局应用状态,存储HID设备和API实例 struct AppState { hid_device: Mutex<Option<HidDevice>>, hid_api: Mutex<Option<HidApi>>, } fn setup<R: Runtime>(app: &mut App<R>) -> Result<(), Box<dyn std::error::Error>> { // 初始化HID API let api = HidApi::new()?; let state = AppState { hid_device: Mutex::new(None), hid_api: Mutex::new(Some(api)), }; // 将状态注入Tauri应用 app.manage(state); // 监听UI就绪事件,就绪后执行设备扫描 app.listen_global("tauri://ready", move |event| { let app_handle = event.app_handle(); detect_and_connect_scanner(app_handle.clone()); }); Ok(()) }
设备扫描与连接逻辑
fn detect_and_connect_scanner(app_handle: AppHandle) { let state = app_handle.state::<AppState>(); let mut api_guard = state.hid_api.lock().unwrap(); let api = api_guard.as_mut().unwrap(); // 替换为你的条码扫描器的VID和PID let target_vid = 0x1234; let target_pid = 0x5678; match api.open(target_vid, target_pid) { Ok(device) => { *state.hid_device.lock().unwrap() = Some(device); // 通知UI设备已连接 let _ = app_handle.emit_all("device-connected", ()); // 启动数据监听异步任务 tokio::spawn(listen_for_scanner_data(app_handle.clone())); } Err(_) => { // 通知UI未找到设备 let _ = app_handle.emit_all("device-disconnected", ()); } } }
设备数据监听任务
async fn listen_for_scanner_data(app_handle: AppHandle) { let state = app_handle.state::<AppState>(); // 缓冲区大小根据设备的HID报告大小调整 let mut buffer = [0u8; 64]; loop { let mut device_guard = state.hid_device.lock().unwrap(); match device_guard.as_mut() { Some(device) => { match device.read(&mut buffer) { Ok(bytes_read) if bytes_read > 0 => { // 转换扫描数据为字符串(根据设备协议调整处理逻辑) let scan_data = String::from_utf8_lossy(&buffer[..bytes_read]).trim().to_string(); // 发送扫描数据到UI let _ = app_handle.emit_all("device-data", scan_data); } Err(_) => { // 设备断开连接,更新状态并通知UI *device_guard = None; let _ = app_handle.emit_all("device-disconnected", ()); break; } _ => {} } } None => { // 设备已被释放,退出监听 break; } } // 短暂休眠降低CPU占用 tokio::time::sleep(tokio::time::Duration::from_millis(10)).await; } }
UI触发重新扫描的命令
#[tauri::command] fn rescan_scanner(app_handle: AppHandle) { detect_and_connect_scanner(app_handle); }
注意事项
- 依赖配置:在
Cargo.toml中添加hidapi = "2.4"和tokio = { version = "1.0", features = ["full"] }。 - 权限处理:Linux系统需添加udev规则授予HID设备访问权限;Windows可能需要对应驱动支持。
- 错误处理:实际代码中需补充更细致的错误捕获与处理,避免panic导致应用崩溃。
内容的提问来源于stack exchange,提问作者Jens Vanhulst
相关产品推荐
相关产品推荐

