NT式驱动的基本结构2 - 创建设备对象

        在NT式的驱动中,创建设备对象是由IoCreateDevice内核函数完成的。

NTSTATUS IoCreateDevice(
        IN  PDRIVER_OBJECT DriverObject,
        IN  ULONG DeviceExtensionSize,
        IN  PUNICODE_STRING DeviceName,
        IN  DEVICE_TYPE DeviceType,
        IN  ULONG DeviceCharacteristics,
        IN  BOOLEAN Exclusive,
        OUT PDEVICE_OBJECT *DeviceObject
        );
* DriverObject: 输入参数,每个驱动程序中。会有唯一的驱动对象与之对应,但每个驱动对象会有若干个设备对象。DriverObject指向的就是驱动对象指针。
* DeviceExtensionSize:输入参数,指定设备扩展的大小,I/O管理器会根据这个大小,在内存中创建设备扩展,并与驱动对象关联。
* DeviceName:输入参数,设置设备对象的名字。
* DeviceCharacteristics:输入参数,设置设备对象的特征。
* Exclusive:输入参数,设置设备对象是否为内核模式下使用,一般设置为TRUE。
* DeviceObject:输出参数,I/O管理器负责创建这个设备对象,并返回设备对象的地址。
* 返回值:返回此函数的调用状态。

        设备名称用UNICODE字符串指定,并且字符串必须是"\Device\[设备名]"的形式。

        在Windows下的所有的设备都是以类似名字命名的,例如,磁盘分区的C盘、D盘、E盘、F盘就是被命名为"\Device\HarddiskVolume1"、"\Device\HarddiskVolume2"、"\Device\HarddiskVolume3"、"\Device\HarddiskVolume4"。

        当然也可以不指定设备名字,如果在IoCreateDevice中没有指定设备对象的名字,I/O管理器会自动分配一个数字作为设备的设备名,例如"\Device\00000001"、"\Device\00000002"、"\Device\00000003"。

        如果指定了设备名,只能被内核模式下的其他驱动所识别。但是在用户模式下的应用程序无法识别这个设备。让用户模式下的应用程序能识别设备有两种办法:
第一种是通过符号链接找到设备;
第二种是通过设备接口找到设备。设备接口的办法在NT驱动中很少使用,经常在在WDM驱动中使用。

        符号链接可以理解为设备对象起了一个“别名”。设备对象的名称只能被内核模式的驱动识别,而别名可以被用户模式下的应用程序识别。例如,常说的C盘、D盘就是符号链接。所谓C盘就是名为"C:"的符号链接,其真正的设备对象是“\Device\HarddiskVolume1”,而"D:"所代表的真正的设备对象是“\Device\HarddiskVolume2”。创建符号链接的函数是IoCreateSymbolicLink,其函数声明如下:

NTSTATUS IoCreateSymbolicLink(
    IN PUNICODE_STRING SymbolicLinkName,
    IN PUNICODE_STRING DeviceName
    );
* SymbolicLinkName: 输入参数,符号链接的字符串,用UNICODE字符串表示。
* DeviceName:输入参数,设备对象名字的字符串,用UNICODE字符串表示。
* 返回值:返回创建符号链接是否成功。

        在内核模式下,符号链接是以“\??\”开头的(或者是“\DosDevices\”开头的)。而在用户模式下,则是以"\\.\"开头的,如C盘就是“\\.\C:”。在HelloDDK中,设备对象的符号链接是“\??\HelloDDK”。

#pragma INITCODE
NTSTATUS CreateDevice (
        IN PDRIVER_OBJECT    pDriverObject) 
{
    NTSTATUS status;
    PDEVICE_OBJECT pDevObj;
    PDEVICE_EXTENSION pDevExt;
    
    //创建设备名称
    UNICODE_STRING devName;
    RtlInitUnicodeString(&devName,L"\\Device\\MyDDKDevice");
    
    //创建设备
    status = IoCreateDevice( pDriverObject,
                        sizeof(DEVICE_EXTENSION),
                        &(UNICODE_STRING)devName,
                        FILE_DEVICE_UNKNOWN,
                        0, TRUE,
                        &pDevObj );
                        
    // 判断设备对象是否创建成功
    if (!NT_SUCCESS(status))
        return status;

    // 将设备对象设置为缓冲区设备
    pDevObj->Flags |= DO_BUFFERED_IO;
    
    // 得到设备扩展
    pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
    
    // 设置设备扩展的设备对象
    pDevExt->pDevice = pDevObj;
    
    // 设置设备扩展中的设备名称
    pDevExt->ustrDeviceName = devName;
    
    //创建符号链接
    UNICODE_STRING symLinkName;
    RtlInitUnicodeString(&symLinkName,L"\\??\\HelloDDK");
    pDevExt->ustrSymLinkName = symLinkName;
    
    // 创建符号链接
    status = IoCreateSymbolicLink( &symLinkName,&devName );
    
    // 判断是否成功创建符号链接
    if (!NT_SUCCESS(status)) 
    {
        // 删除符号链接
        IoDeleteDevice( pDevObj );
        return status;
    }
    
    return STATUS_SUCCESS;
}

        其中在创建设备对象的时候,指定的设备类型是FILE_DEVICE_UNKNOWN,说明此设备是常用设备之外的设备,一般虚拟设备使用这个作为设备类型。在创建设备对象后,对Flags的DO_BUFFERED_IO位进行设置,这里是将设备设置成“缓冲区设备”。关于将设备设置成“缓冲区设备”或者“直接设备”,后面在详细说明。随后设定设备的设备扩展,设备扩展是程序员自己定义的,在这里我们使用的非常简单:

typedef struct _DEVICE_EXTENSION {
    PDEVICE_OBJECT      pDevice;
    UNICODE_STRING      ustrDeviceName;        //设备名称
    UNICODE_STRING      ustrSymLinkName;        //符号链接名
    PUCHAR           MemBar0;            // 内存基地址0
    PUCHAR           MemBar1;            // 内存基地址1
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;

* pDevice: 设备对象中的 DeviceExtension 指向设备扩展,pDevice可以指回设备对象。
* ustrDeviceName:设备名
* ustrSymLinkName:符号链接名
* MemBar0、MemBar1:外设的基地址

        在设备扩展中记录以上几个信息,以备其他回调函数或者派遣函数使用,使用的时候,只需从驱动设备中获取,类似于以下使用方式:

pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WendyWJGu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值