《WDF队列分析(2)----IoQueue的创建》属于未完成篇,这篇接着往下分析。toaster是功能驱动,所以WDF框架顺着FxDevice::_Create函数,进入case FxDeviceInitTypeFdo分支:
case FxDeviceInitTypeFdo:
status = pDevice->FdoInitialize(pInit);
break;
熟悉wdm驱动的读者都清楚驱动程序响应IRP_MN_START事件后的三板斧:
1).进入AddDevice函数; 2).创建Fdo; 3).将Fdo挂载到设备栈上,如下所示:
NTSTATUS
ToasterAddDevice(
__in PDRIVER_OBJECT DriverObject,
__in PDEVICE_OBJECT PhysicalDeviceObject
)
{
status = IoCreateDevice (DriverObject,
sizeof (FDO_DATA),
NULL,
FILE_DEVICE_UNKNOWN,
FILE_DEVICE_SECURE_OPEN,
FALSE,
&deviceObject);
fdoData->NextLowerDriver = IoAttachDeviceToDeviceStack (deviceObject,
PhysicalDeviceObject);
WDF框架也没有跳出这套框架,所以FdoInitialize仍然一板一眼的遵循这个流程:
1).设置Fdo所要挂载的Pdo:
m_PhysicalDevice.SetObject((MdDeviceObject)DeviceInit->Fdo.PhysicalDevice);
在wdm驱动中,我们创建设备对象的同时也为设备对象分配了设备扩展。设备扩展是片自定义的内存空间,自由度很大,但是很多驱动程序都会把Pdo保存到设备扩展中。但是,WDF框架并没有这么做:它很取巧的用到了FxDevice是DeviceObject的扩展这一特性,在类FxDevice中引入m_PhysicalDevice域,用它来保存Pdo。对于我们而言,Pdo信息放在哪并不重要,只要能解不时之需就行了。另外,你可能会对上面的代码片感到疑惑:代码行中DeviceInit出自何处?虽然我们身处FdoInitialize函数中,但不要忘了,这个函数是由WdfDeviceCreate间接调用的。DeviceInit作为WdfDeviceCreate的参数被一层层传递下来。而WdfDeviceCreate又由
NTSTATUS
ToasterEvtDeviceAdd(
IN WDFDRIVER Driver,
IN PWDFDEVICE_INIT DeviceInit
);
调用,所以DeviceInit来自于EvtDriverDeviceAdd传入的参数。
2).完备驱动程序IRP处理能力;
《WDF队列分析(2)----IoQueue的创建》中说过,在调用FdoInitialize之前,所有FxDevice已经具有不完整的IRP处理能力。个性化定制由FdoInitialize实现,它执行下列语句,定制适用于Fdo的Pnp/Power处理方式:
status = FxPkgFdo::_Create(pGlobals, (CfxDevice*)this, &pkgFdo);
if (!NT_SUCCESS(status)) {
return status;
}
else {
m_PkgPnp = pkgFdo;
}
前面说过m_PkgPnp是FxDevice的类成员,用于处理IRP_MJ_Pnp/Power事件。既然代码中将m_PkgPnp设置为新创建的对象pkgFdo,那么pkgFdo势必也具有处理这些事件的能力。查找类定义,pkgFdo果然是FxPkgPnp的派生类:
class FxPkgFdo : public FxPkgPnp
3).创建DeviceOject并将设备加入到设备栈:
status = CreateDevice(DeviceInit);
if (NT_SUCCESS(status)) {
m_AttachedDevice.SetObject(Mx::MxAttachDeviceToDeviceStack(
m_DeviceObject.GetObject(), m_PhysicalDevice.GetObject()));
CreateDevice函数封装了wdm时代调用IoCreateDevice前做的通用设置,没必要展开讨论。
做完以上步骤后,新创建的DeviceObject对象可以开始它的生命之旅。如果让我在它的生命之旅中挑选2个重要的时刻进行分析,我会选IRP_MJ_CREATE和IRP_MJ_READ,因为他们分别代表了两种典型的Package:FxPkgGeneral以及FxPkgIo。首先看下WDF处理IRP_MJ_CREATE时,在windbg中留下的脚印:
kd> x wdf01000!*OnCreate*
8727cb38 Wdf01000!imp_WdfDmaTransactionCreate (void)
872b2128 Wdf01000!imp_VfWdfCollectionCreate (void)
87248b84 Wdf01000!imp_WdfCollectionCreate (void)
8723afc0 Wdf01000!FxPkgGeneral::OnCreate (void)
872b25dc Wdf01000!imp_VfWdfDmaTransactionCreate (void)
kd> bp Wdf01000!FxPkgGeneral::OnCreate
kd> g
Breakpoint 6 hit
Wdf01000!FxPkgGeneral::OnCreate:
8723afc0 8bff mov edi,edi
kd> kb
ChildEBP RetAddr Args to Child
#0 a81d7850 87231a40 a81d788c 8f6c0740 8ba0e030 Wdf01000!FxPkgGeneral::OnCreate [minkernel\wdf\framework\shared\irphandlers\general\fxpkggeneral.cpp @ 846]
#1 a81d78a8 81d534b8 00a0e030 8f6f2c38 00000000 Wdf01000!FxDevice::DispatchWithLock+0x1a0 [minkernel\wdf\framework\shared\core\fxdevice.cpp @ 1430]
#2 a81d78c0 81d5359f 8f6c0740 8cd77e58 a81d78e8 nt!IofCallDriver+0x48
a81d78e8 81f95e55 9d5c0705 8ba0e018 81f95130 nt!IoCallDriverWithTracing+0x2f
a81d799c 81f8a42a 8ba0e030 86445830 ad7b4008 nt!IopParseDevice+0xd25
a81d7aa4 81f87f58 00000040 86445830 8ba9ca01 nt!ObpLookupObjectName+0x3da
a81d7b2c 81f649e3 0149f4fc 86445830 8ba9ca01 nt!ObOpenObjectByNameEx+0x118
a81d7bb4 81f646c0 0149f4fc 0149f514 00000000 nt!IopCreateFile+0x313
a81d7bf4 81db6b87 0149f4e0 00100080 0149f4fc nt!NtOpenFile+0x30
a81d7bf4 77701670 0149f4e0 00100080 0149f4fc nt!KiSystemServicePostCall
WARNING: Stack unwind information not available. Following frames may be wrong.
0149f964 6bc437cf 0149f9b0 005274e8 0101fa48 ntdll+0x91670
先不解释我在Wdf01000!FxPkgGeneral::OnCreate处下断点的原因,回头一定会提到。从函数调用堆栈可以很清晰的看出OS收到应用层的OpenFile API后进入了某个基于WDF框架的驱动派遣函数。大家仔细看栈帧#1和#2,是不是有种似曾相识的感觉?你没记错,在《WDF队列分析(1)--序幕》中WDF框架响应IRP_MJ_READ消息时出现过:
只不过这是处理IRP_MJ_READ的调用堆栈。大家仔细对比蓝笔圈的栈帧和IRP_MJ_CREATE中的#1/#2栈帧,两者内容一样!还记得我曾说过基于WDF框架的驱动,它们的派遣函数无一例外的都被设置成Wdf01000!FxDevice::DispatchWithLock? DispatchWithLock算一个中转函数,根据IRP类型分别调用不同的Pkg->Dispatch函数。像IRP_MJ_CREATE这类IRP,对应的Pkg是FxPkgGeneral,它的Dispatch函数被设置为:
_Must_inspect_result_
NTSTATUS
FxPkgGeneral::Dispatch(
__inout MdIrp Irp
)
{
...
switch (fxIrp.GetMajorFunction()) {
case IRP_MJ_CREATE:
status = OnCreate(&fxIrp);
break;
OnCreate的实现不是很复杂,由于篇幅限制就不详细分析。顺带一提的是,在默认情况下WDF框架虽然直接结束IRP_MJ_CREATE等消息,但是还是可以在创建设备对象前为这类IRP设置回调函数:
_Must_inspect_result_
NTSTATUS
FxDevice::CreateDevice(
__in PWDFDEVICE_INIT DeviceInit
)
{
status = m_PkgGeneral->Initialize(DeviceInit);
FxPkgGeneral::Initialize将为DeivceInit中自定义的关于Create/Close/Cleanup的回调函数创建一个节点,并加入到链表中:
NTSTATUS
FxPkgGeneral::Initialize(
__in PWDFDEVICE_INIT DeviceInit
)
{
NTSTATUS status;
PLIST_ENTRY next;
FxFileObjectInfo* fileObjInfo;
PFX_DRIVER_GLOBALS fxDriverGlobals;
fxDriverGlobals = GetDriverGlobals();
//
// Init file object info.
//
if (DeviceInit->FileObject.Set) {
fileObjInfo = new(fxDriverGlobals) FxFileObjectInfo();
if (fileObjInfo == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
DoTraceLevelMessage(fxDriverGlobals,
TRACE_LEVEL_ERROR, TRACINGDEVICE,
"Couldn't create object FileObjectInfo, "
"%!STATUS!", status);
goto Done;
}
fileObjInfo->ClassExtension = FALSE;
fileObjInfo->FileObjectClass = DeviceInit->FileObject.Class;
fileObjInfo->Attributes = DeviceInit->FileObject.Attributes;
fileObjInfo->AutoForwardCleanupClose =
DeviceInit->FileObject.AutoForwardCleanupClose;
fileObjInfo->EvtFileCreate.Method =
DeviceInit->FileObject.Callbacks.EvtDeviceFileCreate;
fileObjInfo->EvtFileCleanup.Method =
DeviceInit->FileObject.Callbacks.EvtFileCleanup;
fileObjInfo->EvtFileClose.Method =
DeviceInit->FileObject.Callbacks.EvtFileClose;
InsertTailList(&m_FileObjectInfoHeadList, &fileObjInfo->ListEntry);
前面我在OnCreate符号上下断点并进行跟踪,也是我以m_FileObjectInfoHeadList为关键词,搜索到的调用结果。
当FxDevice进入工作状态,从而进入FxPkgGeneral::Dispatch后,将会从m_FileObjectInfoHeadList链表中取出并调用自定义的回调函数。相对而言,FxPkgGeneral的处理还是比较简单,至于IRP_MJ_READ消息的处理,我要放到下一篇了。