You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Tauri应用中用Rust读取HID设备的问题求助

Tauri + hidapi 条码扫描器问题解决方案

解决事件丢失问题

  • 等待UI就绪后再发送初始事件:通过监听tauri://ready全局事件,确保UI完全初始化后再执行设备扫描和事件推送,避免UI未就绪导致事件丢失。
  • 替代方案:让UI在初始化完成后主动调用Tauri命令查询设备状态,而非依赖启动时的主动推送。

维持设备连接与持续监听

  • 用全局状态管理设备实例:通过Tauri的manage API将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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 15:45:12