Windows自定义鼠标过滤驱动加载后立即收到IRP_MJ_CLOSE问题咨询
鼠标内核驱动加载异常及设备关联问题排查
我开发了如下Windows自定义鼠标内核驱动,但无法正常加载。使用OSR Loader启动驱动后,立即收到了IRP_MJ_CLOSE请求,无法理解原因。我使用的是带内置触控板的笔记本电脑,据我所知,此时PointerClass0设备应代表笔记本的触控板(鼠标)。是否应该使用PointerClass0之外的其他类?我还存在PointerClass1和PointerClass2,但关联这些类时IoAttachDevice总是失败,恳请提供解决思路。
#include <ntddk.h> #include <ntstrsafe.h> #pragma warning(disable : 4201) typedef struct { PDEVICE_OBJECT LowerKbdDevice; } DEVICE_EXTENSION, * PDEVICE_EXTENSION; PDEVICE_OBJECT myKbdDevice = NULL; ULONG pendingkey = 0; int first_time = TRUE; typedef struct _MOUSE_INPUT_DATA { USHORT UnitId; USHORT Flags; union { ULONG Buttons; struct { USHORT ButtonFlags; USHORT ButtonData; }; }; ULONG RawButtons; LONG LastX; LONG LastY; ULONG ExtraInformation; } MOUSE_INPUT_DATA, * PMOUSE_INPUT_DATA; VOID DriverUnload(PDRIVER_OBJECT DriverObject) { LARGE_INTEGER interval = { 0 }; interval.QuadPart = -10 * 1000 * 1000; // 1 second PDEVICE_OBJECT DeviceObject = DriverObject->DeviceObject; IoDetachDevice(((PDEVICE_EXTENSION)DeviceObject->DeviceExtension)->LowerKbdDevice); while (pendingkey) { KeDelayExecutionThread(KernelMode, FALSE, &interval); } IoDeleteDevice(myKbdDevice); DbgPrintEx(0, 0, "Mouse filter unloaded\n"); } NTSTATUS DispatchPass(PDEVICE_OBJECT DeviceObject, PIRP Irp) { PIO_STACK_LOCATION sl = IoGetCurrentIrpStackLocation(Irp); DbgPrintEx(0, 0, "DispatchPass: %d\n", sl->MajorFunction); UNREFERENCED_PARAMETER(DeviceObject); IoCopyCurrentIrpStackLocationToNext(Irp); NTSTATUS status = IoCallDriver(((PDEVICE_EXTENSION)DeviceObject->DeviceExtension)->LowerKbdDevice, Irp); return status; } NTSTATUS ReadComplete(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context) { UNREFERENCED_PARAMETER(DeviceObject); UNREFERENCED_PARAMETER(Context); PMOUSE_INPUT_DATA Keys = (PMOUSE_INPUT_DATA)Irp->AssociatedIrp.SystemBuffer; int structnum = (ULONG)Irp->IoStatus.Information / sizeof(PMOUSE_INPUT_DATA); if (structnum == 0) { Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } if (Irp->IoStatus.Status == STATUS_SUCCESS) { for (int i = 0; i < structnum; i++) { DbgPrintEx(0, 0, "The button state is %x\n", Keys->ButtonFlags); } } else { DbgPrintEx(0, 0, "Complete status not success: %d\n", Irp->IoStatus.Status); } if (Irp->PendingReturned) { IoMarkIrpPending(Irp); } pendingkey--; return STATUS_CONTINUE_COMPLETION; } NTSTATUS DispatchRead(PDEVICE_OBJECT DeviceObject, PIRP Irp) { UNREFERENCED_PARAMETER(DeviceObject); IoCopyCurrentIrpStackLocationToNext(Irp); pendingkey++; IoSetCompletionRoutineEx(DeviceObject, Irp, ReadComplete, NULL, TRUE, TRUE, TRUE); NTSTATUS status = IoCallDriver(((PDEVICE_EXTENSION)DeviceObject->DeviceExtension)->LowerKbdDevice, Irp); DbgPrintEx(0, 0, "DispatchRead: IoCallDriver status is %d\n", status); return status; } NTSTATUS MyAttachDevice(PDRIVER_OBJECT DriverObject) { UNICODE_STRING TargetDevice = RTL_CONSTANT_STRING(L"\Device\PointerClass0"); PAGED_CODE(); NTSTATUS status = IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION), NULL, FILE_DEVICE_MOUSE, 0, FALSE, &myKbdDevice); if (!NT_SUCCESS(status)) { DbgPrintEx(0, 0, "IoCreateDevice failed\n"); return status; } RtlZeroMemory(myKbdDevice->DeviceExtension, sizeof(DEVICE_EXTENSION)); myKbdDevice->Flags |= DO_BUFFERED_IO; myKbdDevice->Flags &= ~DO_DEVICE_INITIALIZING; status = IoAttachDevice(myKbdDevice,&TargetDevice,&((PDEVICE_EXTENSION)myKbdDevice->DeviceExtension)->LowerKbdDevice); if (!NT_SUCCESS(status)) { DbgPrintEx(0, 0, "IoAttachDevice failed with status %d\n", status); IoDeleteDevice(myKbdDevice); return status; } return STATUS_SUCCESS; } NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) { UNREFERENCED_PARAMETER(RegistryPath); DriverObject->DriverUnload = DriverUnload; for (int i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) { DriverObject->MajorFunction[i] = DispatchPass; } DriverObject->MajorFunction[IRP_MJ_READ] = DispatchRead; NTSTATUS status = MyAttachDevice(DriverObject); if (!NT_SUCCESS(status)) { DbgPrintEx(0, 0, "MyAttachDevice failed\n"); return status; } return STATUS_SUCCESS; }
解决思路
1. IRP_MJ_CLOSE请求的原因排查
- 设备属性不匹配:驱动创建的设备对象使用
FILE_DEVICE_MOUSE类型,需确认下层PointerClass设备的设备类型、IO方式是否一致。若下层设备采用DO_DIRECT_IO,而你的驱动设置DO_BUFFERED_IO,会导致IO处理逻辑冲突,触发系统关闭设备。 - 替换IoAttachDevice函数:
IoAttachDevice是旧版API,现代Pointer设备(尤其是触控板这类HID子系统设备)建议使用IoAttachDeviceToDeviceStackSafe,它能更安全地处理设备栈附加,避免因设备栈状态异常导致的关闭请求。
2. PointerClass设备选择问题
- 确认触控板对应设备:笔记本的触控板可能不属于PointerClass,而是归属于HID类设备。可通过设备管理器查看设备的硬件路径,或用WinObj工具查看
\Device目录,找到触控板对应的实际设备名称。PointerClass0可能对应外接鼠标,而非内置触控板。 - IoAttachDevice失败的解决:附加PointerClass1/2失败可能是设备被其他驱动独占,或驱动权限不足。确保驱动以管理员权限运行,且系统已开启测试模式(用于加载未签名驱动)。另外,部分设备可能需要特定的驱动加载顺序,可尝试调整附加时机。
3. 驱动代码中的错误修正
- 设备扩展命名修正:设备扩展中的
LowerKbdDevice命名错误(实际为鼠标设备),建议改为LowerMouseDevice,避免逻辑混淆。 - ReadComplete计算错误:
structnum = (ULONG)Irp->IoStatus.Information / sizeof(PMOUSE_INPUT_DATA)中,sizeof(PMOUSE_INPUT_DATA)是指针大小,应改为sizeof(MOUSE_INPUT_DATA)(结构体实际大小),否则会导致结构体数量计算错误,触发错误IO状态返回,引发设备关闭。 - Unload函数设备对象处理:
DriverObject->DeviceObject不一定等于myKbdDevice,若驱动创建多个设备对象会出错,应直接使用myKbdDevice获取设备扩展并调用IoDetachDevice。
4. 调试建议
- 增加日志输出:在DriverEntry、MyAttachDevice、DispatchClose(若需单独处理)中添加详细DbgPrint日志,记录设备附加状态、IRP参数,帮助定位问题。
- WinDbg调试:用WinDbg附加到系统,在驱动关键函数(DriverEntry、DispatchRead、ReadComplete)设置断点,查看调用栈和寄存器状态,确认IRP_MJ_CLOSE的触发来源。
内容的提问来源于stack exchange,提问作者Samuel Smith
相关产品推荐
相关产品推荐

