以下均为个人见解,如果有误,敬请指正,谢谢
Windows驱动程序的两个重要的数据结构,驱动设备对象DRIVER_OBJECT、设备对象结构DEVICE_OBJECT,这里简略了两个结构体中的成员,具体的网上都是,自己去搜吧
1、typedef struct _DRIVER_OBJECT{
PDEVICE_OBJECT DeviceObject;/*该设备对象是由程序员自己生成,非OS生成,该设备对象为驱动对象指向的第一个设备对象,设备对象的结构体中有NextDevice,所以可以通过NextDevice指针遍历设备链,卸载的时候也是通过NextDevice指针删除设备对象的,最后一个设备对象为空。*/
UNICODE_STRING DRIVER_NAME;/*驱动名称*/
PDRIVER_UNLOAD DriverUnLoad;/*驱动卸载时的回调函数*/
PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1];/*函数指针数组,每个指针对用相对应的IRP处理函数*/
}DRIVER_OBJECT;
2、typedef struct _DEVICE_OBJECT{
struct _DRIVER_OBJECT *DriverObject;/*驱动对象*/
struct _DEVICE_OBJECT *NextDevice;/*指向同一层驱动的下一个设备对象*/
struct _DEVICE_OBJECT *AttachedDevice;/*指向高一层驱动的下一个设备对象*/
struct _DEVOBJ_EXTENSION *DeviceObjectExtension;/*设备扩展对象*/
ULONG Flags, DeviceType;/*DeviceType为设备类型,Flags为设备的标志位,每个标志位都有特定含义,Flags=DO_BUFFERED_IO:读写操作采用缓冲方式(系统赋值缓冲区)访问用户模式数据,Flags=DO_EXCLUSIVE:一次只允许一个线程打开设备句柄,Flags=DO_DIRECT_IO:读写操作使用直接方式(内存描述符表)访问用户模式数据,Flags=DO_DEVICE_INITIALIZING:设备对象正在初始化,Flags=DO_POWER_PAGEABLE:必须在PASSIVE_LEVEL级上处理IRP_MJ_PNP请求,Flags=DO_POWER_INRUSH:设备上电期间需要大电流*/
}DEVICE_OBJECT;
将驱动对象和设备对象联合起来,在参照代码看更好理解(以下为我个人在写NT驱动的设备扩展的结构):
typedef struct _DEVICE_EXTENSION
{
PDEVICE_OBJECT pDevice;//设备对象
UNICODE_STRING ustrDeviceName;//设备名称
UNICODE_STRING ustrSymLinkName;//符号链接名
}DEVICE_EXTENSION, *PDEVICE_EXTENSION;
NT驱动简单些,主要DRIVER_ENTRY, DRIVER_UNLOAD和IRP的处理函数,主线很明了。
#pragma INITCODE
extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject,
IN PUNICODE_STRING pRegistryPath)
{
NTSTATUS status;
KdPrint(("Entry DriverEntry\n"));
//注册其他驱动调用入口函数
pDriverObject->DriverUnload = HelloDDKUnload;
pDriverObject->MajorFunction[IRP_MJ_CREATE] =
pDriverObject->MajorFunction[IRP_MJ_CLOSE] =
pDriverObject->MajorFunction[IRP_MJ_WRITE] =
pDriverObject->MajorFunction[IRP_MJ_READ] = HelloDDKDispatchRoutine;
//创建驱动设备对象
status = CreateDevice(pDriverObject);
status = CreateDevice2(pDriverObject);
Dump(pDriverObject);
KdPrint(("DriverEntry end\n"));
return status;
}
上面为驱动的入口函数,其中INITCODE为DDK提供的宏,表明为加载的时候,加载完毕就不存在内存中。第二个形参为设备服务键的键名字符串指针,需要保存起来。这里主要讲解一下创建驱动设备对象,别的没有什么说的了,Dump函数是用来查看调试信息的,有无不影响代码的运行,这里讲代码贴出:
#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;
}
由IoCreateDevice函数返回设备对象指针(地址),设置了设备对象的Flags和扩展设备对象的成员,在内核模式下的符号链接是以"\??\"开头,区别于用户模式下的"\\.\"。
然后再看看卸载历程,代码如下:
#pragma PAGECODE
void HelloDDKUnload(IN PDRIVER_OBJECT pDriverObject)
{
PDEVICE_OBJECT pNextObj;
KdPrint(("Enter DriverUnload\n"));
pNextObj = pDriverObject->DeviceObject;
while(pNextObj != NULL)
{
PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pNextObj->DeviceExtension;
//删除符号链接
UNICODE_STRING pLinkName = pDevExt->ustrSymLinkName;
IoDeleteSymbolicLink(&pLinkName);
pNextObj = pNextObj->NextDevice;
IoDeleteDevice(pDevExt->pDevice);
}
}
注意宏PAGECODE为分页内存,在这里值讨论了同一层驱动的设备链,代码体现出卸载删除了符号链接和设备对象。