Windows Driver Model

✍ dations ◷ 2024-12-23 11:45:50 #微软API,驱动程序

视窗驱动程序模型(Windows Driver Model,简称WDM),是微软视窗操作系统的驱动程序设计架构,包括对于各项设备(Device)的支持,如键盘(Keyboard)、鼠标(Mouse)、网卡(Network Driver Interface Specification,Ndis)、通用串行总线(Universal Serial Bus,USB)等。WDM可支持Windows 98, Windows 98 Second Edition, Windows Me, Windows 2000, Windows XP以及Windows Server 2003在x86平台上的建制工作。这个架构分成好几个管理层面:

驱动程序的种类有总线驱动程序(bus driver)、功能驱动程序(function driver)、筛选驱动程序(filter driver)三种:

如同许多语言从main函数开始,WDM驱动程序的加载(Loading)乃至于动作,以及其卸载(Unload)行为,都有其步骤与规则。WDM驱动程序可以动态式的加载与卸载,当侦测到设备(Device)插入的时候,依据“PnP Manager”会自动地加载相对应的设备驱动程序,然后成为“Driver Object”,并调用DriverEntry函数。所有的WDM驱动程序,都必须拥有这个DriverEntry例程(routine),而且“此一名称不可改变”的,所有的驱动程序也是从这里开始运行的,I/O Manager首先调用驱动程序的DriverEntry()。DriverEntry在DDK Compiler编译出来的输出符号表是“DriverEntry@8”,原因是微软的C编译器把stdcall函示(例如VcDCall)的名称加上“记号”,加上一个@符号,在附上参数的总字节个数,8指此function的所有参数所占的byte数。

DriverEntry函数有两个参数,其中第一个参数PDRIVER_OBJECT DriverObject是指向该驱动程序对应的对象指针;PUNICODE_STRING RegistryPath,驱动程序的服务主要键码,这个参数的使用时机并不多。以下是一个简单而标准的DriverEntry基本实现:

 NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING pRegistryString) {   //PDEVICE_OBJECT DriverObject;   UNICODE_STRING deviceName;   RtlInitUnicodeString( &deviceName, DEVICE_NAME );          status = IoCreateDevice( DriverObject, 0, &deviceName,       FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, true, &pDeviceObj );    UNICODE_STRING linkName;   RtlInitUnicodeString( &linkName, LINK_NAME );   status = IoCreateSymbolicLink( &linkName, &deviceName );    DriverObject->DriverUnload = DriverUnload;   DriverObject->MajorFunction =   DriverObject->MajorFunction =   DriverObject->MajorFunction = xxDriverDispatch;   DriverObject->DriverUnload = xxUnload;    return STATUS_SUCCESS; }

IRQL

DriverEntry运作在IRQL = PASSIVE_LEVEL等级,所以可以使用标签页内存。WDM有数个经常实现的PASSIVE_LEVEL等级如下:

另外还有几个DISPATCH_LEVEL等级的函数如下:

当函数完成对象的初始化与系统注册之后,接下来的重点便会落在Dispatch Function身上。每当I/O Manager得到一个请求时(例如按键,移动鼠标),它使用请求的函数代码(IoControlCode)调用驱动程序中几个Dispatch行程。

 NTSTATUS xxDispatch (IN PDEVICE_OBJECT DeviceObject,IN PIRP pIrp) {     NTSTATUS ntStatus = STATUS_SUCCESS;    ULONG IoControlCodes = 0;                 PIO_STACK_LOCATION IrpStack=NULL;        pIrp->IoStatus.Status = STATUS_SUCCESS;    pIrp->IoStatus.Information = 0;     IrpStack = IoGetCurrentIrpStackLocation(pIrp);         switch (IrpStack->MajorFunction)    {         case IRP_MJ_CREATE:            break;        case IRP_MJ_CLOSE:            break;        case IRP_MJ_DEVICE_CONTROL:            IoControlCodes=IrpStack->Parameters.DeviceIoControl.IoControlCode;            switch (IoControlCodes)            {                 case IOCTL_1:                    break;                case IOCTL_2:                    break;                default:                    pIrp->IoStatus.Status = STATUS_INVALID_PARAMETER;                    break;            }            break;        default:            break;    }    ntStatus=pIrp->IoStatus.Status;     IoCompleteRequest(pIrp,IO_NO_INCREMENT);     return ntStatus; }

卸载

Unload负责在驱动程序被停止前做一些必要的处理动作,如释放资源,记录最后状态等。

 VOID DriverUnLoad (IN PDRIVER_OBJECT DriverObject) {    if (DeviceObject) {   IoUnregisterFileSystem(DeviceObject);   IoDeleteDevice(DeviceObject);   //DeviceObject = 0; }

编译

驱动程序的编译需要使用DDK()中的build指令,它是一道命令行程序,一般会在后面加上参数:-ceZ。例如:

 C:\driver sample>build.exe -ceZ

DDK可用于创建用于 Windows 2000、Windows XP、Microsoft Windows Server 2003、Vista的建置环境,但在Windows操作系统并非默认的功能,必须另行安装。安装完成后你会看到Build Enviroment,free是指release版,check则是debug版。

build指令一开始调用Build.exe编译链接器,从系统“环境变量”(Environment Variable)Include中得到引用文件的地址,然后调用Visual C++的编译链接器Nmake.exe进行实际的编译链接工作。在编译过程中遇到的错误,遇到的警告,会记录到buildxxx.log,buildxxx.wrn,buildxxx.err等文件中。

安装过程分成两个步骤:

Microsoft DDK提供了大量的WDM示例(samples)参考,这些示例随着DDK的安装,会进驻我们的系统之中(WINDDK\xx00\src)。一般人不大可能从轮子造起一个新的驱动程序,大部分要靠“既有的示例”来改良一个新的驱动程序。

相关