有关于《Windows驱动程序开发技术详解》中的HelloDDK卸载后导致Windows7蓝屏的解决方案

看《Windows驱动程序开发技术详解》的朋友们可能发现,按照书上的代码写了(抄了)HelloDDK后安装运行后正常,但卸载时发现Windows 7 蓝屏了,报错信息为PAGE_FAULT_IN_NON_PAGED_AREA (这可着实吓坏了我这个刚入门Windows驱动程序的萌新)

先上解决方案:

把所有#pragma INITCODE 替换成 #pragma PAGEDCODE

至于为什么,我在debug这段HelloDDK后发现是死在了卸载驱动例程的删除符号链接上,通过WinDbg的Watch发现这些无论是符号链接还是驱动设备都显示读取内存错误的提示……然后我试着把这段通过链表递归删除的代码去掉,虽然去掉后驱动卸载没有发生蓝屏,但再次安装时会发现无法安装,原因估计就是在于上次卸载驱动例程根本没卸载干净(包括这些符号链接),而重复创建时发生冲突。于是就有可能发生在创建设备时存储的设备的内存位置有问题。

网上查了一下,说INITCODE是在完成这段函数后会自动删除以节省内存。但是在这我的Windows 7 上却发现这会导致创建的所有信息(包括符号链接和设备对象)都变成无法读取的状态,自然卸载的时候就不会成功(至于为什么会变成无法读取的状态仍未知,如果有哪位知道请及时指出)。而报错信息本质上也确实是关于分页内存的错误信息。全部使用#pragma PAGEDCODE 就不用担心这种问题发生

下面附上改后的代码,并附上了一点注释方便驱动程序入门者理解


//Driver.h

#pragma once

#include <ntddk.h>

//定义分页标记、非分页标记、初始化内存块

#define PAGEDCODE code_seg("PAGE")
#define LOCKEDCODE code_seg()
#define INITCODE code_seg("INIT")

#define PAGEDDATA data_seg("PAGE")
#define LOCKEDDATA data_seg()
#define INITDATA data_seg("INIT")

#define arraysize(p) (sizeof(p)/sizeof((p)[0]))

//设备扩展结构体(自定义的)
typedef struct _DEVICE_EXTENSION {
    PDEVICE_OBJECT pDevice;            //系统的设备对象
    UNICODE_STRING ustrDeviceName;    //设备名称
    UNICODE_STRING ustrSymLinkName;    //符号链接名
}DEVICE_EXTENSION, *PDEVICE_EXTENSION;

//函数声明
NTSTATUS CreateDevice(IN PDRIVER_OBJECT pDriverObject);
void HelloDDKUnload(IN PDRIVER_OBJECT pDriverObject);
NTSTATUS HelloDDKDispatchRoutine(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp);


//Driver.cpp

#include "Driver.h"

//驱动程序入口函数 DriverEntry
//#pragma INITCODE        //Windows 7 下会导致蓝屏
#pragma PAGEDCODE
extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegistryPath){        //参数:(IO设备对象,驱动程序在注册表中的路径)
    NTSTATUS status;
    KdPrint(("Enter DriverEntry. ID = HelloDDK\n"));

    //注册其他驱动程序调用函数入口
    pDriverObject->DriverUnload = HelloDDKUnload;
    pDriverObject->MajorFunction[IRP_MJ_CREATE] = HelloDDKDispatchRoutine;
    pDriverObject->MajorFunction[IRP_MJ_CLOSE] = HelloDDKDispatchRoutine;
    pDriverObject->MajorFunction[IRP_MJ_WRITE] = HelloDDKDispatchRoutine;
    pDriverObject->MajorFunction[IRP_MJ_READ] = HelloDDKDispatchRoutine;
    //创建驱动设备对象(!)
    status = CreateDevice(pDriverObject);

    KdPrint(("Finished DriverEntry. ID = HelloDDK\n"));
    return status;
}

//初始化设备对象函数 CreateDevice
//#pragma INITCODE        //Windows 7 下会导致蓝屏
#pragma PAGEDCODE
NTSTATUS CreateDevice(IN PDRIVER_OBJECT pDriverObject){
    NTSTATUS status;
    PDEVICE_OBJECT pDevObj;        //设备对象(系统的)
    PDEVICE_EXTENSION pDevExt;    //设备扩展(可以理解为我们自己的)
    KdPrint(("Started creating device\n"));
#if DBG
    _asm int 3
#endif

    //创建设备名称
    UNICODE_STRING deviceName;
    RtlInitUnicodeString(&deviceName, L"\\Device\\MyDDKDeivce");
    //创建设备
    status = IoCreateDevice(pDriverObject, sizeof(DEVICE_EXTENSION), &(UNICODE_STRING)deviceName, FILE_DEVICE_UNKNOWN, 0, TRUE, &pDevObj);
    if(!NT_SUCCESS(status)) return status;

    pDevObj->Flags |= DO_BUFFERED_IO;    //设备对内存的操作有两种:BUFFERED_IO和DO_DIRECT_IO,此部分讲解请参考第三章。
    //填写我们自己定义的设备信息结构体
    pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
    pDevExt->pDevice = pDevObj;
    pDevExt->ustrDeviceName = deviceName;
    //创建符号链接,使之对应用程序可见。该符号链接指向真正的设备名称
    UNICODE_STRING symLinkName;
    RtlInitUnicodeString(&symLinkName, L"\\??\\HelloDDK");
    pDevExt->ustrSymLinkName = symLinkName;
    status = IoCreateSymbolicLink(&symLinkName, &deviceName);

    if(!NT_SUCCESS(status)){
        IoDeleteDevice(pDevObj);    //删除设备
        return status;
    }
    KdPrint(("Create device successfully\n"));
    return STATUS_SUCCESS;
}

//驱动程序卸载操作函数 HelloDDKUnload
#pragma PAGEDCODE
void HelloDDKUnload(IN PDRIVER_OBJECT pDriverObject){
#if DBG
    _asm int 3
#endif
    PDEVICE_OBJECT pNextObj;
    KdPrint(("Enter DriverUnload\n"));
    pNextObj = pDriverObject->DeviceObject;
    while(pNextObj!=NULL){
        PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pNextObj->DeviceExtension;
        //删除符号链接
        UNICODE_STRING pLinkName = pDevExt->ustrSymLinkName;
        IoDeleteSymbolicLink(&pLinkName);        //这边在 Windows 7 下pLinkName会显示memory read error(估计就是NULL),造成蓝屏,由于INITCODE被移除或使用了不同的内存段
        //链表递归,删除设备对象
        pNextObj = pNextObj->NextDevice;
        IoDeleteDevice(pDevExt->pDevice);
    }
    KdPrint(("Delete device successfully\n"));
}

//默认派遣例程,用于对设备对象的创建、关闭、读写操作。在此处将其成功返回 HelloDDKDispatchRoutine
#pragma PAGEDCODE
NTSTATUS HelloDDKDispatchRoutine(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp){
    KdPrint(("Enter HelloDDKDispatchRoutine\n"));
    NTSTATUS status = STATUS_SUCCESS;
    //完成IRP
    pIrp->IoStatus.Status = status;        //设置IRP状态为成功。IRP的讲解请参考第4章
    pIrp->IoStatus.Information = 0;        //设置操作的字节数为0,这里无实际意义
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);    //指示完成此IRP
    KdPrint(("Finish HelloDDKDispatchRoutine\n"));
    return status;

}



  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值