功能說明:DriverA是實作讀的普通驅動,DriverA采用緩沖方式讀寫,DriverB是挂載在DriverA之上的上層驅動。之後調用應用程式,此應用程式是對于DriverA裝置的,DriverB會将發送到DriverA的IRP過濾掉,但DriverB隻是簡單的過濾讀寫請求,然後原樣發送到下層驅動DriverA。
DriverA的讀寫派遣函數: NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj,
IN PIRP pIrp)
{
KdPrint(("Enter HelloDDKRead/n"));
PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
NTSTATUS status = STATUS_SUCCESS;
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
ULONG ulReadLength = stack->Parameters.Read.Length;
ULONG ulReadOffset = (ULONG)stack->Parameters.Read.ByteOffset.QuadPart;
if (ulReadOffset+ulReadLength>MAX_FILE_LENGTH)
{
status = STATUS_FILE_INVALID;
ulReadLength = 0;
}else
{
//将資料存儲在AssociatedIrp.SystemBuffer,以便應用程式使用
memcpy(pIrp->AssociatedIrp.SystemBuffer,pDevExt->buffer+ulReadOffset,ulReadLength);
status = STATUS_SUCCESS;
}
pIrp->IoStatus.Status = status;
pIrp->IoStatus.Information = ulReadLength; // bytes xfered
IoCompleteRequest( pIrp, IO_NO_INCREMENT );
KdPrint(("Leave HelloDDKRead/n"));
return status;
}
NTSTATUS HelloDDWrite(IN PDEVICE_OBJECT pDevObj,
IN PIRP pIrp)
{
KdPrint(("Enter HelloDDWrite/n"));
NTSTATUS status = STATUS_SUCCESS;
PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
//擷取存儲的長度
ULONG ulWriteLength = stack->Parameters.Write.Length;
//擷取存儲的偏移量
ULONG ulWriteOffset = (ULONG)stack->Parameters.Write.ByteOffset.QuadPart;
if (ulWriteOffset+ulWriteLength>MAX_FILE_LENGTH)
{
//如果存儲長度+偏移量大于緩沖區長度,則傳回無效
KdPrint(("如果存儲長度+偏移量大于緩沖區長度,則傳回無效"));
status = STATUS_FILE_INVALID;
ulWriteLength = 0;
}else
{
KdPrint(("%ws/n", pIrp->AssociatedIrp.SystemBuffer));
//将寫入的資料,存儲在緩沖區内
memcpy(pDevExt->buffer+ulWriteOffset,pIrp->AssociatedIrp.SystemBuffer,ulWriteLength);
status = STATUS_SUCCESS;
//設定新的檔案長度
if (ulWriteLength+ulWriteOffset>pDevExt->file_length)
{
pDevExt->file_length = ulWriteLength+ulWriteOffset;
}
}
pIrp->IoStatus.Status = status;
pIrp->IoStatus.Information = ulWriteLength; // bytes xfered
IoCompleteRequest( pIrp, IO_NO_INCREMENT );
KdPrint(("Leave HelloDDWrite/n"));
return status;
}
DriverB的主要派遣函數:/**********************************************
*寫的操作中的資料是從應用程式發出的,在請求完成之前就已經存在于輸入緩沖區裡了
*是以可以直接在上層驅動DriverB中直接捕捉到資料。
***********************************************/
#pragma PAGEDCODE
NTSTATUS HelloDDKWrite(IN PDEVICE_OBJECT pDevObj,
IN PIRP pIrp)
{
KdPrint(("DriverB:Enter B HelloDDKWrite/n"));
NTSTATUS ntStatus = STATUS_SUCCESS;
PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(pIrp);
//緩沖區長度
ULONG len = irpsp->Parameters.Write.Length;
PUCHAR buf = NULL;
//緩沖區位址
buf = (PUCHAR)pIrp->AssociatedIrp.SystemBuffer;
for(ULONG i=0; i<len; i++)
{
KdPrint(("DriverB:%2x/n", buf[i]));
}
//略過目前堆棧
IoSkipCurrentIrpStackLocation (pIrp);
//調用底層驅動
ntStatus = IoCallDriver(pdx->TargetDevice, pIrp);
KdPrint(("DriverB:Leave B HelloDDKWrite/n"));
return ntStatus;
}
pragma PAGEDCODE
NTSTATUS MyIoCompletion(PDEVICE_OBJECT DeviceObject, PIRP pIrp, PVOID Context)
{
KdPrint(("DriverB:Enter B MyIoCompletion/n"));
PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(pIrp);
//緩沖區長度
ULONG len = irpsp->Parameters.Read.Length;
PUCHAR buf = NULL;
//緩沖區位址
buf = (PUCHAR)pIrp->AssociatedIrp.SystemBuffer;
for(ULONG i=0; i<len; i++)
{
KdPrint(("DriverB:%2x/n", buf[i]));
}
KdPrint(("DriverB:Leave B MyIoCompletion/n"));
return STATUS_SUCCESS;
}
/*************************************
*讀請求資料則是必須從硬碟上獲得的,
*必須請求完成之後才存在于輸入緩沖區裡
**************************************/
#pragma PAGEDCODE
NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj,
IN PIRP pIrp)
{
KdPrint(("DriverB:Enter B HelloDDKRead/n"));
NTSTATUS ntStatus = STATUS_SUCCESS;
//将自己完成IRP,改成由底層驅動負責
PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
//将目前I/O堆棧拷貝底層堆棧
IoCopyCurrentIrpStackLocationToNext (pIrp);
//設定完成例程
IoSetCompletionRoutine(pIrp, MyIoCompletion,NULL,TRUE,TRUE,TRUE);
ntStatus = IoCallDriver(pdx->TargetDevice, pIrp);
KdPrint(("DriverB:Leave B HelloDDKRead/n"));
return ntStatus;
}
應用程式的代碼:int main()
{
HANDLE hDevice =
CreateFile(".//HelloDDKA",
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: "
"%s with Win32 error code: %d/n",
"MyWDMDevice", GetLastError() );
return 1;
}
UCHAR buffer[10];
memset(buffer,0xBB,10);
ULONG ulRead;
ULONG ulWrite;
BOOL bRet;
bRet = WriteFile(hDevice,buffer,10,&ulWrite,NULL);
if (bRet)
{
printf("Write %d bytes/n",ulWrite);
}
bRet = ReadFile(hDevice,buffer,5,&ulRead,NULL);
if (bRet)
{
printf("Read %d bytes:",ulRead);
for (int i=0;i<(int)ulRead;i++)
{
printf("%02X ",buffer[i]);
}
printf("/n");
}
CloseHandle(hDevice);
return 0;
}
總結:
- 在DriverB的DriverEntry函數中綁定下層裝置,此處為DriverA,用IoGetDeviceObjectPointer函數通過DriverA的裝置名尋找其裝置對象。再用IoAttachDeviceToDeviceStack綁定,并設定好各标志位。
- 寫的過濾:首先獲得IO堆棧irpsp,在irpsp->Parameters.Write.Length中獲得寫的長度,在pIrp->AssociatedIrp.SystemBuffer中獲得緩沖區。再調用IoSkipCurrentIrpStackLocation略過目前堆棧,IoCallDriver調用底層驅動。
- 讀的過濾:與寫的過濾不同的是不用IoSkipCurrentIrpStackLocation,而用IoCopyCurrentIrpStackLocationToNext向下複制堆棧,再用IoSetCompletionRoutine設定完成例程。完成例程中再調用IO堆棧獲得緩沖區,并進行操作。當下層驅動DriverA完成讀的IRP後會馬上進入完成例程,執行完完成例程才會再退出DriverA的讀例程。