跳转到主要内容

设备访问

与基于 Chromium 的浏览器一样,Electron 通过 Web API 提供对设备硬件的访问。 对于大多数情况,这些 API 的工作方式与在浏览器中相同,但需要考虑一些差异。 Electron 和浏览器之间的主要区别在于请求设备访问时会发生什么。 在浏览器中,用户会看到一个弹出窗口,他们可以在其中授予对单个设备的访问权限。 在 Electron 中,提供了可以由开发人员用来自动选择设备或通过开发人员创建的界面提示用户选择设备的 API。

Web Bluetooth API

可以使用 Web Bluetooth API 与蓝牙设备通信。 为了在 Electron 中使用此 API,开发人员需要处理与设备请求关联的 select-bluetooth-device 事件

此外,可以使用 ses.setBluetoothPairingHandler(handler) 在 Windows 或 Linux 上处理与蓝牙设备的配对,当需要额外的验证(例如 PIN 码)时。

示例

此示例演示了一个 Electron 应用程序,当单击“测试蓝牙”按钮时,会自动选择第一个可用的蓝牙设备。

const { app, BrowserWindow, ipcMain } = require('electron/main')
const path = require('node:path')

let bluetoothPinCallback
let selectBluetoothCallback

function createWindow () {
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})

mainWindow.webContents.on('select-bluetooth-device', (event, deviceList, callback) => {
event.preventDefault()
selectBluetoothCallback = callback
const result = deviceList.find((device) => {
return device.deviceName === 'test'
})
if (result) {
callback(result.deviceId)
} else {
// The device wasn't found so we need to either wait longer (eg until the
// device is turned on) or until the user cancels the request
}
})

ipcMain.on('cancel-bluetooth-request', (event) => {
selectBluetoothCallback('')
})

// Listen for a message from the renderer to get the response for the Bluetooth pairing.
ipcMain.on('bluetooth-pairing-response', (event, response) => {
bluetoothPinCallback(response)
})

mainWindow.webContents.session.setBluetoothPairingHandler((details, callback) => {
bluetoothPinCallback = callback
// Send a message to the renderer to prompt the user to confirm the pairing.
mainWindow.webContents.send('bluetooth-pairing-request', details)
})

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()
})

WebHID API

可以使用 WebHID API 访问 HID 设备,例如键盘和游戏手柄。 Electron 提供了几个用于使用 WebHID API 的 API

  • 可以使用 select-hid-device 事件 在 Session 上选择 HID 设备,当调用 navigator.hid.requestDevice 时。 此外,Session 上的 hid-device-addedhid-device-removed 事件可用于处理设备插入或拔出,处理 select-hid-device 事件时。 注意: 这些事件仅在 select-hid-device 的回调函数被调用之前触发。 它们不打算用作通用的 HID 设备监听器。
  • ses.setDevicePermissionHandler(handler) 可用于为设备提供默认权限,而无需先通过 navigator.hid.requestDevice 请求设备权限。 此外,Electron 的默认行为是在相应的 WebContents 的生命周期内存储授予的设备权限。 如果需要更长期的存储,开发人员可以存储授予的设备权限(例如,处理 select-hid-device 事件时),然后使用 setDevicePermissionHandler 从该存储中读取。
  • ses.setPermissionCheckHandler(handler) 可用于为特定来源禁用 HID 访问。

黑名单

默认情况下,Electron 使用 Chromium 使用的相同的 黑名单。 如果您希望覆盖此行为,可以通过设置 disable-hid-blocklist 标志来执行此操作。

app.commandLine.appendSwitch('disable-hid-blocklist')

示例

此示例演示了一个 Electron 应用程序,该应用程序通过 ses.setDevicePermissionHandler(handler) 和 Session 上的 select-hid-device 事件 自动选择 HID 设备,当单击“测试 WebHID”按钮时。

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()
})

Web Serial API

可以使用 Web Serial API 访问通过串口、USB 或蓝牙连接的串口设备。 为了在 Electron 中使用此 API,开发人员需要处理与串口请求关联的 Session 上的 select-serial-port 事件

有几个额外的 API 用于使用 Web Serial API

  • Session 上的 serial-port-addedserial-port-removed 事件可用于处理设备插入或拔出,处理 select-serial-port 事件时。 注意: 这些事件仅在 select-serial-port 的回调函数被调用之前触发。 它们不打算用作通用的串口监听器。
  • ses.setDevicePermissionHandler(handler) 可用于为设备提供默认权限,而无需先通过 navigator.serial.requestPort 请求设备权限。 此外,Electron 的默认行为是在相应的 WebContents 的生命周期内存储授予的设备权限。 如果需要更长期的存储,开发人员可以存储授予的设备权限(例如,处理 select-serial-port 事件时),然后使用 setDevicePermissionHandler 从该存储中读取。
  • ses.setPermissionCheckHandler(handler) 可用于为特定来源禁用串口访问。

黑名单

默认情况下,Electron 使用 Chromium 使用的相同的 黑名单。 如果您希望覆盖此行为,可以通过设置 disable-serial-blocklist 标志来执行此操作。

app.commandLine.appendSwitch('disable-serial-blocklist')

示例

此示例演示了一个 Electron 应用程序,该应用程序通过 ses.setDevicePermissionHandler(handler) 以及 Session 上的 select-serial-port 事件 自动选择串口设备,当单击“测试 Web Serial”按钮时,如果连接了 Arduino Uno 串口设备,则选择第一个可用的 Arduino Uno 串口设备。

const { app, BrowserWindow } = require('electron/main')

function createWindow () {
const mainWindow = new BrowserWindow({
width: 800,
height: 600
})

mainWindow.webContents.session.on('select-serial-port', (event, portList, webContents, callback) => {
// Add listeners to handle ports being added or removed before the callback for `select-serial-port`
// is called.
mainWindow.webContents.session.on('serial-port-added', (event, port) => {
console.log('serial-port-added FIRED WITH', port)
// Optionally update portList to add the new port
})

mainWindow.webContents.session.on('serial-port-removed', (event, port) => {
console.log('serial-port-removed FIRED WITH', port)
// Optionally update portList to remove the port
})

event.preventDefault()
if (portList && portList.length > 0) {
callback(portList[0].portId)
} else {
// eslint-disable-next-line n/no-callback-literal
callback('') // Could not find any matching devices
}
})

mainWindow.webContents.session.setPermissionCheckHandler((webContents, permission, requestingOrigin, details) => {
if (permission === 'serial' && details.securityOrigin === 'file:///') {
return true
}

return false
})

mainWindow.webContents.session.setDevicePermissionHandler((details) => {
if (details.deviceType === 'serial' && details.origin === 'file://') {
return true
}

return false
})

mainWindow.loadFile('index.html')

mainWindow.webContents.openDevTools()
}

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()
})

WebUSB API

可以使用 WebUSB API 访问 USB 设备。 Electron 提供了几个用于使用 WebUSB API 的 API

  • 可以使用 Session 上的 select-usb-device 事件 在调用 navigator.usb.requestDevice 时选择 USB 设备。 此外,Session 上的 usb-device-addedusb-device-removed 事件可用于处理设备插入或拔出,处理 select-usb-device 事件时。 注意: 这两个事件仅在 select-usb-device 的回调函数被调用之前触发。 它们不打算用作通用的 USB 设备监听器。
  • 可以使用 Session 上的 usb-device-revoked 事件 来响应在 USB 设备上调用 device.forget() 时的情况。
  • ses.setDevicePermissionHandler(handler) 可用于为设备提供默认权限,而无需先通过 navigator.usb.requestDevice 请求设备权限。 此外,Electron 的默认行为是在相应的 WebContents 的生命周期内存储授予的设备权限。 如果需要更长期的存储,开发人员可以存储授予的设备权限(例如,处理 select-usb-device 事件时),然后使用 setDevicePermissionHandler 从该存储中读取。
  • ses.setPermissionCheckHandler(handler) 可用于为特定来源禁用 USB 访问。
  • `ses.setUSBProtectedClassesHandler` 可用于允许使用 受保护的 USB 类,这些类默认情况下不可用。

黑名单

默认情况下,Electron 使用 Chromium 使用的相同的 黑名单。 如果您希望覆盖此行为,可以通过设置 disable-usb-blocklist 标志来执行此操作。

app.commandLine.appendSwitch('disable-usb-blocklist')

示例

此示例演示了一个 Electron 应用程序,该应用程序通过 ses.setDevicePermissionHandler(handler) 以及 Session 上的 select-usb-device 事件 自动选择 USB 设备(如果已连接),当单击“测试 WebUSB”按钮时。

const { app, BrowserWindow } = require('electron/main')

function createWindow () {
const mainWindow = new BrowserWindow({
width: 800,
height: 600
})

let grantedDeviceThroughPermHandler

mainWindow.webContents.session.on('select-usb-device', (event, details, callback) => {
// Add events to handle devices being added or removed before the callback on
// `select-usb-device` is called.
mainWindow.webContents.session.on('usb-device-added', (event, device) => {
console.log('usb-device-added FIRED WITH', device)
// Optionally update details.deviceList
})

mainWindow.webContents.session.on('usb-device-removed', (event, device) => {
console.log('usb-device-removed FIRED WITH', device)
// Optionally update details.deviceList
})

event.preventDefault()
if (details.deviceList && details.deviceList.length > 0) {
const deviceToReturn = details.deviceList.find((device) => {
return !grantedDeviceThroughPermHandler || (device.deviceId !== grantedDeviceThroughPermHandler.deviceId)
})
if (deviceToReturn) {
callback(deviceToReturn.deviceId)
} else {
callback()
}
}
})

mainWindow.webContents.session.setPermissionCheckHandler((webContents, permission, requestingOrigin, details) => {
if (permission === 'usb' && details.securityOrigin === 'file:///') {
return true
}
})

mainWindow.webContents.session.setDevicePermissionHandler((details) => {
if (details.deviceType === 'usb' && details.origin === 'file://') {
if (!grantedDeviceThroughPermHandler) {
grantedDeviceThroughPermHandler = details.device
return true
} else {
return false
}
}
})

mainWindow.webContents.session.setUSBProtectedClassesHandler((details) => {
return details.protectedClasses.filter((usbClass) => {
// Exclude classes except for audio classes
return usbClass.indexOf('audio') === -1
})
})

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()
})