转载请标明是引用于 http://blog.csdn.net/chenyujing1234
欢迎大家提出意见,一起讨论!
代码及EzDriverInstaller下载地址 : http://www.rayfile.com/zh-cn/files/9376d678-b9e1-11e1-9cc9-0015c55db73d/
(编译环境:VS2008+DDK库(参考:Window XP驱动开发(十六) XP下新建驱动程序工程并编译的第二种方法))
在驱动程序开发中,经常需要一个驱动程序调用另一个驱动程序。
例如:
虚拟串口转USB设备的驱动程序,这种驱动程序首先创建一个虚拟串口设备,对这个虚拟串口设备的读写请求会转到一个USB设备上去,
这时需要在虚拟串口驱动程序中调用USB驱动程序。
这种方法类似于在应用程序中调用驱动程序。这种方法使用简单,不需要程序员对Windows底层了解过多知识。
这个“目标”驱动程序创建一个模拟设备,模拟设备支持异步读取操作。事先规定每次对设备读取要耗时3S,因为这样可以很好地演示异步读取操作。
对于这样的驱动程序,应该设置一个定时器,定时器的间隔为3S。另外在IRP_MJ_READ的派遣函数中不结束IRP请求,而是将IRP请求挂起,并且在派遣函数
退出前开启定时器。这样3S后就会进入定时器的回调函数中,并在回调函数中结束IRP请求。
我们把这个“目标”驱动程序命名为DriverA,调用DriverA的驱动程序被命名为DriverB,以下列出DriverA的部分代码:
首先是DriverA的IRP_MJ_READ派遣函数。
/************************************************************************ * 函数名称:HelloDDKRead * 功能描述:对读IRP进行处理 * 参数列表: pDevObj:功能设备对象 pIrp:从IO请求包 * 返回 值:返回状态 *************************************************************************/ #pragma PAGEDCODE NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp) { KdPrint(("DriverA:Enter A HelloDDKRead\n")); PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION) pDevObj->DeviceExtension; //将IRP设置为挂起 IoMarkIrpPending(pIrp); //将挂起的IRP记录下来 pDevExt->currentPendingIRP = pIrp; //定义3秒后将IRP_MJ_READ的IRP完成 ULONG ulMicroSecond = 3000000; //将32位整数转化成64位整数 LARGE_INTEGER timeout = RtlConvertLongToLargeInteger(-10*ulMicroSecond); // 开启定时器 KeSetTimer( &pDevExt->pollingTimer, timeout, &pDevExt->pollingDPC ); KdPrint(("DriverA:Leave A HelloDDKRead\n")); //返回pending状态 return STATUS_PENDING; }
DriverA的超时回调DPC例程,此例程在CreateDevice创建设备时安装的。
// DriverA的超时回调DPC例程 VOID OnTimerDpc( IN PKDPC pDpc, IN PVOID pContext, IN PVOID SysArg1, IN PVOID SysArg2 ) { PDEVICE_OBJECT pDevObj = (PDEVICE_OBJECT)pContext; PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)pDevObj->DeviceExtension; PIRP currentPendingIRP = pdx->currentPendingIRP; KdPrint(("DriverA:complete the Driver A IRP_MJ_READ irp!\n")); //设置完成状态为STATUS_CANCELLED currentPendingIRP->IoStatus.Status = STATUS_SUCCESS; currentPendingIRP->IoStatus.Information = 0; // bytes xfered IoCompleteRequest( currentPendingIRP, IO_NO_INCREMENT ); }
/************************************************************************ * 函数名称:CreateDevice * 功能描述:初始化设备对象 * 参数列表: pDriverObject:从I/O管理器中传进来的驱动对象 * 返回 值:返回初始化状态 *************************************************************************/ #pragma INITCODE NTSTATUS CreateDevice ( IN PDRIVER_OBJECT pDriverObject) { NTSTATUS status; PDEVICE_OBJECT pDevObj; PDEVICE_EXTENSION pDevExt; //创建设备名称 UNICODE_STRING devName; RtlInitUnicodeString(&devName,L"\\Device\\MyDDKDeviceA"); //创建设备 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; // 分配定时器及设置超时回调例程 KeInitializeTimer( &pDevExt->pollingTimer ); KeInitializeDpc( &pDevExt->pollingDPC, OnTimerDpc, (PVOID) pDevObj ); //创建符号链接 UNICODE_STRING symLinkName; RtlInitUnicodeString(&symLinkName,L"\\??\\HelloDDKA"); pDevExt->ustrSymLinkName = symLinkName; status = IoCreateSymbolicLink( &symLinkName,&devName ); if (!NT_SUCCESS(status)) { IoDeleteDevice( pDevObj ); return status; } return STATUS_SUCCESS; }
以上就是准备的“目标”驱动程序DriverA ,为了演示DriverB调用DriverA,并使读者可以清楚log消息来源于哪个驱动程序,我们
让DriverA在输出log信息上加“DriverA:”这个前缀,而让DriverB在输出log信息之前加上“DriverB:”这个前缀。
DriverB有多种方法调用DriverA,这些方法可以是同步调用,也可以是异步调用。
;--------- 版本区域性 --------------------------------------------------- [Version] Signature="$CHICAGO$"; Provider=Zhangfan_DeviceA DriverVer=11/1/2007,3.0.0.3 ; 如果设备是一个标准类别,使用标准类别名称和GUI ; 否则创建一个自定义的类别名称,并自定义它的GUID Class=ZhangfanDeviceB ClassGUID={EF2962F0-0D55-4bff-B8AA-2221EE8A79B1} ;--------- 安装磁盘节 ----------------------- ; 这此节确定安装盘和安装文件的路径 ; 读者可以按照自己的需要修改 [SourceDisksNames] 1 = "HelloDDKA",Disk1,, [SourceDisksFiles] HelloDDKA.sys = 1,MyDriver_Check, ;--------- ClassInstall/ClassInstall32 Section ------------------------------- ; 如果使用标准类别设备,下面是不需要的 ; 9X Style [ClassInstall] Addreg=Class_AddReg ; NT Style [ClassInstall32] Addreg=Class_AddReg [Class_AddReg] HKR,,,,%DeviceClassName% HKR,,Icon,,"-5" ;--------- 目标文件节------------------------------------------- [DestinationDirs] YouMark_Files_Driver = 10,System32\Drivers [DefaultInstall] ;默认的安装。(即右击后点安装后的执行,有些人讲点安装了,可文件还是没有复制过去,就是这个没加的原因。) Copyfiles=YouMark_Files_Driver AddReg=YouMark_9X_AddReg ;--------- 制造商节 ---------------------------------- [Manufacturer] %MfgName%=Mfg0 [Mfg0] ; PCI hardware Ids use the form ; PCI\VEN_aaaa&DEV_bbbb&SUBSYS_cccccccc&REV_dd ;改成你自己的ID %DeviceDesc%=YouMark_DDI, PCI\VEN_9999&DEV_9999 ;---------- DDInstall Sections ----------------------------------------------- ; --------- Windows 9X ----------------- ; 如果在DDInstall中的字符串超过19,将会导致严重的问题 [YouMark_DDI] CopyFiles=YouMark_Files_Driver AddReg=YouMark_9X_AddReg [YouMark_9X_AddReg] HKR,,DevLoader,,*ntkern HKR,,NTMPDriver,,HelloDDKA.sys HKR, "Parameters", "BreakOnEntry", 0x00010001, 0 ; --------- Windows NT ----------------- [YouMark_DDI.NT] CopyFiles=YouMark_Files_Driver AddReg=YouMark_NT_AddReg [YouMark_DDI.NT.Services] Addservice = HelloDDKA, 0x00000002, YouMark_AddService [YouMark_AddService] DisplayName = %SvcDesc% ServiceType = 1 ; SERVICE_KERNEL_DRIVER StartType = 3 ; SERVICE_DEMAND_START ErrorControl = 1 ; SERVICE_ERROR_NORMAL ServiceBinary = %10%\System32\Drivers\HelloDDKA.sys [YouMark_NT_AddReg] HKLM, "System\CurrentControlSet\Services\HelloDDKA\Parameters",\ "BreakOnEntry", 0x00010001, 0 ; --------- 文件 (common) ------------- [YouMark_Files_Driver] HelloDDKA.sys ;--------- 字符串节--------------------------------------------------- [Strings] ProviderName="ZhangfanA." MfgName="Zhangfan SoftA" DeviceDesc="Hello World DDKA!" DeviceClassName="Zhangfan_DeviceA" SvcDesc="ZhangfanA"
(1) 通过ZwCreateFile,以设备名来打开
在驱动程序中,打开设备使用ZwCreateFile内核函数,它会返回设备句柄。这里要讨论一下如何用
ZwCreateFile内核函数打开“同步”设备和“异步”设备。ZwCreateFile内核函数声明如下:
NTSYSAPI NTSTATUS NTAPI ZwCreateFile( OUT PHANDLE FileHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, OUT PIO_STATUS_BLOCK IoStatusBlock, IN PLARGE_INTEGER AllocationSize OPTIONAL, IN ULONG FileAttributes, IN ULONG ShareAccess, IN ULONG CreateDisposition, IN ULONG CreateOptions, IN PVOID EaBuffer OPTIONAL, IN ULONG EaLength );
(2)
很多情况下,使用都不容易知道具体的设备名,而只知道符号链接。例如“C:"代表第一个硬件分区,而“C:”就是第一个符号链接,
它指向第一个磁盘分区设备。尤其在WDM驱动设备中,通过符号链接打开设备是经常遇到的。
利用ZwOpenSymbolicLinkObject内核函数先得到符号链接的句柄,
然后使用ZwQuerySymbolicLinkObject内核函数查找到设备名。
通过设备名就可以方便地打开设备。
NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp) { KdPrint(("DriverB:Enter B HelloDDKRead\n")); NTSTATUS ntStatus = STATUS_SUCCESS; UNICODE_STRING DeviceSymbolicLinkName; RtlInitUnicodeString( &DeviceSymbolicLinkName, L"\\??\\HelloDDKA" ); //初始化objectAttributes OBJECT_ATTRIBUTES objectAttributes; InitializeObjectAttributes(&objectAttributes, &DeviceSymbolicLinkName, OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE, NULL, NULL ); HANDLE hSymbolic; //设定了FILE_SYNCHRONOUS_IO_NONALERT或者FILE_SYNCHRONOUS_IO_ALERT为同步打开设备 ntStatus = ZwOpenSymbolicLinkObject(&hSymbolic,FILE_ALL_ACCESS,&objectAttributes); #define UNICODE_SIZE 50 UNICODE_STRING LinkTarget; LinkTarget.Buffer = (PWSTR)ExAllocatePool(PagedPool,UNICODE_SIZE); LinkTarget.Length = 0; LinkTarget.MaximumLength = UNICODE_SIZE; ULONG unicode_length; // 通过符号链接得到设备名 ntStatus = ZwQuerySymbolicLinkObject(hSymbolic,&LinkTarget,&unicode_length); KdPrint(("DriverB:The device name is %wZ\n",&LinkTarget)); // 构造objectAttributes InitializeObjectAttributes(&objectAttributes, &LinkTarget, OBJ_CASE_INSENSITIVE, NULL, NULL ); HANDLE hDevice; IO_STATUS_BLOCK status_block; //设定了FILE_SYNCHRONOUS_IO_NONALERT或者FILE_SYNCHRONOUS_IO_ALERT为同步打开设备 ntStatus = ZwCreateFile(&hDevice, FILE_READ_ATTRIBUTES|SYNCHRONIZE, &objectAttributes, &status_block, NULL,FILE_ATTRIBUTE_NORMAL,FILE_SHARE_READ, FILE_OPEN_IF,FILE_SYNCHRONOUS_IO_NONALERT,NULL,0); // 判断是否成功打开设备 if (NT_SUCCESS(ntStatus)) { ZwReadFile(hDevice,NULL,NULL,NULL,&status_block,NULL,0,NULL,NULL); } ZwClose(hDevice); ZwClose(hSymbolic); ExFreePool(LinkTarget.Buffer); ntStatus = STATUS_SUCCESS; // 完成IRP pIrp->IoStatus.Status = ntStatus; pIrp->IoStatus.Information = 0; // bytes xfered IoCompleteRequest( pIrp, IO_NO_INCREMENT ); KdPrint(("DriverB:Leave B HelloDDKRead\n")); return ntStatus; }
(1)如果打开“同步”设备,第二个参数DesiredAccess需要设置为SYNCHRONIZE,
且倒数第三个参数CreateOptions需要指定为FILE_SYNCHRONOUS_IO_NONALERT或FILE_SYNCHRONOUS_IO_ALERT。
(2) 如果打开“异步”设备,第二个参数DesiredAccess不能设置为SYNCHRONIZE,并且倒数第三个参数CreateOptions 不能指定为
FILE_SYNCHRONOUS_IO_NONALERT或FILE_SYNCHRONOUS_IO_ALERT。
//异步打开设备 //没有设定FILE_SYNCHRONOUS_IO_NONALERT或者FILE_SYNCHRONOUS_IO_ALERT为异步打开设备 ntStatus = ZwCreateFile(&hDevice, FILE_READ_ATTRIBUTES, &objectAttributes, &status_block, NULL,FILE_ATTRIBUTE_NORMAL,FILE_SHARE_READ, FILE_OPEN_IF,0,NULL,0);
在应用程序中,ReadFile函数既可以来读取文件,又可以来读取设备。同样,在驱动程序中,ZwReadFile内核函数既可以来读文件,也可以来读设备。
如果用ZwReadFile内核函数同步读取设备,操作对应的IRP_MJ_READ请求被结束后,函数才会返回。否则这个函数会一直等待IRP_MJ_READ请求被结束。
如果用ZwReadFile内核函数异步读取设备,即使操作对应的IRP_MJ_READ请求没有被结束,函数也会立即返回。
本节介绍的DriverB是利用同步读取的方法调用DriverA,如用ZwReadFile内核函数同步读取DriverA的设备,它的内部操作过程如下:
(1)在DriverB中用ZwReadFile内核函数读取DriverA的设备对象。ZwReadFile内核函数内部会创建IRP_MJ_READ类型的IRP,然后将这个IRP
当做参数传递给DriverA的派遣函数。
(2)DriverA的派遣函数没有结束IRP请求,而是将IRP请求“挂起”。
(3)ZwReadFile函数会一直等待IRP中的一个事件,此时当前线程进入睡眠状态。
(4)3S后,触发DriverA 的定时器例器,这里IRP请求被结束,IRP中的相关事件也被设置。
(5)由于相应事件被设置,刚才休眠的线程恢复运行,ZwReadFile内核函数退出。
我们是采用同步打开设备的方式。
NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp) { KdPrint(("DriverB:Enter B HelloDDKRead\n")); NTSTATUS ntStatus = STATUS_SUCCESS; UNICODE_STRING DeviceName; RtlInitUnicodeString( &DeviceName, L"\\Device\\MyDDKDeviceA" ); //初始化objectAttributes OBJECT_ATTRIBUTES objectAttributes; InitializeObjectAttributes(&objectAttributes, &DeviceName, OBJ_CASE_INSENSITIVE, NULL, NULL ); HANDLE hDevice; IO_STATUS_BLOCK status_block; //同步打开设备 //设定了FILE_SYNCHRONOUS_IO_NONALERT或者FILE_SYNCHRONOUS_IO_ALERT为同步打开设备 ntStatus = ZwCreateFile(&hDevice, FILE_READ_ATTRIBUTES|SYNCHRONIZE, &objectAttributes, &status_block, NULL,FILE_ATTRIBUTE_NORMAL,FILE_SHARE_READ, FILE_OPEN_IF,FILE_SYNCHRONOUS_IO_NONALERT,NULL,0); if (NT_SUCCESS(ntStatus)) { ZwReadFile(hDevice,NULL,NULL,NULL,&status_block,NULL,0,NULL,NULL); } ZwClose(hDevice); // 结束来自应用程序的IRP请求。完成IRP pIrp->IoStatus.Status = ntStatus; pIrp->IoStatus.Information = 0; // bytes xfered IoCompleteRequest( pIrp, IO_NO_INCREMENT ); KdPrint(("DriverB:Leave B HelloDDKRead\n")); return ntStatus; }
另外,DriverB还需要做应用程序的客户端,因此DriverB的派遣函数还需要结束来自应用程序的IRP请求。
int main() { HANDLE hDevice = CreateFile("\\\\.\\HelloDDKB", GENERIC_READ | GENERIC_WRITE, 0, // share mode none NULL, // no security OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); // no template if (hDevice == INVALID_HANDLE_VALUE) { printf("Failed to obtain file handle to device " "with Win32 error code: %d\n", GetLastError() ); return 1; } DWORD dRet; ReadFile(hDevice,NULL,0,&dRet,NULL); CloseHandle(hDevice); return 0; }
;--------- 版本区域性 --------------------------------------------------- [Version] Signature="$CHICAGO$"; Provider=Zhangfan_DeviceB DriverVer=11/1/2007,3.0.0.3 ; 如果设备是一个标准类别,使用标准类别名称和GUI ; 否则创建一个自定义的类别名称,并自定义它的GUID Class=ZhangfanDeviceB ClassGUID={EF3962F0-1D55-4bff-B8AA-2221EE8A79B3} ;--------- 安装磁盘节 ----------------------- ; 这此节确定安装盘和安装文件的路径 ; 读者可以按照自己的需要修改 [SourceDisksNames] 1 = "HelloDDKB",Disk2,, [SourceDisksFiles] HelloDDKB.sys = 1,MyDriver_Check, ;--------- ClassInstall/ClassInstall32 Section ------------------------------- ; 如果使用标准类别设备,下面是不需要的 ; 9X Style [ClassInstall] Addreg=Class_AddReg ; NT Style [ClassInstall32] Addreg=Class_AddReg [Class_AddReg] HKR,,,,%DeviceClassName% HKR,,Icon,,"-5" ;--------- 目标文件节------------------------------------------- [DestinationDirs] YouMark_Files_Driver = 10,System32\Drivers [DefaultInstall] ;默认的安装。(即右击后点安装后的执行,有些人讲点安装了,可文件还是没有复制过去,就是这个没加的原因。) Copyfiles=YouMark_Files_Driver AddReg=YouMark_9X_AddReg ;--------- 制造商节 ---------------------------------- [Manufacturer] %MfgName%=Mfg0 [Mfg0] ; PCI hardware Ids use the form ; PCI\VEN_aaaa&DEV_bbbb&SUBSYS_cccccccc&REV_dd ;改成你自己的ID %DeviceDesc%=YouMark_DDI, PCI\VEN_1189&DEV_2398 ;---------- DDInstall Sections ----------------------------------------------- ; --------- Windows 9X ----------------- ; 如果在DDInstall中的字符串超过19,将会导致严重的问题 [YouMark_DDI] CopyFiles=YouMark_Files_Driver AddReg=YouMark_9X_AddReg [YouMark_9X_AddReg] HKR,,DevLoader,,*ntkern HKR,,NTMPDriver,,HelloDDKB.sys HKR, "Parameters", "BreakOnEntry", 0x00010002, 0 ; --------- Windows NT ----------------- [YouMark_DDI.NT] CopyFiles=YouMark_Files_Driver AddReg=YouMark_NT_AddReg [YouMark_DDI.NT.Services] Addservice = HelloDDKB, 0x00000003, YouMark_AddService [YouMark_AddService] DisplayName = %SvcDesc% ServiceType = 1 ; SERVICE_KERNEL_DRIVER StartType = 3 ; SERVICE_DEMAND_START ErrorControl = 1 ; SERVICE_ERROR_NORMAL ServiceBinary = %10%\System32\Drivers\HelloDDKB.sys [YouMark_NT_AddReg] HKLM, "System\CurrentControlSet\Services\HelloDDKB\Parameters",\ "BreakOnEntry", 0x00010001, 0 ; --------- 文件 (common) ------------- [YouMark_Files_Driver] HelloDDKB.sys ;--------- 字符串节--------------------------------------------------- [Strings] ProviderName="ZhangfanB." MfgName="Zhangfan SoftB" DeviceDesc="Hello World DDKB!" DeviceClassName="Zhangfan_DeviceB" SvcDesc="ZhangfanB"
由于DriverA与DriverB都是虚拟设备,通过设备管理器里的硬件更新向导加载驱动是可以的。
但是为了方便我们通过EzDriverInstaller工具。它是DriverStudio 自带的一个工具软件,用于快速安装WDM程序。
(1)打开EzDriverInstaller,选择“File”|“Open”,在弹出的对话框中,选择需要的inf文件。
(2)然后点击“Add New Device”。
此时就会提示成功,并在列表中显示我们的设备名:
在DriverA装载成功后用DebugView查看打印信息:
打开我们的设备管器看看效果吧:
(3) 运行应用程序,用DebugView查看打印信息。
注:网上很多人反映KdPrint打印不能在DebugView上显示出来。开始我也怀疑KdPrint这个函数不对。
后来才知道是DebugView没设置好。确保Capture菜单下的Log Boot选择已经勾选。
异步读取主要是指ZwReadFile内核函数没有等待DriverA真正结束IRP请求时,就已经退出。如果用ZwReadFile内核函数异步读取DriverA的设备,
它的内部操作过程如下:
(1)ZwReadFile内核函数内部创建IRP_MJ_READ类型的IRP,然后将这个IRP传递给DriverA的派遣函数。
(2)DriverA派遣函数没有结束IRP请求,而是将IRP"挂起”
(3)ZwReadFile内核函数发现DriverA将IRP_MJ_READ“挂起”,于是它直接返回,返回值是STATUS_PENDING,这就代表读取操作正在进行。
ZwReadFile内核函数退出后,无法得到“挂起”的IRP何时被结束。
因此在调用ZwReadFile内核函数前,可以为IRP设置一个完成例程。当IRP结束时触发这个完成例程。
NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp) { KdPrint(("DriverB:Enter B HelloDDKRead\n")); NTSTATUS ntStatus = STATUS_SUCCESS; UNICODE_STRING DeviceName; RtlInitUnicodeString( &DeviceName, L"\\Device\\MyDDKDeviceA" ); //初始化objectAttributes OBJECT_ATTRIBUTES objectAttributes; InitializeObjectAttributes(&objectAttributes, &DeviceName, OBJ_CASE_INSENSITIVE, NULL, NULL ); HANDLE hDevice; IO_STATUS_BLOCK status_block; //异步打开设备 //没有设定了FILE_SYNCHRONOUS_IO_NONALERT和FILE_SYNCHRONOUS_IO_ALERT为异步打开设备 ntStatus = ZwCreateFile(&hDevice, FILE_READ_ATTRIBUTES,//没有设SYNCHRONIZE &objectAttributes, &status_block, NULL,FILE_ATTRIBUTE_NORMAL,FILE_SHARE_READ, FILE_OPEN_IF,0,NULL,0); KEVENT event; //初始化事件,用于异步读 KeInitializeEvent(&event,SynchronizationEvent,FALSE); LARGE_INTEGER offset = RtlConvertLongToLargeInteger(0); if (NT_SUCCESS(ntStatus)) { // 用ZwReadFile函数进行异步读取 ntStatus = ZwReadFile(hDevice,NULL,CompleteDriverA_Read,&event,&status_block,NULL,0,&offset,NULL); } // 判断ZwReadFile函数是否返回STATUS_PENDING if (ntStatus==STATUS_PENDING) { KdPrint(("DriverB:ZwReadFile return STATUS_PENDING!\n")); KdPrint(("DriverB:Waiting...")); // 等待IRP被结束 KeWaitForSingleObject(&event,Executive,KernelMode,FALSE,NULL); } // 关闭设备句柄 ZwClose(hDevice); ntStatus = STATUS_SUCCESS; // 设置IRP完成状态 pIrp->IoStatus.Status = ntStatus; pIrp->IoStatus.Information = 0; // bytes xfered // 结束IRP请求 IoCompleteRequest( pIrp, IO_NO_INCREMENT ); KdPrint(("DriverB:Leave B HelloDDKRead\n")); return ntStatus; }
以下是完成例程的代码。完成例程主要是用一个事件通知IRP请求结束。
VOID CompleteDriverA_Read(PVOID context,PIO_STATUS_BLOCK pStatus_block,ULONG) { KdPrint(("DriverB:The Driver A Read completed now!\n")); KeSetEvent((PKEVENT)context,IO_NO_INCREMENT,FALSE); }
第二种异步方法和每一种异步方法略有不同。第一种异步读取时,将一个事件的句柄传递给ZwReadFile内核函数,这个事件可以用来通知读取操作何时完成。
而这里介绍的方法不用将事件句柄传递给ZwCreateFile 函数,而是将通过文件对象判断读取是否完毕。
每打开一个设备,都会伴随存在一个关联的文件对象(FILE_OBJECT)。利用内核函数ObReferenceObjectByHandle可以获得和设备相关的文件对象指针。
当IRP_MJ_READ请求被结束后,文件对象的子域Event会被设置,因此用文件对象的Event子域可以当做同步点使用。
这里利用文件对象的子域Event,即FileObject->Event作为同步点。以下是代码:
NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp) { KdPrint(("DriverB:Enter B HelloDDKRead\n")); NTSTATUS ntStatus = STATUS_SUCCESS; UNICODE_STRING DeviceName; RtlInitUnicodeString( &DeviceName, L"\\Device\\MyDDKDeviceA" ); //初始化objectAttributes OBJECT_ATTRIBUTES objectAttributes; InitializeObjectAttributes(&objectAttributes, &DeviceName, OBJ_CASE_INSENSITIVE, NULL, NULL ); HANDLE hDevice; IO_STATUS_BLOCK status_block; //异步打开设备 ntStatus = ZwCreateFile(&hDevice, FILE_READ_ATTRIBUTES,//没有设SYNCHRONIZE &objectAttributes, &status_block, NULL,FILE_ATTRIBUTE_NORMAL,FILE_SHARE_READ, FILE_OPEN_IF,0,NULL,0); LARGE_INTEGER offset = RtlConvertLongToLargeInteger(0); if (NT_SUCCESS(ntStatus)) { ntStatus = ZwReadFile(hDevice,NULL,NULL,NULL,&status_block,NULL,0,&offset,NULL); } if (ntStatus==STATUS_PENDING) { KdPrint(("DriverB:ZwReadFile return STATUS_PENDING!\n")); PFILE_OBJECT FileObject; // 通过设备句柄找到文件对象指针 ntStatus = ObReferenceObjectByHandle(hDevice, EVENT_MODIFY_STATE, *ExEventObjectType, KernelMode, (PVOID*) &FileObject, NULL); if (NT_SUCCESS(ntStatus)) { KdPrint(("DriverB:Waiting...")); // 等待文件对象中的事件 KeWaitForSingleObject(&FileObject->Event,Executive,KernelMode,FALSE,NULL); KdPrint(("DriverB:Driver A Read IRP completed now!\n")); // 减少文件对象的引用计数 ObDereferenceObject(FileObject); } } ZwClose(hDevice); ntStatus = STATUS_SUCCESS; // 完成IRP pIrp->IoStatus.Status = ntStatus; pIrp->IoStatus.Information = 0; // bytes xfered IoCompleteRequest( pIrp, IO_NO_INCREMENT ); KdPrint(("DriverB:Leave B HelloDDKRead\n")); return ntStatus; }