ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Electron 中 HIDDevice 对象全解:WebHID 设备选择事件里的数据结构与实战用法

Electron 中 HIDDevice 对象全解:WebHID 设备选择事件里的数据结构与实战用法 Electron 中 HIDDevice 对象全解WebHID 设备选择事件里的数据结构与实战用法【免费下载链接】electron:electron: Build cross-platform desktop apps with JavaScript, HTML, and CSS项目地址: https://gitcode.com/GitHub_Trending/el/electron本文以 Electron 官方结构文档HIDDevice为骨架系统讲解该对象每个字段的含义与取值背景并结合 Electron 源码shell/browser/hid下的选择器控制器与 gin 转换器说明deviceId的生成规则、对象在Session各类 HID 事件中的流转路径最后给出可运行的 WebHID 设备自动选择示例帮助你在 Electron 主进程中正确识别、筛选并持久化 HID 设备权限。一、HIDDevice 是什么HIDDevice是 Electron 暴露给主进程Node 环境的数据结构用于描述一个 HIDHuman Interface Device人机接口设备如键盘、游戏手柄、自定义 USB 控制器。它是 WebHID API 的配套对象当渲染进程调用navigator.hid.requestDevice时Electron 会拦截该请求并触发Session的select-hid-device事件事件参数中携带的deviceList数组元素就是HIDDevice对象此外hid-device-added、hid-device-removed、hid-device-revoked事件以及setDevicePermissionHandler的回调参数中也会出现它。官方字段定义见 HIDDevice Object完整字段如下表字段类型是否可选说明deviceIdstring否设备的唯一标识符namestring否设备名称vendorIdInteger否USB 厂商 IDproductIdInteger否USB 产品 IDserialNumberstring是USB 设备序列号部分设备不暴露guidstring是HID 接口级唯一标识符同一物理设备可能有多个 HID 接口collectionsObject[]否report format 数组即设备的集合collection描述collections是一个递归结构每个元素包含usageInteger该集合关联 HID usage 的 usage ID 分量usagePageIntegerusage page 分量如0x01通用桌面、0x0C消费控制等typeInteger8 位集合类型值描述分组内条目之间的关系如物理、应用、逻辑等childrenObject[]子集合数组格式与顶层集合相同可无限递归inputReports/outputReports/featureReportsObject[]该集合下描述的输入、输出、特性 report 列表。这套 collection 结构正是 WebHID 规范中HIDDevice.collections的镜像详见 MDN 对HIDDevice/collections的描述Electron 主进程拿到的与渲染进程navigator.hid.getDevices()返回的HIDDevice在结构上保持一致方便两边互相比对设备。二、deviceId与guid的区别从源码看唯一标识的生成规则文档中deviceId被描述为“设备的唯一标识符”而guid被描述为“HID 接口”的标识。两者粒度不同这一点在源码中有明确印证。在 hid_chooser_controller.cc 中const std::string HidChooserController::PhysicalDeviceIdFromDeviceInfo( const device::mojom::HidDeviceInfo device) { // A single physical device may expose multiple HID interfaces, each // represented by a HidDeviceInfo object. When a device exposes multiple // HID interfaces, the HidDeviceInfo objects will share a common // |physical_device_id|. Group these devices so that a single chooser item // is shown for each physical device. If a devices physical device ID is // empty, use its GUID instead. return device.physical_device_id.empty() ? device.guid : device.physical_device_id; }可以确认一个物理设备可能暴露多个 HID 接口每个接口对应一条HidDeviceInfo记录deviceId使用的是physical_device_id多个接口共享为空时才回退到guid。也就是说deviceId是“物理设备级”标识guid是“接口级”标识。该deviceId如何进入 JS 层的HIDDevice对象由 hid_device_info_converter.h 中的 gin 转换器完成dict.Set(deviceId, electron::HidChooserController::PhysicalDeviceIdFromDeviceInfo(*device));即device的 mojom 信息先被转成base::DictValue再注入deviceId字段后转换到 V8。这意味着你在select-hid-device事件里拿到的device.deviceId就是回调给 Chromium 层时用于定位整组接口的标识在OnDeviceChosen中hid_chooser_controller.ccElectron 用你传入的deviceId查device_map_命中的所有接口都会被GrantDevicePermission授权后一并返回给渲染进程若传入的deviceId不存在则触发UnknownHIDDeviceId警告并取消请求。三、select-hid-device事件HIDDevice 列表的入口HIDDevice最核心的消费场景是 Session 的select-hid-device事件其参数为details.deviceListHIDDevice[]数组details.frame发起请求的WebFrameMain可能为nullcallback(deviceId?)传入选中的deviceId完成选择不传参调用则取消本次请求。官方文档给出的标准用法来自 session.mdconst { app, BrowserWindow } require(electron) let win null app.whenReady().then(() { win new BrowserWindow() win.webContents.session.setPermissionCheckHandler((webContents, permission, requestingOrigin, details) { if (permission hid) { // Add logic here to determine if permission should be given to allow HID selection return true } return false }) // Optionally, retrieve previously persisted devices from a persistent store const grantedDevices fetchGrantedDevices() win.webContents.session.setDevicePermissionHandler((details) { if (new URL(details.origin).hostname some-host details.deviceType hid) { if (details.device.vendorId 123 details.device.productId 345) { // Always allow this type of device (this allows skipping the call to navigator.hid.requestDevice first) return true } // Search through the list of devices that have previously been granted permission return grantedDevices.some((grantedDevice) { return grantedDevice.vendorId details.device.vendorId grantedDevice.productId details.device.productId grantedDevice.serialNumber grantedDevice.serialNumber details.device.serialNumber }) } return false }) win.webContents.session.on(select-hid-device, (event, details, callback) { event.preventDefault() const selectedDevice details.deviceList.find((device) { return device.vendorId 9025 device.productId 67 }) callback(selectedDevice?.deviceId) }) })注意几个要点event.preventDefault()是自定义选择流程的前提。从源码 hid_chooser_controller.cc 可以看到OnGotDevices在Emit(select-hid-device, ...)返回prevent_default为假时会直接RunCallback({})结束本次请求——即你必须调用preventDefault()并在后续手动调用callback(deviceId)否则请求会以空结果终止。回调传null或空字符串等于取消OnDeviceChosen中!args-GetNext(device_id) || device_id.empty()会直接以空设备列表返回。设备列表在枚举阶段就已被过滤DisplayDevice下文第四节会解释为什么某些设备不会出现在deviceList里。热插拔辅助事件处理选择 UI 期间若用户插拔设备Electron 会额外发出两个事件参数中的device同为HIDDevicehid-device-addedselect-hid-device触发后、回调调用前有新设备接入时发出用于刷新选择 UIhid-device-removed同理设备被拔出时发出。注意这两个事件只在select-hid-device的回调被调用之前有效不能当作通用 HID 监听器使用见 devices 教程 的说明。源码层面OnDeviceAdded/OnDeviceRemovedhid_chooser_controller.cc确实只在控制器存活期间即选择流程未结束时向 Session 发出这些事件与文档描述一致。另外还有一个权限侧事件hid-device-revoked渲染进程调用HIDDevice.forget()后触发参数同样携带HIDDevice及被吊销的origin适合配合setDevicePermissionHandler的持久化授权存储做同步清理。四、deviceList为什么可能“缺设备”Blocklist 与 FIDO 过滤文档字段之外理解HIDDevice列表的实际边界同样重要。默认情况下 Electron 沿用 Chromium 的 WebHID blocklist从 DisplayDevice 的实现可以确认三层过滤逻辑FIDO安全密钥设备若设备顶层集合中存在 FIDO usage page默认不展示除非设置了disable-hid-blocklist开关或 origin 被认定为特权来源blocklist 命中设备is_excluded_by_blocklist为真时直接排除保护报告全被剥离的设备模拟HidService::FinishRequestDevice的递归集合过滤若设备的所有 report 都位于受保护集合中过滤后collections为空也会从列表剔除。被排除时 Electron 会向发起请求的页面 console 写入 Info 级日志含 vendorId/productId/name/serial便于排查“设备为什么没出现”。如需放开 blocklist 限制按 devices 教程 的说明在应用启动前追加命令行开关即可app.commandLine.appendSwitch(disable-hid-blocklist)此外渲染进程requestDevice的 filtersvendorId / productId / usage / usagePage会在FilterMatchhid_chooser_controller.cc中参与匹配filter 可单独按 vendor、按 vendorproduct或按 usage page、usagepage 匹配collections。注意deviceId标识的物理设备分组是选择粒度而 filter 匹配发生在接口级HidDeviceInfo上。五、完整实战示例自动选择 HID 设备仓库内置的 WebHID fiddlemain.js 与 index.html演示了用HIDDevice打通权限与选择的完整链路核心代码如下const { app, BrowserWindow } require(electron/main) function createWindow () { const mainWindow new BrowserWindow({ width: 800, height: 600 }) mainWindow.webContents.session.on(select-hid-device, (event, details, callback) { // Add events to handle devices being added or removed before the callback on // select-hid-device is called. mainWindow.webContents.session.on(hid-device-added, (event, device) { console.log(hid-device-added FIRED WITH, device) // Optionally update details.deviceList }) mainWindow.webContents.session.on(hid-device-removed, (event, device) { console.log(hid-device-removed FIRED WITH, device) // Optionally update details.deviceList }) event.preventDefault() if (details.deviceList details.deviceList.length 0) { callback(details.deviceList[0].deviceId) } }) mainWindow.webContents.session.setPermissionCheckHandler((webContents, permission, requestingOrigin, details) { if (permission hid details.securityOrigin file:///) { return true } }) mainWindow.webContents.session.setDevicePermissionHandler((details) { if (details.deviceType hid details.origin file://) { return true } }) mainWindow.loadFile(index.html) } app.whenReady().then(() { createWindow() app.on(activate, function () { if (BrowserWindow.getAllWindows().length 0) createWindow() }) }) app.on(window-all-closed, function () { if (process.platform ! darwin) app.quit() })对应页面 index.html 只是一个点击按钮触发navigator.hid.requestDevice的极简页面并声明了 CSP。示例展示了HIDDevice的两种消费方式通过setDevicePermissionHandler对指定 originfile://的hid类型设备默认放行从而跳过requestDevice弹窗流程注意该 handler 的details.device字段就是HIDDevice或SerialPort/USBDevice取决于deviceType见 session.md 中 setDevicePermissionHandler 说明通过select-hid-device事件取deviceList[0].deviceId完成自动选择并在选择窗口期内订阅hid-device-added/hid-device-removed更新候选列表。权限持久化建议Electron 默认的授权存储只覆盖对应WebContents的生命周期。若需要跨会话记住设备常见做法是处理select-hid-device时把vendorId/productId/serialNumber三元组写入自己的存储如electron-store再在setDevicePermissionHandler中比对放行当渲染进程调用HIDDevice.forget()触发hid-device-revoked时从存储中删除对应记录。比对时注意serialNumber是可选字段——文档示例中先判断grantedDevice.serialNumber存在才比较避免误放行。六、小结与相关路径HIDDevice字段定义docs/api/structures/hid-device.mdselect-hid-device/hid-device-added/hid-device-removed/hid-device-revoked事件docs/api/session.mdWebHID 使用指南与 blocklist 开关说明docs/tutorial/devices.md可运行示例docs/fiddles/features/web-hid/main.js、docs/fiddles/features/web-hid/index.html主进程侧实现shell/browser/hid/hid_chooser_controller.cc、shell/common/gin_converters/hid_device_info_converter.h核心要点回顾deviceId是物理设备级标识可能聚合多个 HID 接口guid是接口级标识collections的usage/usagePage可用于精细筛选处理select-hid-device时必须preventDefault()再手动callback(deviceId)传空即取消默认存在 FIDO 与 blocklist 过滤被过滤设备会以 console Info 日志提示原因必要时用disable-hid-blocklist开关调整。【免费下载链接】electron:electron: Build cross-platform desktop apps with JavaScript, HTML, and CSS项目地址: https://gitcode.com/GitHub_Trending/el/electron创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表