首页 > Windows开发 > 详细

RtlInitUnicodeString、IoCreateDevice、IoCreateSymbolicLink、IoDeleteDevice 四个 API 驱动函数的使用

时间:2015-11-18 21:22:46      阅读:3100      评论:0      收藏:0      [点我收藏+]

       要解释“驱动对象”,就得先从 DriverEntry() 说起:

       做过C语言开发的都知道程序是从 main() 函数开始执行。在进行 Windows 驱动程序开发的时候没有 main() 函数作为函数入口,取而代之的是 DriverEntry().

       DriverEntry() 的原型如下:

extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pUnicodeString)
{    
}

       前面的 extern “C”大概的意思就是调用C编译器对函数进行编译,实现C++和C的混合编程。

       DriverEntry

       描述:初始化驱动程序,定位和申请硬件资源,创建内核对象

       参数:

       pDriverObject

       从I/O管理器中传来的驱动对象

       pRegistryPath

       驱动程序在注册表中的路径

       返回值: 返回初始化驱动状态

 

什么是 DRIVER_OBJECT 驱动对象,他的作用是什么?

       每一个 驱动对象 代表着一个已经装载到内核模式下的驱动,以下例程的输入参数之一就都包含驱动对象:

            DriverEntry, AddDevice, Reinitialize(可选例程),Unload(可选例程)

       它们都包含指向驱动对象的指针。

      驱动对象是一个半透明对象,驱动编写者必须熟悉它的某些成员对象,以实现驱动的初始化功能和卸载功能(如果该驱动能够卸载)。以下列出的是驱动对象中能被驱动访问的成员:

      可访问成员

      PDEVICE_OBJECT DeviceObject

      指向一个由驱动创建的设备对象,当驱动程序调用IoCreateDevice成功时,该成员会自动更新。驱动程序可以利用该成员以及DEVICE_OBJECT对象中的NextDevice成员来实现对由该驱动创建的所有设备列表中设备的遍历。

      PDRIVER_EXTENSION DriverExtension

      驱动扩展对象指针,该对象唯一能访问的成员是DriverExtension-> AddDevice,对应的是驱动DriverEntry例程中的AddDevice例程。

      PUNICODE_STRING HardwareDatabase

      指向\Registry\Machine\Hardware,该路径指向的是注册表中包含该硬件的配置信息。

      PFAST_IO_DISPATCH FastIoDispatch

      指向快速I/O入口地址,该成员之用于FSD(文件系统驱动)已经网络传输驱动。

      PDRIVER_INITIALIZE DriverInit

      DriverEntry例程的入口点,由I\O管理器设置。

     PDRIVER_STARTIO DriverStarIo

      驱动程序中StartIo例程的入口地址(如果有的话),当驱动初始化时,DriverEntry例程负责设置它,如果驱动程序没有StartIo,该成员为NULL。

      PDRIVER_UNLOAD DriverUnload

      驱动程序中Unload例程的入口地址(如果有的话),当驱动初始化时,DriverEntry例程负责设置它,如果驱动程序没有StartIo,该成员为NULL。

      PDRIVER_DISPATCHMajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1]

      派遣例程表,该表包含了驱动中DispatchXxx routines等所有派遣例程的入口地址。该数组的索引值为IRP_MJ_Xxx,该值代表每一个IRP的主功能函数代码(IRP major function code), 任何驱动都必须为IRP_MJ_Xxx请求设置入口地址。每一个DispatchXxx例程的定义如下所示:

       NTSTATUS(*PDRIVER_DISPATCH) (IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp);
 

     备注:

      每一个内核模式的驱动的初始化例程(驱动入口)都必须命名为 DriverEntry,这样系统才可以自动将驱动加载进来。

      如果例程的名字为其他,则驱动编写者必须为链接器指定对应的初始化例程的名字,否则,系统的加载器或者I/O管理器将无法找到驱动的传递地址。其他标准驱动的名字可以由驱动编写者自行决定。

      一个驱动必须在驱动对象中设置 DispatchXxx 的入口地址,在驱动加载之后,该驱动对象将传递给 DriverEntry。一个设备的驱动必须为每一个 IRP_MJ_XXX 设置一个或者多个DispatchXxx 的入口地址,对同一类型的设备来说,该 IRP_MJ_XXX 都将被处理。一个上层的驱动必须为所有的 IRP_MJ_XXX 设置一个或者多个 DispatchXxx 入口点,这些IRP_MJ_XXX 将被发送到下一级的设备驱动,否则,驱动无法给没有设置 DispatchXxx 例程发送任何 IRP_MJ_XXX。

      DriverEntry 例程设置了的驱动的 AddDevice, StartIO 以及 Unload 等函数的入口地址。

      当驱动加载时,设备驱动可以利用 HardWareDatabase 字符串来从注册表中获取硬件的配置信息。该字符串对驱动来说只读。驱动对象中没有列出的成员是无法访问的。

 

什么是 DEVICE_OBJECT 驱动对象,它的作用是什么?

      DEVICE_OBJECT 结构被操作系统用来表示为一个”设备对象 ”,这个设备对象可以代表逻辑的、虚拟的或者是物理设备。每一个设备对象都会有一个指针来指向下一个设备对象(如果有的话),形成一个设备链。设备链的第一个设备是在 DRIVER_OBJECT 驱动对象结构体中指明的。

 

驱动的 API 函数:

     1. RtlInitUnicodeString

      作用: 初始化设备名称指针。

VOID  RtlInitUnicodeString
(
IN OUT UNICODE_STRING DestinationString,
IN PCWSTR SourceString
);

       参数:

       DestinationString

       需要初始化的指针 UNICODE_STRING

       SourceString

       指向一个以空结尾的 Unicode 字符串常量,用这个字符串来初始化 DestinationString     

       如例:

       代码1:

UNICODE_STRING US1; 

RtlInitUnicodeString(&US1,L"DDDD");

         会动态分配一块指向“DDDD”的内存指针,赋值给US1.Buffer;

       代码2:

wchar_t tmpstr[260]={0}; 

UNICODE_STRING US1; 

RtlInitUnicodeString(&US1,tmpstr);

          这时 US1.Buffer 直接指向 tmpstr, 如果修改了 US1,也会同时修改 tmpstr。

        另外此时 US1.MaximumLength=2;

        要重新设定 MaximumLength=260*2,才能正常使用。

        以前总是对符号链接不太明白,今天看到了一篇文章,讲的很好,记录一下。

        Windows下的设备是以"\Device\[设备名]”形式命名的。例如磁盘分区的c盘,d盘的设备名就是 "\Device\HarddiskVolume1”,"\Device\HarddiskVolume2”, 当然也可以不指定设备名称。

        如果 IoCreateDevice 中没有指定设备名称,那么I/O管理器会自动分配一个数字作为设备的名称。

              例如"\Device\00000001"。

       \Device\[设备名],不容易记忆,通常符号链接可以理解为设备的别名,更重要的是设备名,只能被内核模式下的其他驱动所识别,而别名可以被用户模式下的应用程序识别。

        例如c盘,就是名为"c:"的符号链接,其真正设备对象是"\Device\HarddiskVolume1”,所以在写驱动时候,一般我们创建符号链接,即使驱动中没有用到,这也算是一个好的习惯吧。

        驱动中符号链接名是这样写的 :

        L"\\??\\HelloDDK"  --->\??\HelloDDK

        L"\\DosDevices\\HelloDDK"  --->\DosDevices\HelloDDK

        在应用程序中,符号链接名:

        L"\\\\.\\HelloDDK"  -->\\.\HelloDDK

        winobj 和 DeviceTree 可以用来查看这些信息。

 

      2. IoCreateDevice

       作用: 此函数用于创建常规的设备对象.

         NTSTATUS IoCreateDevice(

              IN PDRIVER_OBJECT DriverObject,

              IN ULONG DeviceExtensionSize,

              IN PUNICODE_STRING DeviceNameOPTIONAL,

              IN DEVICE_TYPE DeviceType,

              IN ULONG DeviceCharacteristics,

              IN BOOLEAN Exclusive,

              OUT PDEVICE_OBJECT *DeviceObject  );

          参数:

          DriverObject

        一个指向调用该函数的驱动程序对象.每一个驱动程序在它的DriverEntry过程里接收一个指向它的驱动程序对象. WDM功能和过滤驱动程序也在他们的AddDevice过程接受一个驱动程序对象的指针.

          DeviceExtensionSize

        指定驱动程序为设备扩展对象而定义的结构体的大小.

          DeviceName

        (可选的参数)指向一个以零结尾的包含 Unicode 字符串的缓冲区,那是这个设备的名称,该字符串必须是一个完整的设备路径名. WDM功能驱动程序和过滤驱动程序它们设备对象没有名字.

          注意:如果设备名未提供(即这个参数是NULL),IoCreateDevice创建的设备对象将不会有一个DACL与之相关联.

          DeviceType

        指定一个由一个系统定义的FILE_DEVICE_XXX常量,表明了这个设备的类型 (如FILE_DEVICE_DISK,FILE_DEVICE_KEYBOARD等),或供应商定义的一种新型设备的类型.

          DeviceCharacteristics

        指定一个或多个系统定义的常量,连接在一起,提供有关驱动程序的设备其他信息.对于可能的设备特征信息, 见DEVICE_OBJECT结构体.

          Exclusive

        如果指定设备是独占的,大部分驱动程序设置这个值为FALSE,如果是独占的话设置为TRUE,非独占设置为FALSE.

          DeviceObject

        一个指向 DEVICE_OBJECT 结构体指针的指针,这是一个指针的指针,指向的指针用来接收DEVICE_OBJECT结构体的指针.

         返回值:

        IoCreateDevice 函数成功时返回 STATUS_SUCCESS,失败时返回适当的 NTSTATUS 错误代码.这些错误代码是:

                STATUS_INSUFFICIENT_RESOURCES  //资源不足

                STATUS_OBJECT_NAME_EXISTS   //指定对象名存在

                STATUS_OBJECT_NAME_COLLISION   //对象名有冲突


         调用要求: 包含文件( wdm.h,ntddk.h)

      3. IoCreateSymbolicLink 

         作用:  此函数用于将设备与符号连接进行绑定。

         NTSTATUS   IoCreateSymbolicLink( 

             IN PUNICODE_STRING  SymbolicLinkName,

             IN PUNICODE_STRING  DeviceName  );

          参数:

          SymbolicLinkName

          Unicode 字符串指针,是一个用户态可见的名称。

          DeviceName

          Unicode 字符串指针,是驱动程序创建的设备对象名称。

          返回值:

          如果符号链接创建成功 返回 STATUS_SUCCESS 

 

        4. IoDeleteDevice 

         作用:  此函数用于删除已建立的设备

         VOID   IoDeleteDevice(  

             IN PDEVICE_OBJECT  DeviceObject );

         参数:

         DeviceObject

         PDEVICE_OBJECT类型的指针,指向需要删除的设备对象

         无返回值

 

添加驱动设备例程

     用工具查看驱动及驱动设备

     请看下面的实例:

     首先创建一个头文件(创建设备的类)

#pragma once
#ifdef _cplusplus
extern "C"
{
#endif
#include <ntddk.h>
#ifdef _cplusplus
}
#endif

#define INITCODE cod_seg("INIT")
#pragma INITCODE

//定义一个创建我的设备的对象
NTSTATUS CreateMyDevice(IN PDRIVER_OBJECT pDriverObject) //IN 表示输入参数,在实际编译的时候没有用,只是让用户更好读
{
    NTSTATUS status;  //返回的值变量
    PDEVICE_OBJECT pDveObj;  //用来返回创建完成后得到的设备的变量
    UNICODE_STRING devName, symLinkName; //创建两个设备名称的变量,用来在内存中存储设备的名称
    
    //用 RtlInitUnicodeString API函数初始化一个设备名称,什么名称呢,名称为:\\Device\\Myddk.Device",然后在内存中
    //划分一块空间来存储,这个内存空间有个编码叫做内存指针,它指向这个设备名称。
    //我们可以通过这个内存指针,在众多的内存块中直接顺速的定位。
    RtlInitUnicodeString(&devName, L"\\Device\\Myddk.Device");
    
    //用 IoCreateDevice 函数创建常规的设备对象.包含 7 个参数,并有返回值。
    //第一个参数表示调用函数的驱动对象;第二个参数表示驱动程序为设备扩展对象而定义的结构体的大小.
    //第三个参数(可选的参数)表示这个设备的名称;//第四个表示这个设备的类型(如FILE_DEVICE_DISK, FILE_DEVICE_KEYBOARD等), 或供应商定义的一种新型设备的类型
    //第五个表示指定一个或多个系统定义的常量,连接在一起,提供有关驱动程序的设备其他信息.
    //第六个表示指定设备是否为独占的,逻辑值;//第七个表示一个指向DEVICE_OBJECT结构体指针的指针,也就是创建完后,得到的设备的指针
    status = IoCreateDevice(pDriverObject, 0, &devName, FILE_DEVICE_UNKNOWN, 0, TRUE, &pDveObj);
    
    //NT_SUCCESS 宏能获取变量的值,!NT_SUCCESS表示取非,也就是所不成立的条件下。
    if (!NT_SUCCESS(status))
    {
        if (status == STATUS_INSUFFICIENT_RESOURCES)
        {
            KdPrint(("资源部足"));
        }
        if (status == STATUS_OBJECT_NAME_EXISTS)
        {
            KdPrint(("指定对象名存在"));
        }
        if (status == STATUS_OBJECT_NAME_COLLISION)
        {
            KdPrint(("对象名冲突"));
        }
        KdPrint(("设备创建失败!"));
    }return status;
     pDveObj->Flags |= DO_BUFFERED_IO; //设置读写方式,这里为缓冲区读写方式,
    
     //创建符号链接
     RtlInitUnicodeString(&symLinkName, L"\\??\Myddk.LinkName"); //初始化连接符名称,并在内存中分配空间,产生内存指针
     status = IoCreateSymbolicLink(&symLinkName, &devName);  //给设备创建链接符,这个链接符,上面以在内存中分配空间了

     //删除设备
     if (!NT_SUCCESS(status))  //判断创建链接符是否成功
     {
         IoDeleteDevice(pDveObj);  //如果不成功,就删除设备
         return status;  //返回这个返回值
     }
     KdPrint(("设备创建成功")); 
return STATUS_SUCCESS;   //否则,返回创建成功信息
}

        然后,在源文件中,来加载与卸载这个设备!

#ifdef _cplusplus
extern "C"
{
#endif
#include "myddk.h"  //实例自定义的myddk.h 头文件(类)
#ifdef _cplusplus
}
#endif

#define PAGEDCODE code_seg("PAGE")
#pragma PAGEDCODE
VOID Unload(IN PDRIVER_OBJECT pDriverObject);
extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pUnicodeString)
{    
    KdPrint(("My First Driver!!"));
    CreateMyDevice(pDriverObject); //用入口函数的驱动对象来作创建设备函数的参数
    pDriverObject->DriverUnload = Unload;
    return 0;
}

VOID Unload(IN PDRIVER_OBJECT pDriverObject)
{
    KdPrint(("Unload My Driver Success"));
}

然后,我们用 DriverMonitor 工具来把生成的驱动加载到系统中,再用 Winobj 工具来查看设备,看是否创建了我们定义的名称为“Myddk.Device”的设备。

如图:

技术分享

驱动加载成功!

技术分享

在 Winobj 工具中的 Device 路径项中可以查看到,我们的设备 Myddk.Device 以创建成功。设备名称为 Myddk.Device;设备类型为 Device;可是设备的链接符没有??这是为什么,链接符也定义初始化也绑定了,如果绑定不成功,会删除设备啊!!

呵呵呵,我也不明白…这问题暂搁着,我问继续往下看。

现在我们用 DriverMonitor 工具卸载驱动看看!!

技术分享

卸载成功!

技术分享

什么情况??怎么设备还在呢??

技术分享

但是驱动去不在了!!

对了,想起来了,我刚刚确实卸载的只是驱动,并没卸载什么设备…,晕,咋这么古板呢!!

好吧!看来我们还得用代码向系统内存发送卸载设备的指令!!

见一下代码:

RtlInitUnicodeString、IoCreateDevice、IoCreateSymbolicLink、IoDeleteDevice 四个 API 驱动函数的使用

原文:http://www.cnblogs.com/lfls128/p/4972691.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!