Window XP驱动开发(十五) 驱动程序调用驱动程序(以文件句柄形式)

2023-11-05

转载请标明是引用于 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驱动程序。

 

1、 以文件句柄形式调用其他驱动程序

这种方法类似于在应用程序中调用驱动程序。这种方法使用简单,不需要程序员对Windows底层了解过多知识。

 

1、1  准备一个标准驱动DriverA

这个“目标”驱动程序创建一个模拟设备,模拟设备支持异步读取操作。事先规定每次对设备读取要耗时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,这些方法可以是同步调用,也可以是异步调用。

 

1、1、1  DriverA的安装文件Inf的书写
;--------- 版本区域性 ---------------------------------------------------

[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、2   以文件句柄形式调用其他驱动程序的知识点

1、2、1 获得设备句柄的两种方法

(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、2、2  打开方式的两种方式

现在我们介绍打开“同步”和“异步”设备的区别。

(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);


1、3   以文件句柄形式调用的第一种方法:同步调用

在应用程序中,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内核函数退出。

1、3、1   DriverB设计

我们是采用同步打开设备的方式。

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;
}

 

1、3、2 应用程序设计

 

另外,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;
}


 

1、3、3  DriverB的安装文件inf的书写 
;--------- 版本区域性 ---------------------------------------------------

[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"

1、3、4   加载驱动测试

 由于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选择已经勾选。

 

1、4   以文件句柄形式调用的第二种方法:异步调用

1、4、1 异步调用方法一

异步读取主要是指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);
}


 

 

1、4、2 异步调用方法二

第二种异步方法和每一种异步方法略有不同。第一种异步读取时,将一个事件的句柄传递给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;
}


 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Window XP驱动开发(十五) 驱动程序调用驱动程序(以文件句柄形式) 的相关文章

  • 使用 C++ 删除文本文件中重复行的内存有效方法

    使用 C 删除大型文本文件中的重复行的最有效内存方法是什么 让我澄清一下 我不是要求代码 只是最好的方法 不保证重复的行是相邻的 我意识到针对最小内存使用进行优化的方法会导致速度变慢 但这是我的限制 因为文件太大 我会对每一行进行散列 然后
  • 使用 PHP 创建 .jpg 文件的下载链接

    我想这应该很容易 我有一个分页图像库 每个图像下方都有一个小链接 上面写着 下载 Comp 这应该允许人们快速将 jpg 文件 带有 PHP 生成的水印 下载到他们的计算机上 现在 我知道我可以直接链接到 jpg 文件 但这需要用户在新窗口
  • 获取文件哈希性能/优化

    我正在尝试尽快获取文件的哈希值 我有一个程序 可以对大量数据 100GB 进行哈希处理 这些数据由随机文件大小 每个文件从几KB到5GB 组成 跨少量文件到数十万个文件 该程序必须支持所有 Java 支持的算法 MD2 MD5 SHA 1
  • 我想向我的销售点的用户授予特权,但我不知道如何做,有什么建议吗?

    我有一个问题 我有一个用netBeans制作的销售点系统 2个用户可以在我的系统中注册 管理员 和 供应商 系统中有几个模块 包括 Inventario 模块允许您在数据库中输入 修改 删除和搜索文章 问题是我希望只有管理员有权限进入该模块
  • 在android中根据文件的创建日期对文件进行排序

    我想要基于我的创建日期的文件列表 当我更新任何图像并尝试检索所有图像时 订单会随机更改 这是我的代码 File files parentDir listFiles for File file files I am getting files
  • Python读取文件内容[重复]

    这个问题在这里已经有答案了 在Python中 如何像Java中的InputStream一样只读取文件内容 不包括属性和文件名 我需要一种适用于各种文件格式的方法 我试过这个 with open filePath rb as imageFil
  • 区分大小写 Directory.Exists / File.Exists

    有没有办法区分大小写Directory Exists File Existssince Directory Exists folderPath and Directory Exists folderPath ToLower 都返回true
  • 在 Vim 中将 DOS/Windows 行结尾转换为 Linux 行结尾

    如果我打开在 Windows 中创建的文件 所有行都以 M 如何一次性删除这些字符 dos2unix https sourceforge net projects dos2unix是一个可以执行此操作的命令行实用程序 In Vim s M
  • 逐行读取文件而不是逐字读取文件

    我正在尝试编写一些代码来扫描输入文件中的回文 但它从每个单词而不是每行获取字符串 一个例子是赛车会显示为racecar 回文或太热而不能叫 回文 但相反它会显示为too 不是回文 hot 不是回文等等 这是我当前正在执行的读取文件的操作 F
  • 获取实体的请求。attribute == @"somevalue"

    如何设置提取请求以仅从具有一个特定值的实体属性中提取数据 这是我之前使用过的基本代码 void fetchResults NSFetchRequest fetchRequest NSFetchRequest fetchRequestWith
  • 如何在 SQL 中替换 PIVOT 中的 Null 值

    我有以下代码 我试图用零替换使用枢轴时出现的 Null 我执行了以下操作 但它说 ISNULL 附近的语法不正确 我不确定我做错了什么 有什么建议请 select from tempfinaltable pivot ISNULL sum T
  • 如何在 C 中对 .txt 文件内的数据进行排序

    我是新来的C编程 现在我正在编写一个程序C它读取 txt 文件并将数据存储到另一个 txt 文件中 例如 open 20150101 txt 然后获取里面的数据 2015010103I 2015010102O 然后将其存储在2015JAN
  • 根据用户输入C++打开文件

    我正在尝试制作一个程序 该程序将根据用户输入打开文件 这是我的代码 include
  • 我的 java 应用程序不读取我的文件(maven 项目)

    我有一个 Java 简单项目中的应用程序 但是 我需要将此项目粘贴到 Maven 项目中 因此 我基本上制作了一个简单的 Maven 项目 并将所有类复制并粘贴到其中 我需要在服务器中运行一个war 并且我需要像Java应用程序一样运行Ma
  • php:将变量内容下载为文件

    题主可以吗 我有一个正在执行的脚本 有一次 我在变量中有一大段文本 我可以将其作为可下载文件提供 而不实际将变量内容写入磁盘吗 如果您的意思是让用户单击链接并弹出一个对话框以将某些内容保存为文本文件
  • 使用 bash 解析 ICS 文件

    这是一个谷歌日历 ics 文件 我每次都会下载它来检查是否有新的比赛事件被添加或更改 并且我出现在IRC上 我需要转换这样的文件 BEGIN VEVENT DTSTART 20160612T201000Z DTEND 20160612T21
  • 输入类型=“文件”接受=“图像/*”在手机间隙不起作用?

    我尝试使用 HTML PhoneGap 2 6 0 制作一个 Android 应用程序 它支持
  • php 的问题:读取文件名,生成 javascript 和 html

    UPDATE 再一次问好 我发现自己遇到了一个新问题 php代码在我的PC wamp服务器 上完美运行 但我现在已将其上传到免费的网络主机服务器上 虽然php部分运行完美 它生成数组 但javascript函数本身不起作用 因为没有照片在网
  • PHP:删除任何扩展名的文件?

    当用户上传照片时 它会检查他们是否已经拥有一张照片 如果他们这样做 我希望它删除旧的 可以有任何扩展名 然后放入新的 有没有办法在不从数据库获取旧扩展的情况下做到这一点 目前的代码 del members gt prepare insert
  • 滚动文件实现

    我一直很好奇滚动文件是如何在日志中实现的 如何开始用任何语言创建一个文件写入类 以确保不超过文件大小 我能想到的唯一可能的解决方案是 write method size file size size of string to write i

随机推荐