上回讲到ToasterFdoGetData内部通过调用WdfObjectGetTypedContextWorker以获得自定义Context结构,当时由于篇幅所限,没有展开分析WdfObjectGetTypedContextWorker的实现。为了填补这个空白,乃有此文。其实<竹林蹊径>中提到过函数WdfObjectGetTypedContextWorker,只是作者成书之时限制太多,因此没有深入分析。我相信以作者的文笔,应该更让读者收益,但是,既然作者没有再版,那就先由我做个引子吧。
WDFAPI
PVOID
FASTCALL
WDFEXPORT(WdfObjectGetTypedContextWorker)(
__in
PWDF_DRIVER_GLOBALS DriverGlobals,
__in
WDFOBJECT Handle,
__in
PCWDF_OBJECT_CONTEXT_TYPE_INFO TypeInfo
)
{
FxContextHeader* pHeader;
FxObject* pObject;
PFX_DRIVER_GLOBALS pFxDriverGlobals;
WDFOBJECT_OFFSET offset;
offset = 0;
pObject = FxObject::_GetObjectFromHandle(Handle, &offset); //从WDF句柄获得FxObject对象地址,其实这个函数内部仅仅做了几次位运算
pFxDriverGlobals = pObject->GetDriverGlobals(); //FxObject!m_Globals域指向驱动程序的FxDriverGlobals全局变量
pHeader = pObject->GetContextHeader(); //3).
for ( ; pHeader != NULL; pHeader = pHeader->NextHeader) { //4)
if (pHeader->ContextTypeInfo == TypeInfo) {
return &pHeader->Context[0];
}
}
//...
return NULL;
}
先给出WDF源码给出的上下文结构,以及一个宏,然后再慢慢缕清WdfObjectGetTypedContextWorker的脉络:
struct FxContextHeader {
// Backpointer to the object that this is a context for
FxObject* Object;
// Next context in the chain
FxContextHeader* NextHeader;
// Function to call when object is deleted
PFN_WDF_OBJECT_CONTEXT_CLEANUP EvtCleanupCallback; //<---------熟悉的结构体成员#1
// Function to call when the object's memory is destroyed
// when the last reference count goes to zero
PFN_WDF_OBJECT_CONTEXT_DESTROY EvtDestroyCallback; //<---------熟悉的结构体成员#2
// Type associated with this context
PCWDF_OBJECT_CONTEXT_TYPE_INFO ContextTypeInfo;
// Start of client's context
DECLSPEC_ALIGN(MEMORY_ALLOCATION_ALIGNMENT) ULONG_PTR Context[1];
};
除了定义FxContextHeader结构,源码还额外给出了基于x86架构的FxObject内存布局示意图:
/**
Framework Object Memory Layout (x86 32 bit)
Note: The handle is XOR'ed with (-1) to scramble it
---------------------- handle + sizeof(ContextSize) <-----内存高地址
| |
| Drivers Context |
| Memory |
| |
WDFOBJECT handle -> ---------------------- this + sizeof(C++_OBJECT) + _extrasize + sizeof(FxContextHeader)
| |
| FxContextHeader |
| |
---------------------- this + sizeof(C++_OBJECT) + _extrasize (m_ObjectSize)
| |
| C++ Object |
| "extra" memory |
| |
|--------------------| this + sizeof(C++_OBJECT)
| |
| C++ Object |
| |
| |
Base Pool Allocation -> ---------------------- this <-----内存低地址
C++ this pointer
**/
示意图中,1.最底部标有C++ Object的是FxObject对象所占据的空间;2.往上C++ Object "extra" memory按照我的理解,可能是继承FxObject的子类对象所占有的空间,如FxDevice/FxDriver对象,它们都继承自FxObject,所以FxObject是它们的公共头结构----共性,然而,每个子对象又各实现所特有的特性,这部分特性所需的空间占据"extra" memory部分;3.再往上,是自定义的对象上下文结构。
另外,在WdfObjectGetTypedContextWorker函数中,我们还看到一个函数GetContextHeader,它是内联函数,定义如下:
#define WDF_PTR_ADD_OFFSET_TYPE(_ptr, _offset, _type) \
((_type) (((PUCHAR) (_ptr)) + (_offset)))
#define GET_CONTEXT_HEADER() WDF_PTR_ADD_OFFSET_TYPE(this, m_ObjectSize, FxContextHeader*)
__inline
FxContextHeader*
GetContextHeader(
VOID
)
{
if (m_ObjectSize == 0) {
return NULL;
}
else {
return GET_CONTEXT_HEADER();
}
}
结合示意图,要理解这个函数应该不成问题,我就不废话了~让我们回到FxContextHeader结构的定义,其头部有个指针
FxObject* Object;
注释指明它的作用为后向指针,指向前面FxObject对象头部,这倒是个好信号:驱动中获得Context后,就能顺带获得整个对象了! 一个FxObject可能具有多个Context结构,WDF框架使用链表管理这串Context结构,所以代码的最后根据传入的TypeInfo比较各个Context!TypeInfo域。这么看来TypeInfo颇有ID的感觉?
大家可以翻看前一篇WDF对象上下文1 在定义Context全局变量时,其中已包含了TypeInfo的定义:
WDF_DECLARE_TYPE_AND_GLOBALS(
FDO_DATA,
&_WDF_FDO_DATA_TYPE_INFO,
NULL,
".data")
WDF_DECLARE_CASTING_FUNCTION(FDO_DATA, ToasterFdoGetData)
至此,大家应该知道通过WdfObjectGetTypedContextWorker函数Get上下文的过程了,有Get()则必然有Set(),下一篇我来讲讲所谓的Set()。