键盘和鼠标类驱动程序的配置

注意

本文面向正在配置键盘和鼠标类驱动程序的开发人员。 如果要修复鼠标或键盘,请参阅:

非 HID 键盘和鼠标可以通过多个旧总线进行连接,但仍使用相同的类驱动程序。 本部分包含类驱动程序本身的详细信息。 以下部分将详细介绍控制器。

本文介绍 Microsoft Windows 2000 及更高版本中键盘和鼠标设备的典型物理配置。

下图显示了采用单个键盘和单个鼠标的两种常见配置。

说明使用单个键盘和单个鼠标的两种配置的关系图。

该图显示通过独立控制器连接到系统总线的键盘和鼠标。 典型的配置包括通过 i8042 控制器操作的 POWERSHELL/2 样式键盘,以及通过串行端口控制器操作的串行样式鼠标。

对于键盘和鼠标制造商来说,以下附加信息非常重要:

  • 出于安全原因,操作系统以独占模式打开键盘
  • Windows 支持同时连接多个键盘和鼠标设备。
  • Windows 不支持客户端对每个设备的独立访问。

类驱动程序功能

本文介绍以下Microsoft Windows 2000 及更高版本的系统类驱动程序的功能:

  • Kbdclass,GUID_CLASS_KEYBOARD设备类设备的类驱动程序

  • Mouclass,GUID_CLASS_MOUSE设备类设备的类驱动程序

Kbdclass 实现 Kbdclass 服务,其可执行映像kbdclass.sys。

Mouclass 实现 Mouclass 服务及其可执行映像mouclass.sys。

Kbdclass 和 Mouclass 每个功能:

设备对象的配置

下图显示了即插即用 PS/2 样式键盘和鼠标设备的设备对象的配置。 每个类驱动程序都通过可选的上层设备筛选器 DO 创建附加到函数设备对象(FDO)的上层类筛选器设备对象filter DO)。 高级设备筛选器驱动程序创建上层设备筛选器 DO。 I8042prt 创建函数 DO 并将其附加到根总线驱动程序创建的物理设备对象(PDO)。

说明即插即用 ps/2 样式键盘和鼠标设备的设备对象的配置的关系图。

PS/2 键盘

键盘驱动程序堆栈由以下各项组成。

  • Kbdclass,高级键盘类筛选器驱动程序
  • 一个或多个可选的上层键盘筛选器驱动程序
  • 函数驱动程序 I8042prt

PS/2 鼠标

鼠标驱动程序堆栈由以下各项组成。

  • Mouclass,上级鼠标类筛选器驱动程序
  • 一个或多个可选的上层鼠标筛选器驱动程序
  • 函数驱动程序 I8042prt

Kbdclass 和 Mouclass 可以在两种不同的模式下支持多个设备。 在 一对一模式下,每个设备都有独立的设备堆栈。 类驱动程序创建独立类 DO 并将其附加到每个设备堆栈。 每个设备堆栈都有自己的控制状态和输入缓冲区。 Microsoft Win32 子系统通过唯一的文件对象访问每个设备的输入。

大师模式下,类驱动程序以以下方式运行所有设备:

  • 类驱动程序为每个设备创建一个表示所有设备和从属类 DO 的 Grandmaster 类 DO

    类驱动程序将从属类 DO 附加到每个设备堆栈。 在从属类 DO 下,设备堆栈与在一对一模式下创建的堆栈相同。

  • Grandmaster 类 DO 控制所有从属 DO 的操作。

  • Win32 子系统通过表示大师类设备的文件对象访问所有设备输入。

  • 所有设备输入都缓冲在大师的数据队列中。

  • 大师维护单个全局设备状态。

如果将 Kbdclass 和 Mouclass 的注册表项值 ConnectMultiplePorts 设置为0x00(在键 HKLM\Services\CurrentControlSet\class service>\<Parameters 下,类服务为 Kbdclass 或 Mouclass),则 Kbdclass 和 Mouclass 在一对一模式下运行。 否则,Kbdclass 和 Mouclass 在大师模式下运行。

通过类驱动程序打开和关闭

Microsoft Win32 子系统将打开所有键盘和鼠标设备供其独占使用。 对于每个设备类,Win32 子系统将来自所有设备的输入视为输入来自单个输入设备。 应用程序无法请求仅从一个特定设备接收输入。

Win32 子系统从即插即用管理器收到启用GUID_CLASS_KEYBOARD或GUID_CLASS_MOUSE设备接口的通知后,动态打开即插即用输入设备。 Win32 子系统在收到已打开的接口被禁用的通知后关闭即插即用设备。 Win32 子系统还按名称(例如“\Device\KeyboardLegacyClass0”)打开旧版设备。 Win32 子系统成功打开旧版设备后,无法确定设备是否在物理上被删除。

Kbdclass 和 Mouclass 收到创建请求后,针对即插即用和旧操作执行以下操作:

  • 即插即用操作

    如果设备处于即插即用启动状态,则类驱动程序会将IRP_MJ_CREATE请求向下发送驱动程序堆栈。 否则,类驱动程序无需将请求发送到驱动程序堆栈即可完成请求。 类驱动程序设置对设备的读取访问权限的受信任文件。 如果存在 Grandmaster 设备,则类驱动程序会将创建请求发送到与从属类设备关联的所有端口。

  • 旧操作

    类驱动程序将内部设备控制请求发送到端口驱动程序以启用设备。

将服务回调连接到设备

类驱动程序必须先将其类服务连接到设备,然后才能打开设备。 类驱动程序将类 DO 附加到设备堆栈后连接其类服务。 函数驱动程序使用类服务回调将输入数据从设备传输到设备的类数据队列。 设备的函数驱动程序 ISR 调度完成例程调用类服务回调。 Kbdclass 提供类服务回调 KeyboardClassServiceCallback,而 Mouclass 提供类服务回调 MouseClassServiceCallback

供应商可以通过为设备安装高级筛选器驱动程序来修改类服务回调的操作。 示例键盘筛选器驱动程序 Kbfiltr 定义 KbFilter_ServiceCallback 回调,示例鼠标筛选器驱动程序 Moufiltr 定义 MouFilter_ServiceCallback 回调。 可以将示例筛选器服务回调配置为修改从设备端口输入缓冲区传输到类数据队列的输入数据。 例如,筛选器服务回调可以删除、转换或插入数据。

类和筛选器服务回调按以下方式进行连接:

  • 类驱动程序在设备堆栈(IOCTL_INTERNAL_KEYBOARD_CONNECTIOCTL_INTERNAL_MOUSE_CONNECT)下发送内部设备连接请求。 类连接数据是由一个CONNECT_DATA结构指定的,该结构包括指向类设备对象的指针,以及指向类服务回调的指针。

  • 筛选器驱动程序收到连接请求后,它会保存类连接数据的副本,并将请求的连接数据替换为筛选器连接数据。 筛选器连接数据指定指向筛选器设备对象的指针,以及指向筛选器驱动程序服务回调的指针。 然后,筛选器驱动程序将筛选的连接请求发送到函数驱动程序。

类和筛选器服务回调按以下方式调用:

  • 函数驱动程序使用筛选器连接数据对筛选器服务回调进行初始回调。

  • 筛选输入数据后,筛选器服务回调使用它保存的类连接数据来回调类服务回调。

查询并设置键盘设备

I8042prt 支持以下内部设备控制请求来查询有关键盘设备的信息,并在键盘设备上设置参数:

IOCTL_KEYBOARD_QUERY_ATTRIBUTES

IOCTL_KEYBOARD_QUERY_INDICATOR_TRANSLATION

IOCTL_KEYBOARD_QUERY_INDICATORS

IOCTL_KEYBOARD_QUERY_TYPEMATIC

IOCTL_KEYBOARD_SET_INDICATORS

IOCTL_KEYBOARD_SET_TYPEMATIC

有关所有键盘设备控制请求的详细信息,请参阅 “人机界面设备参考”。

扫描键盘代码映射器

在 Microsoft Windows 操作系统中,输入设备提供的 PS/2 兼容的扫描代码将转换为虚拟密钥,这些密钥以 Windows 消息的形式通过系统传播。 如果设备为特定密钥生成不正确的扫描代码,则会发送错误的虚拟密钥消息。 可以通过编写筛选器驱动程序来修复此问题,该驱动程序分析固件生成的扫描代码,并将不正确的扫描代码修改为系统理解的扫描代码。 但是,这是一个繁琐的过程,有时可能会导致严重问题,如果内核级筛选器驱动程序中存在错误。

Windows 2000 和 Windows XP 包含新的扫描代码映射器,它提供了一种允许映射扫描代码的方法。 Windows 的扫描代码映射存储在以下注册表项中:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layout

注意

控件键下还有 键盘布局 键,但不应修改该键。

键盘布局 键中 ,必须添加 Scancode 地图 值。 此值的类型为 REG_BINARY(小 Endian 格式),并具有下表中指定的数据格式。

开始偏移量(以字节为单位) 大小(以字节为单位) 数据
0 4 标头:版本信息
4 4 标头:标志
8 4 标头:映射数
12 4 单个映射
... ... ...
最后 4 个字节 4 Null 终止符 (0x00000000)

第一个和第二个 DWORDS 存储标头信息,应设置为当前版本的扫描代码映射器的所有零。 第三个 DWORD 条目包含以下映射总数的计数,包括 null 终止映射。 因此,最小计数为 1(未指定映射)。 各个映射遵循标头。 每个映射的长度为一个 DWORD,并分为两个 WORD 长度字段。 每个 WORD 字段存储要映射的密钥的扫描代码。

映射存储在注册表中后,必须重新启动系统才能使映射生效。 如果在键压上需要扫描代码的映射,则会在将扫描代码转换为虚拟密钥之前在用户模式下执行该步骤。 在用户模式下执行此转换可能会存在某些限制,例如,在终端服务下运行时映射无法正常工作。

若要删除这些映射,请删除 Scancode 映射注册表值并重新启动。

示例 1

若要将左 Ctrl 键与 CAPS LOCK 键交换,请使用注册表编辑器(最好是Regedt32.exe)使用以下值修改 Scancode 映射键:

00000000 00000000 03000000 3A001D00 1D003A00 00000000

下表包含拆分为 DWORD 字段和交换的字节的这些条目。

:解释

0x00000000:标头:版本。 设置为所有零。

0x00000000:标头:标志。 设置为所有零。

0x00000003:地图中的三个条目(包括 null 条目)。

0x001D003A:左 CTRL 键 --> CAPS LOCK (0x1D --> 0x3A)。

0x003A001D:CAPS LOCK --> 左 CTRL 键(0x3A --> 0x1D)。

0x00000000:Null 终止符。

示例 2

还可以添加键盘上未正式提供的键,或者删除从未使用的键。 以下示例显示了存储在 Scancode Map以删除右 CTRL 键并更改右 Alt 键的功能以用作静音键的值:

00000000 00000000 03000000 00001DE0 20E038E0 00000000

下表包含拆分为 DWORD 字段和交换的字节的这些条目。

:解释

0x00000000:标头:版本。 设置为所有零。

0x00000000:标头:标志。 设置为所有零。

0x00000003:地图中的三个条目(包括 null 条目)。

0xE01D0000:删除右 Ctrl 键(0xE01D --> 0x00)。

0xE038E020:右 ALT 键 --> 静音键 (0xE038 --> 0xE020)。

0x00000000:Null 终止符。

生成必要的数据后,可以通过多种方式将其插入注册表中。

  • 可以生成一个.reg文件,可以使用注册表编辑器轻松合并到系统注册表中。
  • 还可以使用包含要添加的注册表信息的 [AddReg] 节创建 .inf 文件。
  • Regedt32.exe可用于手动将信息添加到注册表。

扫描代码映射器有几个优点和缺点。

优点包括:

  • 映射器可用作更正固件错误的简单修复方法。
  • 可以通过修改注册表中的映射,将常用键添加到键盘。 不经常使用的键(例如,右 CTRL 键)可以映射到 null(已删除)或交换其他键。
  • 可以轻松更改关键位置。 用户可以轻松自定义常用密钥的位置,使其受益。

可识别以下缺点:

  • 映射存储在注册表中后,需要重新启动系统才能激活它。
  • 存储在注册表中的映射在系统级别工作,并应用于所有用户。 无法根据当前用户将这些映射设置为以不同的方式工作。
  • 当前实现限制映射的功能,以便映射始终应用于连接到系统的所有键盘。 目前无法基于每个键盘创建地图。

查询鼠标设备

I8042prt 支持以下内部设备控制请求来查询有关鼠标设备的信息:

IOCTL_MOUSE_QUERY_ATTRIBUTES

有关所有鼠标设备控制请求的详细信息,请参阅 人机接口设备参考

与鼠标类驱动程序关联的注册表设置

下面是与鼠标类驱动程序关联的注册表项的列表。

[键: HKLM\SYSTEM\CurrentControlSet\Services\Mouclass\Parameters]

  • MaximumPortsServiced – 未在 Windows XP 及更高版本上使用。 仅适用于 Windows NT4。
  • PointerDeviceBaseName – 指定鼠标类设备驱动程序创建的设备对象的基名称
  • ConnectMultiplePorts – 确定每个类设备对象是否存在一个或多个端口设备对象。 此条目主要由设备驱动程序使用。
  • MouseDataQueueSize - 指定鼠标驱动程序缓冲的鼠标事件数。 它还用于计算非分页内存池中鼠标驱动程序的内部缓冲区的大小。

绝对指向设备

对于类型 为 GUID_CLASS_MOUSE 的设备,设备的功能驱动程序:

  • 处理特定于设备的输入。

  • 创建 MouseClassServiceCallback 所需的MOUSE_INPUT_DATA结构。

  • 通过在 IsR 调度完成例程中调用 MouseClassServiceCallback ,将MOUSE_INPUT_DATA结构传输到 Mouclass 数据队列。

对于绝对指向设备,设备的函数驱动程序必须通过以下方式设置 MOUSE_INPUT_DATA结构的 LastXLastYFlags 成员:

  • 除了将设备输入值除以设备的最大功能外,驱动程序还会通过0xFFFF来缩放设备输入值:

    LastX = ((device input x value) * 0xFFFF ) / (Maximum x capability of the device)
    LastY = ((device input y value) * 0xFFFF ) / (Maximum y capability of the device)
    
  • 驱动程序在 Flags设置MOUSE_MOVE_ABSOLUTE标志。

  • 如果窗口管理器应将输入映射到整个虚拟桌面,驱动程序将在 Flags设置MOUSE_VIRTUAL_DESKTOP标志。 如果未设置MOUSE_VIRTUAL_DESKTOP标志,窗口管理器会将输入仅映射到主监视器。

以下列表按设备类型指定如何实现绝对指向设备的这些特殊要求:

  • HID 设备:

    HID 鼠标设备的 Windows 功能驱动程序 Mouhid 自动实现这些特殊要求。

  • PS/2 样式的设备:

    需要高级筛选器驱动程序。 筛选器驱动程序提供 IsrHook 回调和类服务回调。 I8042prt 调用 IsrHook 来处理原始设备输入,并调用筛选器类服务回调来筛选输入。 筛选器类服务回调反过来又调用 MouseClassServiceCallback。 IsrHook 回调和类服务回调的组合处理特定于设备的输入,创建所需的MOUSE_INPUT_DATA结构,缩放设备输入数据,并设置MOUSE_MOVE_ABSOLUTE标志。

  • 即插即用 Serenum 枚举的 COM 端口设备:

    需要即插即用函数驱动程序。 函数驱动程序创建所需的MOUSE_INPUT_DATA结构、缩放设备输入数据,并在调用 MouseClassServiceCallback 之前设置MOUSE_MOVE_ABSOLUTE标志。

  • 非即插即用 COM 端口设备:

    需要特定于设备的函数驱动程序。 函数驱动程序创建所需的MOUSE_INPUT_DATA结构、缩放设备输入数据,并在调用 MouseClassServiceCallback 之前设置MOUSE_MOVE_ABSOLUTE标志。

  • 不支持的总线上的设备:

    需要特定于设备的函数驱动程序。 函数驱动程序创建所需的MOUSE_INPUT_DATA结构、缩放设备输入数据,并在调用 MouseClassServiceCallback 之前设置MOUSE_MOVE_ABSOLUTE标志。