连接指定键盘,监听键盘输入

当系统中连接了多个键盘时,操作系统默认将所有键盘输入汇总到同一个输入流,无法区分按键来自哪个物理设备。本文介绍如何通过原生模块 + Electron + React 实现指定键盘的原始输入监听,精确区分不同键盘设备的按键事件。

背景

在某些工业或测试场景中,需要区分不同键盘的输入。例如:

  • 多操作员共用一台电脑,各自使用不同键盘,需要识别操作员身份
  • 测试键盘的按键响应和扫描码
  • 工业设备中,特定键盘触发特定功能

Windows 提供了 Raw Input API,可以获取每个 HID 设备的原始输入数据,从而区分不同键盘。

技术架构

1
2
3
4
5
原生模块 (raw-keyboard / N-API)
↓ 通过 IPC 暴露
Electron Preload (window.api.rawKeyboard)
↓ React 组件调用
RawKeyboardPage.tsx
  • 原生模块:通过 Windows Raw Input API 枚举键盘设备、注册原始输入、捕获 WM_INPUT 消息
  • Electron Preload:将原生模块能力通过 contextBridge 暴露为 window.api.rawKeyboard
  • React 前端:设备选择、监听控制、事件日志展示

原生模块接口

原生模块通过 Electron IPC 暴露以下方法:

方法 说明
check() 检查模块是否可用
listDevices() 枚举所有键盘设备
start() 开始监听原始输入
stop() 停止监听
onData(callback) 注册按键事件回调

设备数据结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
interface KeyboardDevice {
handle: number // 设备句柄,用于标识和选择设备
name: string // 设备名称
vid: number // 厂商 ID (Vendor ID)
pid: number // 产品 ID (Product ID)
}

interface KeyEvent {
handle: number // 产生事件的设备句柄
vKey: number // 虚拟键码
scanCode: number // 扫描码
keyDown: boolean // true=按下, false=释放
keyName: string // 键名
}

VID/PID 格式化显示:

1
2
3
const formatVidPid = (vid: number, pid: number) => {
return `VID_${vid.toString(16).toUpperCase().padStart(4, '0')} PID_${pid.toString(16).toUpperCase().padStart(4, '0')}`
}

React 组件实现

模块可用性检查

组件挂载时首先检查原生模块是否可用(仅 Windows 支持):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
useEffect(() => {
const check = async () => {
try {
const result = await window.api.rawKeyboard.check()
setModuleAvailable(result.available)
if (!result.available) {
setModuleError(result.error || '模块不可用')
}
} catch {
setModuleAvailable(false)
setModuleError('检查模块失败')
}
}
check()
}, [])

设备列表加载

模块可用后,自动加载键盘设备列表并默认选中第一个:

1
2
3
4
5
6
7
8
useEffect(() => {
const load = async () => {
const list = await window.api.rawKeyboard.listDevices()
setDevices(list)
if (list.length > 0) setSelectedHandle(list[0].handle)
}
if (moduleAvailable) load()
}, [moduleAvailable])

开始/停止监听

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const handleStart = useCallback(async () => {
await window.api.rawKeyboard.start()
setListening(true)

// 注册事件回调,保留 cleanup 函数
const cleanup = window.api.rawKeyboard.onData((evt: KeyEvent) => {
setEvents(prev => {
const next = [...prev, evt]
return next.length > MAX_EVENTS ? next.slice(-MAX_EVENTS) : next
})
})
cleanupRef.current = cleanup
}, [])

const handleStop = useCallback(async () => {
cleanupRef.current?.()
cleanupRef.current = null
await window.api.rawKeyboard.stop()
setListening(false)
}, [])

组件卸载时自动停止监听,防止资源泄漏:

1
2
3
4
5
6
7
8
useEffect(() => {
return () => {
if (listening) {
window.api.rawKeyboard.stop().catch(() => {})
}
cleanupRef.current?.()
}
}, [])

事件过滤

支持按选中设备过滤事件,只显示目标键盘的输入:

1
2
3
4
const isMatchedEvent = (evt: KeyEvent) => {
if (!filterSelected || selectedHandle === null) return true
return evt.handle === selectedHandle
}

界面设计

设备选择区

以卡片形式展示所有检测到的键盘设备,点击选中目标设备:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{devices.map((device) => (
<div
key={device.handle}
onClick={() => !listening && setSelectedHandle(device.handle)}
style={{
border: `2px solid ${device.handle === selectedHandle ? '#4fc3f7' : '#ddd'}`,
background: device.handle === selectedHandle ? '#e3f2fd' : '#fff',
}}
>
<div>{formatVidPid(device.vid, device.pid)}</div>
<div>Handle: {device.handle}</div>
<div>{device.name}</div>
</div>
))}

事件日志区

深色终端风格,按键按下(DOWN)显示绿色,释放(UP)显示蓝色,选中设备事件左侧高亮标记:

1
2
3
4
5
6
7
8
9
10
11
12
<div style={{
background: evt.keyDown ? '#1a3a1a' : '#1a1a2a',
color: evt.keyDown ? '#81c784' : '#90caf9',
borderLeft: `3px solid ${evt.handle === selectedHandle ? '#4fc3f7' : '#555'}`
}}>
<span>#{idx + 1}</span>
<span>h:{evt.handle}</span>
<span>{evt.keyName}</span>
<span>0x{evt.vKey.toString(16).toUpperCase().padStart(2, '0')}</span>
<span>sc:{evt.scanCode}</span>
<span>{evt.keyDown ? 'DOWN' : 'UP'}</span>
</div>

状态指示

实时显示监听状态、事件数量、设备数量,支持自动滚动和设备过滤开关。

注意事项

  1. 平台限制:Raw Input API 仅 Windows 可用,其他平台需使用不同方案
  2. 原生模块编译raw-keyboard 原生模块需要 VS Build Tools 编译
  3. 管理员权限:某些场景下可能需要管理员权限才能枚举 HID 设备
  4. 事件缓冲:组件保留最近 200 条事件,超出自动丢弃最早的记录
  5. 资源清理:组件卸载时必须调用 stop() 和回调 cleanup(),否则原生层会持续监听

总结

通过 Windows Raw Input API + Electron 原生模块,可以突破操作系统对键盘输入的统一抽象,实现精确到物理设备的按键监听。React 前端提供直观的设备选择和事件日志,使多键盘场景下的输入区分变得简单可行。


连接指定键盘,监听键盘输入
https://cszy.top/20260422-连接指定键盘监听键盘输入/
作者
csorz
发布于
2026年4月22日
许可协议