WDM驱动的基本结构:

WDM驱动模型是建立在NT式驱动程序模型基础之上的。对于WDM驱动程序来说,一般都是基于分层的,即完成一个设备的操作,至少要由两个驱动设备共同完成。

1)物理设备对象和功能设备对象

物理设备对象(Physical Device Object,PDO)和功能设备对象(Function Device Object,FDO)的关系是“附加”与“被附加”的关系。

当PC插入某个设备时,PDO会自动创建。确切的说,是由总线驱动创建的。PDO不能单独操作设备,需要配合FDO一起使用。系统会提示检测到新设备,要求安装驱动程序。需要安装的驱动程序就是WDM程序,此驱动程序负责创建FDO,并且附加到PDO上。

当一个FDO附加到PDO上时,PDO设备对象的子域AttachedDevice会记录FDO的位置。PDO被称作底层驱动或下层驱动,而FDO被称作高层驱动或上层驱动。

复杂一点的情况是,在FDO和PDO之间还会存在过滤驱动。在FDO上面的过滤驱动被称作上层过滤驱动,在FDO下层的驱动被称作下层过滤驱动。每个设备对象中,有个StackSize子域,表明操作这个设备对象需要几层才能到达最下层的物理设备。

过滤驱动可以嵌套,即可以有很多个高层过滤驱动或多个底层过滤驱动。过滤驱动不是必须的,在WDM模型中PDO和FDO是必须的。

NT设备是被动装入的,例如当有设备插入PC时,系统不会有提示,用户需要自己指定加载何种驱动;而WDM驱动则不然,当插入设备时,系统会自动创建出PDO,并提示请求用户安装FDO。

2)WDM驱动的入口程序

和NT驱动一样也是DriverEntry,但是初始化作用被分散到其他例程中。如创建设备对象的工作被放在了AddService例程中,同时,在DriverEntry中,需要设置对IRP_MJ_PNP处理的派遣函数。

WDM驱动的DriverEntry和NT式驱动的DriverEntry有以下几点不同:

(1)增加了对AddService函数的设置;AddService例程负责创建FDO,并附加到PDO上。

(2)创建设备对象的操作不在这个函数中了,而转到了AddService例程中。

(3)必须加入IRP_MJ_PNP的派遣回调函数。IRP_MJ_PNP主要是负责计算机中即插即用的处理。

3)WDM驱动的AddService例程

AddService例程是WDM独有的。在DriverEntry中需设置AddService例程的函数地址,设置的方式是驱动对象中有个DriverExtension子域,DriverExtension中有个AddService子域,将该子域指向AddService例程的函数地址即可。

和DriverEntry不同,AddService例程的名字可以任意命名:

DRIVER_ADD_DEVICE AddDevice;

NTSTATUS AddDevice(

__in  struct _DRIVER_OBJECT *DriverObject, //驱动对象(I/O管理器创建的)

__in  struct _DEVICE_OBJECT *PhysicalDeviceObject //设备对象(底层总线驱动创建的PDO设备对象)

//传入该参数的目的是将FDO附加在PDO上

)

{ ... }

AddService函数的功能可分为:

(1)通过IoCreateDevice等函数,创建设备对象,该设备对象就是FDO。

(2)创建FDO后,需要将FDO的地址保存下来,保存的位置是在设备扩展中。

(3)驱动程序将创建的FDO附加到PDO上,附加这个动作是利用函数IoAttachDeviceToDeviceStack实现的:

PDEVICE_OBJECT IoAttachDeviceToDeviceStack(

__in  PDEVICE_OBJECT SourceDevice, //要附加到其他设备上的设备,

//将FDO附加在PDO之上时,此处即填FDO的地址

__in  PDEVICE_OBJECT TargetDevice //被附加的设备,将FDO直接附加在PDO上时

//(不考虑过滤驱动),此处即时PDO的地址

);

返回值:附加后,返回附加设备的下层设备。如果中间没有过滤驱动,返回值就是PDO;如果中间有过滤驱动,返回的是过滤驱动。

当FDO附加到PDO上时,PDO会通过AttachedDevice子域知道它上层的设备是FDO(或过滤驱动);而FDO无法知道下层是什么设备,因此,需要通过设备扩展设置FDO下层的设备,例如:

//设备扩展结构体

typedefstruct _DEVICE_EXTENSION

{

PDEVICE_OBJECT fdo; //功能设备对象FDO

PDEVICE_OBJECT NextStackDevice; //FDO的下层驱动设备

UNICODE_STRING ustrDeviceName; //设备名

UNICODE_STRING ustrSymLinkName; //符号链接

}DEVICE_EXTENSION, *PDEVICE_EXTENSION;

一般根据自己的需要定制自己的设备扩展。子域fdo是为了保存FDO的地址以备后用;子域NextStackDevice是为了定位设备的下一层设备。在附加操作完成后,需要设定符号链接,以便用户应用程序访问该设备。

4)DriverUnload例程

在NT式驱动中,DriverUnload例程主要负责做删除设备和取消符号链接。而在WDM驱动中,这部分操作被IRP_MN_REMOVE_DEVICE的处理函数所负责。因此,如果在DriverEntry中有申请内存的操作,可以在DriverUnload例程中回收这些内存,DriverUnload例程变得相对简单了。

5)对IRP_MN_REMOVE_DEVICE IRP的处理

驱动程序内部是由IRP驱动的。创建IRP的原因很多,IRP_MN_REMOVE_DEVICE这个IRP是当设备被卸载时,由即插即用管理器创建,并发送到驱动程序中的。IRP一般由两个号码指定该IRP的具体意义:一是主IRP号(Major IRP);一是辅IRP号(Minor IRP)。

每个IRP都由对应的派遣函数所处理,派遣函数是在DriverEntry中指定的。

在WDM驱动程序中,对设备的卸载一般是在对IRP_MN_REMOVE_DEVICE的处理函数中进行的。除了需要删除设备,取消符号链接外,在卸载函数中还需要将FDO从PDO上的堆栈中移除掉,使用函数IoDetachDevice:

VOID IoDetachDevice(

__inout  PDEVICE_OBJECT TargetDevice //下层堆栈上的设备对象

);

调用此函数后,可将FDO从设备链上删除,但PDO还是存在的,PDO的删除是由操作系统负责的。

WDM驱动程序的编写实例如下,先定义头文件HelloWDM.h:

#pragmaonce

#ifdef __cplusplus

extern"C"

{

#endif

#include<wdm.h>

#ifdef __cplusplus

}

#endif

//设备扩展结构体

typedefstruct _DEVICE_EXTENSION

{

PDEVICE_OBJECT fdo;

PDEVICE_OBJECT NextStackDevice;

UNICODE_STRING ustrDeviceName;

UNICODE_STRING ustrSymLinkName;

}DEVICE_EXTENSION, *PDEVICE_EXTENSION;

//定义分页内存、非分页内存和INIT段内存标志

#define PAGEDCODE code_seg("PAGE")

#define LOCKEDCODE code_seg()

#define INITCODE code_seg("INIT")

#define PAGEDDATA data_seg("PAGE")

#define LOCKEDCODE code_seg()

#define INITDATA data_seg("INIT")

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

NTSTATUS HelloWDMAddDevice(IN PDRIVER_OBJECT DriverObject,

IN PDEVICE_OBJECT PhysicalDeviceObject);

NTSTATUS HelloWDMPnp(IN PDEVICE_OBJECT fdo, IN PIRP Irp);

NTSTATUS HelloWDMDispatchRoutine(IN PDEVICE_OBJECT fdo, IN PIRP Irp);

NTSTATUS DefaultPnpHandler(PDEVICE_EXTENSION pdx, PRIP Irp);

NTSTATUS HandleRemoveDevice(PDEVICE_EXTENSION pdx, PRIP Irp);

void HelloWDMUnload(IN PDRIVER_OBJECT DriverObject);

extern"C"

NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject,

IN PUNICODE_STRING pRegistryPath);

和NT式驱动程序一样,WDM的入口函数地址也是DriverEntry,且在C++编译时需要使用extern “C”修饰:

/**

* 函数名称:DriverEntry

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

* 参数列表:

*    pDriverObject:从I/O管理器中传进来的驱动对象

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

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

*/

#pragma INITCODE //此函数放在INIT段中,当驱动加载结束后,此函数就可以从内存中卸载了

extern"C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject,

IN PUNICODE_STRING pRegistryPath)

{

KdPrint(("Enter DriverEntry\n"));

pDriverObject->DriverExtension->AddDevice = HelloWDMAddDevice;

pDriverObject->MajorFunction[IRP_MJ_PNP] = HelloWDMPnp;

pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = HelloWDMDispatchRoutine;

pDriverObject->MajorFunction[IRP_MJ_CREATE] = HelloWDMDispatchRoutine;

pDriverObject->MajorFunction[IRP_MJ_READ] = HelloWDMDispatchRoutine;

pDriverObject->MajorFunction[IRP_MJ_WRITE] = HelloWDMDispatchRoutine;

pDriverObject->DriverUnload = HelloWDMUnload;

KdPrint(("Leave DriverEntry\n"));

return STATUS_SUCCESS;

}

在WDM驱动程序中,创建设备对象的任务不再由DriverEntry承担,而需要驱动程序向系统注册一个称为AddDevice的例程。此例程由PNP管理器负责调用,其函数主要职责是创建设备。

/**

* 函数名称:HelloWDMAddDevice

* 功能描述:添加新设备

* 参数列表:

*    DriverObject:从I/O管理器中传进来的驱动对象

*    PhysicalDeviceObject:从I/O管理器中传进来的物理设备对象

* 返回值:返回新添加设备的状态

*/

#pragma PAGEDCODE

NTSTATUS HelloWDMAddDevice(IN PDRIVER_OBJECT DriverObject,

IN PDEVICE_OBJECT PhysicalDeviceObject)

{

PAGED_CODE(); //DDK提供的宏,只在check版中有效,

//用于确保该例程运行在低于APC_LEVEL的中断优先级别上

KdPrint(("Enter HelloWDMAddDevice\n"));

NTSTATUS status;

PDEVICE_OBJECT fdo;

UNICODE_STRING devName;

RtlInitUnicodeString(&devName, L"\\Device\\ASCEWDMDevice");

status = IoCreateDevice(DriverObject,sizeof(DEVICE_EXTENSION),

&(UNICODE_STRING)devName,

FILE_DEVICE_UNKNOWN,

0, FALSE, &fdo);

if(!NT_SUCCESS(status))

{

return status;

}

PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;

pdx->fdo = fdo;

//将fdo(功能设备对象)挂接到设备堆栈上,将返回值(下层堆栈的位置)

//记录在设备扩展结构中

pdx->NextStackDevice = IoAttachDeviceToDeviceStack(

fdo, PhysicalDeviceObject);

UNICODE_STRING symLinkName;

RtlInitUnicodeString(&symLinkName, L"\\DosDevices\\HelloWDM");

pdx->ustrDeviceName = devName;

pdx->ustrSymLinkName = symLinkName;

status = IoCreateSymbolicLink(&(UNICODE_STRING)symLinkName,

&(UNICODE_STRING)devName);

if(!NT_SUCCESS(status))

{

IoDeleteSymbolicLink(&pdx->ustrSymLinkName);

status = IoCreateSymbolicLink(&symLinkName, &devName);

if(!NT_SUCCESS(status))

{

return status;

}

}

fdo->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE;

fdo->Flags &= ~DO_DEVICE_INITIALIZING;

KdPrint(("Leaving HelloWDMAddDevice\n"));

}

WDM式驱动程序主要区别在于对IRP_MJ_PNP的IRP的处理。其中,IRP_MJ_PNP会细分为若干个子类。下面代码除了对IRP_MN_REMOVE_DEVICE做特殊处理外,其他IRP则做相同处理:

/**

* 函数名称:HelloWDMPnp

* 功能描述:对即插即用IRP进行处理

* 参数列表:

*    fdo:功能设备对象

*    Irp:从I/O请求包

* 返回值:返回状态

*/

#pragma PAGEDCODE

NTSTATUS HelloWDMPnp(IN PDEVICE_OBJECT fdo, IN PIRP Irp)

{

PAGED_CODE();

KdPrint(("Enter HelloWDMPnp\n"));

NTSTATUS status = STATUS_SUCCESS;

PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;

//得到当前IRP的堆栈

PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);

static NTSTATUS (*fcntab[])(PDEVICE_EXTENSION pdx, PRIP Irp) =

{

DefaultPnpHandler, //IRP_MN_START_DEVICE

DefaultPnpHandler, //IRP_MN_QUERY_REMOVE_DEVICE

HandleRemoveDevice,//IRP_MN_REMOVE_DEVICE

DefaultPnpHandler, //IRP_MN_CANCEL_REMOVE_DEVICE

DefaultPnpHandler, //IRP_MN_STOP_DEVICE

DefaultPnpHandler, //IRP_MN_QUERY_STOP_DEVICE

DefaultPnpHandler, //IRP_MN_CANCEL_STOP_DEVICE

DefaultPnpHandler, //IRP_MN_QUERY_DEVICE_RELATIONS

DefaultPnpHandler, //IRP_MN_QUERY_INTERFACE

DefaultPnpHandler, //IRP_MN_QUERY_CAPABILITIES

DefaultPnpHandler, //IRP_MN_QUERY_RESOURCES

DefaultPnpHandler, //IRP_MN_QUERY_RESOURCE_REQUIREMENTS

DefaultPnpHandler, //IRP_MN_QUERY_DEVICE_TEXT

DefaultPnpHandler, //IRP_MN_FILTER_RESOURCE_REQUIREMENTS

DefaultPnpHandler, //

DefaultPnpHandler, //IRP_MN_READ_CONFIG

DefaultPnpHandler, //IPR_MN_WRITEZ_CONFIG

DefaultPnpHandler, //IRP_MN_EJECT

DefaultPnpHandler, //IRP_MN_SET_LOCK

DefaultPnpHandler, //IRP_MN_QUERY_ID

DefaultPnpHandler, //IRP_MN_QUERY_PNP_DEVICE_STATE

DefaultPnpHandler, //IRP_MN_QUERY_BUS_INFORMATION

DefaultPnpHandler, //IRP_MN_DEVICE_USAGE_NOTIFICATION

DefaultPnpHandler, //IRP_MN_SURPRISE_REMOVAL

};

ULONG fcn = stack->MinorFunction;

if(fcn >= ARRAYSIZE(fcntab))

{

//未知的设备类型

status = DefaultPnpHandler(pdx, Irp); //对于未知的IRP类型,我们让

//DefaultPnpHandler函数处理

return status;

}

#if DBG

staticchar* fcnname[] =

{

"IRP_MN_START_DEVICE",

"IRP_MN_QUERY_REMOVE_DEVICE",

"IRP_MN_REMOVE_DEVICE",

"IRP_MN_CANCEL_REMOVE_DEVICE",

"IRP_MN_STOP_DEVICE",

"IRP_MN_QUERY_STOP_DEVICE",

"IRP_MN_CANCEL_STOP_DEVICE",

"IRP_MN_QUERY_DEVICE_RELATIONS",

"IRP_MN_QUERY_INTERFACE",

"IRP_MN_QUERY_CAPABILITIES",

"IRP_MN_QUERY_RESOURCES",

"IRP_MN_QUERY_RESOURCE_REQUIREMENTS",

"IRP_MN_QUERY_DEVICE_TEXT",

"IRP_MN_FILTER_RESOURCE_REQUIREMENTS",

"",

"IRP_MN_READ_CONFIG",

"IRP_MN_WRITE_CONFIG",

"IRP_MN_EJECT",

"IRP_MN_SET_LOCK",

"IRP_MN_QUERY_ID",

"IRP_MN_QUERY_PNP_DEVICE_STATE",

"IRP_MN_QUERY_BUS_INFORMATION",

"IRP_MN_DEVICE_USAGE_NOTIFICATION",

"IRP_MN_SURPRISE_REMOVAL"

};

Kdprint(("PNP request (%s)\n", fcnname[fcn]));

#endif//DBG

status = (*fcntab[fcn])(pdx, Irp);

KdPrint(("Leave HelloWDMPnp\n"));

return status;

}

除了IRP_MN_STOP_DEVICE之外,HelloWDM对其他PNP的IRP做同样处理,即直接传递到底层驱动,并将底层驱动的结果返回:

/**

* 函数名称:DefaultPnpHandler

* 功能描述:对PNP IRP进行默认处理

* 参数列表:

*    pdx:设备对象的扩展

*    Irp:从I/O请求包

* 返回值:返回状态

*/

#pragma PAGEDCODE

NTSTATUS DefaultPnpHandler(PDEVICE_EXTENSION pdx, PRIP Irp)

{

PAGED_CODE();//确保该例程处于APC_LEVEL之下

KdPrint(("Enter DefaultPnpHandler\n"));

IoSkipCurrentIrpStackLocation(Irp); //略过当前堆栈

KdPrint(("Leave DefaultPnpHandler\n"));

return IoCallDriver(pdx->NextStackDevice, Irp); //用下层堆栈的驱动设备对象处理此IRP

}

对IRP_MN_REMOVE_DEVICE的处理类似于在NT式驱动中的卸载例程,而在WDM驱动中,卸载例程几乎不用做处理:

/**

* 函数名称: HandleRemoveDevice

* 功能描述:对IRP_MN_REMOVE_DEVICE进行处理

* 参数列表

*    fdo:功能设备对象

*    Irp:从I/O请求包

* 返回值:返回状态

*/

#pragma PAGEDCODE

NTSTATUS HandleRemoveDevice(PDEVICE_EXTENSION pdx, PIRP Irp)

{

PAGED_CODE();

KdPrint(("Enter HandleRemoveDevice\n"));

Irp->IoStatus.Status = STATUS_SUCCESS;

NTSTATUS status = DefaultPnpHandler(pdx, Irp);

IoDeleteSymbolicLink(&(UNICODE_STRING)pdx->ustrSymLinkName);

//调用IoDetachDevice()把fdo从设备栈中脱开

if(pdx->NextStackDevice)

IoDetachDevice(pdx->NextStackDevice);

//删除fdo

IoDeleteDevice(pdx->fdo);

KdPrint(("Leave HandleRemoveDevice\n"));

return status;

}

/**

* 函数名称:HelloWDMDispatchRoutine

* 功能描述:对默认IRP进行处理

* 参数列表:

*    fdo:功能设备对象

*    Irp:从I/O请求包

* 返回值:返回状态

*/

#pragma PAGEDCODE

NTSTATUS HelloWDMDispatchRoutine(IN PDEVICE_OBJECT fdo, IN PIRP Irp)

{

PAGED_CODE();

KdPrint(("Enter HelloWDMDispatchRoutine\n"));

Irp->IoStatus.Status = STATUS_SUCCESS;

Irp->IoStatus.Information = 0;

IoCompleteRequest(Irp, IO_NO_INCREMENT);

KdPrint(("Leave HelloWDMDispatchRoutine\n"));

return STATUS_SUCCESS;

}

由于WDM式的驱动程序将主要的卸载任务放在了对IRP_MN_REMOVE_DEVICE的处理函数中,在标准的卸载例程中几乎没有什么需要做的:

/**

* 函数名称:HelloWDMUnload

* 功能描述:负责驱动程序的卸载操作

* 参数列表:

*    DriverObject:驱动对象

* 返回值:返回状态

*/

#pragma PAGEDCODE

void HelloWDMUnload(IN PDRIVER_OBJECT DriverObject)

{

PAGED_CODE();

KdPrint(("Enter HelloWDMUnload\n"));

KdPrint(("Leave HelloWDMUnload\n"));

}

WDM驱动程序的基本结构和实例相关推荐

  1. WINDOWS下PCI接口卡WDM驱动程序的DMA编程技术1

    摘要:     本文主要讨论了在Windows环境下开发PCI接口卡DMA应用的WDM编程技术,并给出了一个应用DriverWorks和VC++开发的实例程序代码. 关键词:Windows.PCI总线 ...

  2. WDM驱动程序介绍(引)

    http://zhidao.baidu.com/question/56715021.html WDM(Windows driver model)是微软为开发人员提供的一种编写运行在Windows平台下 ...

  3. WDM 驱动程序开发

    1.概述  引入了全新的WDM (Win32 Driver Model)的驱动程序架构,说是新技术,其实早在1997年Microsoft就提出了该项技术并在Windows 98中得到了充分的应用,换句 ...

  4. go面向对象编程:结构体struct详解、结构体实例的创建方式、结构体之间的转换(type取别名的使用)、方法的注意事项及与函数的区别

    入门示例 package main import "fmt" //定义老师结构体,将老师中的各个属性 统一放入结构体中管理: type Teacher struct{//变量名字大 ...

  5. C++结构体实例和类实例的初始化

    结构体实例(包括共用体)和类实例的初始化方法完全相同,二者都可以应用于继承层次中.不同点是结构体(包括共用体)默认成员为public,而类默认成员是private型的. 一.若类和结构体所有数据成员均 ...

  6. WDM驱动程序入门(很详细)

    WDM驱动程序是一种很新的东西,相信很多人都跟我一样,对它很感兴趣,但是又找不到学习的切入点.究其原因,还是因为WDM是一种非常"死板板"的程序,它一运行就是工作在系统的底层RIN ...

  7. WDM驱动程序入门(3)——安装步骤

    WDM驱动程序入门(3)--安装步骤 DDK分为98 DDK和2000 DDK两种,它们工作起来是大同小异的,不过有些驱动程序只能在2000 DDK中使用.由于Win98注定是一种即将被淘汰的操作系统 ...

  8. WDM驱动程序入门(1)-Hello WDM

    WDM驱动程序入门(1)-Hello WDM     WDM驱动程序是一种很新的东西,相信很多人都跟我一样,对它很感兴趣,但是又找不到学习的切入点.究其原因,还是因为WDM是一种非常"死板板 ...

  9. C++结构体实例和类实例的初始化 .

    结构体实例(包括共用体)和类实例的初始化方法完全相同,二者都可以应用于继承层次中.不同点是结构体(包括共用体)默认成员为public,而类默认成员是private型的. 一.若类和结构体所有数据成员均 ...

  10. c语言-结构体实例笔记

    结构体实例 实例一览: 使用结构体存储学生的信息 Store information of a student using structure 计算二者距离(以英寸英尺为单位) Add two dis ...

最新文章

  1. ubuntud——系统备份和恢复
  2. 大疆开挂,谁都不能阻拦它登上好莱坞无人机领域巅峰!
  3. 云计算实训总结_云计算实习报告.doc
  4. 带有ActiveMQ和Maven的JMS Sender应用程序
  5. 【缓存】缓存,这么用才真正达到缓存的效果
  6. 数据分析对企业的重要性
  7. DotNET企业架构应用实践-数据库表记录的唯一性设计的设计兼议主键设定原则
  8. 文字转语音怎么在线生成MP3格式的音频?
  9. java语言的编译器命令_Java编译器命令行功能
  10. oracle 自增长这么使用,Eova Oracle 自增长的处理
  11. 一文看懂三维建模到底是什么?
  12. Java 动态代理机制讲解(Proxy.newProxyInstance)
  13. 未来规划——北京大学数院432应用统计备考攻略
  14. java jxl导出excel小结
  15. 58同城产品2面面经
  16. 【Openai】介绍
  17. LDA+U中U值的计算方法
  18. 先验概率、后验概率、条件概率的形象解释
  19. Mendix for Manufacturing Industries指南
  20. 兄弟2260激光打印机硒鼓灯常亮

热门文章

  1. 干货!Web 网页设计规范
  2. N、NP、NPC问题分析
  3. IDEA2020版本下载、安装
  4. ic和mos怎么区分_快速掌握MOS管源极和漏极的区别!看了受益匪浅!
  5. TCP/IP报文头部整理
  6. 文件后缀名查询(全)
  7. 关于计算机信息管理的照片,2021年10月青海自考计算机科学与技术(计算机信息管理方向)专业报名照片要什么格式...
  8. android 校验手机号码,检查Android中的有效手机号码
  9. PHP判断手机号码是否正确
  10. 天天说大数据但不知怎么用?读完这篇你就懂了