在上一篇文章里我们写了第一个驱动程序-HelloWorld, 今天我们来完善它,主要完成两个功能:添加一个驱动设备与给驱动添加默认派遣(IRP)。
首先我们来完成第一个功能:添加一个驱动设备。
代码如下:
NTSTATUS CreateTheDevice(IN PDRIVER_OBJECT pDeviceObject) { NTSTATUS status; PDEVICE_OBJECT pDevObj; UNICODE_STRING devName; UNICODE_STRING symLinkName; RtlInitUnicodeString(&devName, L"\\Device\\first_Device"); //create device status = IoCreateDevice(pDeviceObject,\ 0,\ &devName,\ FILE_DEVICE_UNKNOWN,\ 0,\ TRUE,\ &pDevObj\ ); if (!NT_SUCCESS(status)) { if (STATUS_INSUFFICIENT_RESOURCES == status) { KdPrint(("资源不足")); } if (STATUS_OBJECT_NAME_EXISTS == status) { KdPrint(("指定对象名存在")); } if (STATUS_OBJECT_NAME_COLLISION == status) { KdPrint(("//对象名有冲突")); } KdPrint(("设备创建失败")); return status; } KdPrint(("创建设备成功")); pDevObj->Flags |= DO_BUFFERED_IO; //创建符号链接 RtlInitUnicodeString(&symLinkName,L"\\??\\firstSymDevice"); status = IoCreateSymbolicLink( &symLinkName,&devName ); if (!NT_SUCCESS(status)) /*status等于0*/ { IoDeleteDevice( pDevObj ); return status; } return STATUS_SUCCESS; }
这样就可以给我们的驱动加入一个设备,驱动开发是unicode的方式,所以不能直接用用户态的API去处理字符串,必须用Rtl开头的内核函数,如上面RtlInitUnicodeString就是给第一个参数初始化。
第二个功能是关于IRP的,什么叫IRP(I/O Request Package).用户模式下所有对驱动的I/O请求,全部由操作系统转化为一个叫着IRP的数据结构,不同的IRP请求会被“派遣”到不同的派遣函数中。
有五种常用IRP类型,分别是:
#define IRP_MJ_CREATE 0x00 //CreateFile() #define IRP_MJ_CLOSE 0x02 //CloseHandle() #define IRP_MJ_READ 0x03//ReadFile #define IRP_MJ_WRITE 0x04//WriteFile #define IRP_MJ_DEVICE_CONTROL 0x0e//DeviceIoControl
步骤是:
1.创建IRP处理函数
2.在驱动入口外注册IRP处理发函数
3.实现IRP处理函数
有两种方式注册IRP派遣函数,第一种是只有一个派遣函数,在该派遣函数内分别对根据IRP类型做不同的处理,这种方式代码如下:
//注册派遗函数 pDriverObject->MajorFunction[IRP_MJ_CREATE]=ddk_DispatchRoutine; //注册派遗函数 pDriverObject->MajorFunction[IRP_MJ_CLOSE]=ddk_DispatchRoutine; //注册派遗函数 pDriverObject->MajorFunction[IRP_MJ_READ]=ddk_DispatchRoutine; //注册派遗函数 pDriverObject->MajorFunction[IRP_MJ_WRITE]=ddk_DispatchRoutine; //注册派遗函数 pDriverObject->MajorFunction[ IRP_MJ_DEVICE_CONTROL]=ddk_DispatchRoutine; NTSTATUS ddk_DispatchRoutine(IN PDEVICE_OBJECT pDevobj,IN PIRP pIrp ) { PIO_STACK_LOCATION irpsp=IoGetCurrentIrpStackLocation(pIrp); switch (irpsp->MajorFunction) { case IRP_MJ_CREATE: break; case IRP_MJ_CLOSE: break; case IRP_MJ_READ: break; case IRP_MJ_WRITE: break; case IRP_MJ_DEVICE_CONTROL: break; default: KdPrint(("其它处理")); //指示完成此IRP } //成功返回 return STATUS_SUCCESS; }
第二种是对每一种类型的IRP注册一个派遣函数,代码如下:
//注册派遗函数 pDriverObject->MajorFunction[IRP_MJ_CREATE]=ddk_DispatchRoutine_CREATE; //注册派遗函数 pDriverObject->MajorFunction[IRP_MJ_CLOSE]=ddk_DispatchRoutine_CLOSE; //注册派遗函数 pDriverObject->MajorFunction[IRP_MJ_READ]=ddk_DispatchRoutine_READ; //注册派遗函数 pDriverObject->MajorFunction[IRP_MJ_WRITE]=ddk_DispatchRoutine_WRITE; //注册派遗函数 pDriverObject->MajorFunction[ IRP_MJ_DEVICE_CONTROL]=ddk_DispatchRoutine_CONTROL; NTSTATUS ddk_DispatchRoutine_CONTROL(IN PDEVICE_OBJECT pDevobj,IN PIRP pIrp ) { //对相应的IPR进行处理 pIrp->IoStatus.Information=0;//设置操作的字节数为0,这里无实际意义 pIrp->IoStatus.Status=STATUS_SUCCESS;//返回成功 IoCompleteRequest(pIrp,IO_NO_INCREMENT);//指示完成此IRP KdPrint(("离开派遣函数\n"));//调试信息 return STATUS_SUCCESS; //返回成功 }
到此代码介绍完毕, 记得在卸载驱动的时候,要卸载设备,不然下次加载驱动的时候,因为设备没有卸载而不能成功创建设备。请参看完整的源码 。