You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 13:20:55