(转)驱动开发之五 --- TDI之八 【译文】
http://hi.baidu.com/combojiang/item/fe7ee5147f64a621f6625c0e
步骤8:关闭句柄
这个函数被两个句柄调用,传输句柄和上下文句柄。
NTSTATUS TdiFuncs_CloseTdiOpenHandle(HANDLE hTdiHandle,
PFILE_OBJECT pfoTdiFileObject)
{
NTSTATUS NtStatus = STATUS_SUCCESS;
/*
* De-Reference the FILE_OBJECT and Close The Handle
*/
ObDereferenceObject(pfoTdiFileObject);
ZwClose(hTdiHandle);
return NtStatus;
}
其他资源
一旦你熟悉了,TDI接口就变得简单了。当写驱动时,最大的事情就是处理IRP。TDI似乎比socket有些复杂,但是它是内核接口。
如果曾经研究过TDI或者NDIS,你大概进入过Thomas Divine. 如果你想购买复杂的TDI或者NDIS示例,你可以找他们,或者他公司网站上的其他资源。在上面你也可以找到其他不同网站的指南。
IRP处理
下篇文章将粗微的谈到一些IRP的基本概念和怎样处理他们。实际上那里描述的东西有一些大的缝隙,为了保持下篇文章简单,所以在本篇我们将加快步伐,尽可能多的去填充这些缝隙。在这时,正面的揭示这些给驱动开发者,我们能非常简单的做这个。然而这里有大量的信息,并不是所有的都在示例代码中体现出来了。你需要自己试验这些IRP的处理,这是开发驱动的根本部分。
驱动请求
当写驱动的时候,有两次不同的揭示IRP. 即:发给你自己驱动的请求和你创建的IRP,发送给在其他驱动的请求两种。我记得,有一个驱动栈,栈里的每一个驱动在IRP中都有自己的栈区域。每次一个IRP沿着栈向下发送,IRP的当前栈区域是优先的。当它到达你的驱动时,你可以有少量的选择。
前进和遗忘
你可以使用IoCallDriver使IRP向前到栈中的下一个驱动。这是我们在其它驱动指南中的做法。我们向前了IRP并且忘记它。可是这里有一个问题,我们并没有考虑到STATUS_PENDING的情况。STATUS_PENDING是实现异步操作的一个方法。由底层驱动通知调用者,他们没有完成这个IRP。或许他们也正在一个单独的线程中完成这个IRP. 它的规则是如果你返回STATUS_PENDING,你必须在返回之前调用IoMarkIrpPending。如果你已经向前把IRP传给了下一个驱动,现在这仍然是个问题。在调用后,不允许你接触它。所以实际上你有两个选择。
IoMarkIrpPending(Irp);
IoCallDriver(pDeviceObject, Irp);
return STATUS_PENDING;
第二个选择是设置一个完成例程。你应该记得在第四篇文章中的代码,我们使用完成例程通过返回STATUS_MORE_PROCESSING_REQUIRED而不是STATUS_SUCCESS,停止了一个IRP的完成。,
IoSetCompletionRoutine(Irp, CompletionRoutine, NULL, TRUE, TRUE, TRUE);
return IoCallDriver(pDeviceObject, Irp);
...
NTSTATUS CompletionRoutine(PDEVICE_OBJECT DeviceObject,
PIRP Irp, PVOID Context)
{
if(Irp->PendingReturned)
{
IoMarkIrpPending(Irp);
}
return STATUS_SUCCESS;
}
你可以在这里再一次的停止处理过程,如果你这么做,你需要使用IoMarkIrpPending。这里有个循环逻辑,如果你调用IoMarkIrpPending,那么你必须从你的驱动中返回STATUS_PENDING,如果你从你的驱动中返回STATUS_PENDING,你必须调用IoMarkIrpPending。记住,如果你停止一个完成的处理,那么这就意味着你必须完成它。我们在第四篇就是这么做的。
需要注意一点,如果一个完成例程不提供这些,I/O管理器会把"IoMarkIrpPending"告诉给你。在这个或许你不想去相信的主题中,无论信息是多么分散,你要确信你所做的任何事情都是对的。
向前和投递处理
这和我们在第四篇的处理有些细微的差别。我们需要考虑pending的情况,如果IRP从底层驱动中返回pending的状态,我们需要等待,直到底层驱动完成它。一旦驱动完成,我们需要唤醒原始线程,这样我们就可以处理和完成IRP. 作为一个最佳方式,如果pending状态返回,我们只想设置事件。如果任何事情被同步处理,就不需要增加前面的设置和等待事件。下面是代码示例。
IoSetCompletionRoutine(Irp, CompletionRoutine,
&kCompleteEvent, TRUE, TRUE, TRUE);
NtStatus = IoCallDriver(pDeviceObject, Irp);
if(NtStatus == STATUS_PENDING)
{
KeWaitForSingleObject(&kCompleteEvent,
Executive, KernelMode, FALSE, NULL);
/*
* Find the Status of the completed IRP
*/
NtStatus = IoStatusBlock.Status;
}
/*
* Do Post Processing
*/
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return NtStatus;
...
NTSTATUS CompletionRoutine(PDEVICE_OBJECT DeviceObject,
PIRP Irp, PVOID Context)
{
if(Irp->PendingReturned)
{
KeSetEvent(Context, IO_NO_INCREMENT, FALSE);
}
return STATUS_MORE_PROCESSING_REQUIRED;
}