/****************************************************************
* 函数名称:CreateDrivce
* 功能描述:初始化设备对象
* 参数列表:
pDriverObject:从I/O管理器中传进来的驱动对象
* 返回值:返回初始化状态
****************************************************************/
#pragma INITCODE
NTSTATUS CreateDeivce( 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;
}
这个函数是在DriverEntry入口函数中执行的,该函数使用了5个数据类型,分别是PDRIVER_OBJECT、NTSTATUS、PDEVICE_OBJECT、PDEVICE_EXTENSION和UNICODE_STRING。其中PDRIVER_OBJECt是驱动对象,NTSTATUS是函数返回状态,PDEVICE_OBJECt是设备对象,PDEVICE_EXTENSION是头文件中自定义的一个结构体。该函数使用了四个内核函数,分别是RtlInitUnicodeString、IoCreateDevice、IoCreateSymbolicLink和IoDeleteDevice。
其中几个数据类型在第一篇笔记中差不多都已经谈到了,除了PDEVICE_EXTENSION这个结构体,该结构体定义的是设备对象扩展,其定义如下:
typedef struct _DEVICE_EXTENSION
{
PDEVICE_OBJECT pDevice;
UNICODE_STRING ustrDeviceName; //设备名称
UNICODE_STRING ustrSymLinkName; //符号链接名
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
该结构体只有三个成员,pDevice是设备对象,该成员的值由IoCreateDevice函数获得;ustrDeviceName是创建的设备名称;ustrSymLinkName是符号链接名。
接下来就是在驱动程序中第一次接触的几个内核函数,RtlInitUnicodeString函数是用来给UNICODE_STRING字符串赋值的;IoCreateDevice是用来创建设备对象的,在此有必要熟悉熟悉IoCreateDevice函数:
NTKERNELAPI
NTSTATUS
IoCreateDevice(
__in PDRIVER_OBJECT DriverObject,
__in ULONG DeviceExtensionSize,
__in_opt PUNICODE_STRING DeviceName,
__in DEVICE_TYPE DeviceType,
__in ULONG DeviceCharacteristics,
__in BOOLEAN Exclusive,
__out
__drv_out_deref(
__drv_allocatesMem(Mem)
__drv_when((((inFunctionClass$("DRIVER_INITIALIZE"))
||(inFunctionClass$("DRIVER_DISPATCH")))),
__drv_aliasesMem)
__on_failure(__null))
PDEVICE_OBJECT *DeviceObject
);
IoCreateDevice函数共有7个参数,包含6个输入函数和1个输出函数。其中DriverObject是驱动对象实例句柄;DeviceExtensionSize是自定义设备对象扩展的大小;DeviceName是设备对象名称,这个名称可以不设置,系统会自动为其命名;
DeviceType是一个比较重要的参数,该参数可以指定添加的驱动是即插即用设备还是非即插即用设备,对于非即插即用设备,应设置为FILE_DEVICE_UNKNOWN;DeviceCharacteristics为设置设备对象特征;Exclusive设置设备对象在内核模式下是否可用,一般为TRUE;最后一个参数是DeviceObject,该参数返回设备对象的地址。
现在要初始化设备对象扩展,在该扩展中指定当前设备对象的指针和设备对象名称,代码如下:
pDevObj->Flags |= DO_BUFFERED_IO;
pDevExt = ( PDEVICE_EXTENSION ) pDevObj->DeviceExtension;
pDevExt->pDevice = pDevObj;
pDevExt->ustrDeviceName = devName;
pDevObj为设备对象指针,“pDevObj->Flags |= DO_BUFFERED_IO;”是将设备设置为缓冲区设备,接着获取设备扩展指针赋值到pDevExt中,并设置设备对象扩展的设备对象及设备对象名称。
最后是创建符号链接,这如同给对象设备一个别名,以便记忆和识别。
//创建符号链接
UNICODE_STRING symLinkName;
RtlInitUnicodeString( &symLinkName, L"\\??\\HelloDDK" );
pDevExt->ustrSymLinkName = symLinkName;
status = IoCreateSymbolicLink( &symLinkName, &devName );
if ( !NT_SUCCESS( status ) )
{
IoDeleteDevice( pDevObj );
return status;
}
IoCreateSymbolicLink函数的第一个参数是符号链接名称,第二个参数是设备对象名称。
至此,DriverEntry入口函数中的功能均已实现,其大致顺序如下:
第一步:初始化驱动对象,设置IRP派遣函数和驱动卸载函数
第二步:创建设备对象。这一步可细分为创建设备对象、设置设备对象扩展、创建设备对象符号链接。
由于驱动程序对线程、硬件等的操作与文件操作类似,都包括打开、关闭、取消和退出等操作,所以也使用IO方式来进行控制,而在IRP派遣函数中就包含了这些特性。
在这第一个驱动程序中,只有一个IRP派遣函数,即HelloDDKDispatchRuntine,对于派遣函数的作用及设置将在另外一篇笔记中详细整理。