其实竹林蹊径中已经详述了WDF_DECLARE_CONTEXT_TYPE_WITH_NAME宏定义,写这篇博文的目的无非是为后文做个引子。toaster中如此使用该宏:
typedef struct _FDO_DATA
{
WDFWMIINSTANCE WmiDeviceArrivalEvent;
BOOLEAN WmiPowerDeviceEnableRegistered;
TOASTER_INTERFACE_STANDARD BusInterface;
} FDO_DATA, *PFDO_DATA;
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(FDO_DATA, ToasterFdoGetData)
[1]
其定义如下:
#define WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(_contexttype, _castingfunction) \
\
WDF_DECLARE_TYPE_AND_GLOBALS( \
_contexttype, \
WDF_GET_CONTEXT_TYPE_INFO(_contexttype), \
NULL, \
WDF_TYPE_DEFAULT_SECTION_NAME) \
\
WDF_DECLARE_CASTING_FUNCTION(_contexttype, _castingfunction)
[2]
a).WDF_GET_CONTEXT_TYPE_INFO用来为自定义的结构名生成一个WDF开头的新结构名
#define WDF_GET_CONTEXT_TYPE_INFO(_contexttype) \
(&WDF_TYPE_NAME_TO_TYPE_INFO(_contexttype))
#define WDF_TYPE_NAME_TO_TYPE_INFO(_contexttype) \
_WDF_ ## _contexttype ## _TYPE_INFO
[3]
"##"是连接符,连接参数前后,作用和胶水差不多,最终,WDF_GET_CONTEXT_TYPE_INFO(_contexttype)变为:
&_WDF_FDO_DATA_TYPE_INFO
[4]
顺带一提,"#"用于将宏参数变成字符串,后面马上会看到它的应用。
#define WDF_TYPE_DEFAULT_SECTION_NAME ".data"
[5]
初步展开WDF_DECLARE_CONTEXT_TYPE_WITH_NAME,得到这样的中间结果:
WDF_DECLARE_TYPE_AND_GLOBALS(
FDO_DATA,
&_WDF_FDO_DATA_TYPE_INFO,
NULL,
".data")
WDF_DECLARE_CASTING_FUNCTION(FDO_DATA, ToasterFdoGetData)
#define WDF_DECLARE_TYPE_AND_GLOBALS(_contexttype, _UniqueType, _GetUniqueType, _section)\
\
typedef _contexttype* WDF_TYPE_NAME_POINTER_TYPE(_contexttype); \
\
WDF_EXTERN_C \
__declspec(allocate( _section )) \
__declspec(selectany) \
extern const WDF_OBJECT_CONTEXT_TYPE_INFO \
WDF_TYPE_NAME_TO_TYPE_INFO(_contexttype) = \
{ \
sizeof(WDF_OBJECT_CONTEXT_TYPE_INFO), \
#_contexttype, \
sizeof(_contexttype), \
_UniqueType, \
_GetUniqueType, \
}; \
[7]
它做了两件事,1).申明新的类型;2).借助编译器指令__desclspec(allocate(".data")),在".data"节中定义变量。根据反汇编经验,局部变量定义在栈上,定义在".data"节的变量属于全局变量。所以,这个宏顺带着定义并初始化一个结构体全局变量。至于这个结构体的成员,通过source insight来揭示:
typedef struct _WDF_OBJECT_CONTEXT_TYPE_INFO {
ULONG Size; //结构体大小
PCHAR ContextName; //上下文的名字字符串,其形式如"DEGVICE_CONTEXT"
size_t ContextSize;
PCWDF_OBJECT_CONTEXT_TYPE_INFO UniqueType; //如果该域非空,其值代表上下文的ID
PFN_GET_UNIQUE_CONTEXT_TYPE EvtDriverGetUniqueContextType;
} WDF_OBJECT_CONTEXT_TYPE_INFO, *PWDF_OBJECT_CONTEXT_TYPE_INFO;
[8]
宏WDF_TYPE_NAME_TO_TYPE_INFO在前文中已经出现过了,算是老熟人了,不再介绍,见[3];至于宏WDF_TYPE_NAME_POINTER_TYPE,其定义是:
#define WDF_TYPE_NAME_POINTER_TYPE(_contexttype) \
WDF_POINTER_TYPE_ ## _contexttype
[9]
这段宏的作用是将程序员自定义的上下文结构体单独声明为新的类型。
综上所述,这段宏
WDF_DECLARE_TYPE_AND_GLOBALS(
FDO_DATA,
&_WDF_FDO_DATA_TYPE_INFO,
NULL,
".data")
[10]
编译后,终于拨云见日般得到如下结果:
typedef FDO_DATA* WDF_POINTER_TYPE_FDO_DATA;
WDF_EXTERN_C
__declspec(allocate( ".data" ))
__declspec(selectany)
extern const WDF_OBJECT_CONTEXT_TYPE_INFO _WDF_FDO_DATA_TYPE_INFO =
{
sizeof(WDF_OBJECT_CONTEXT_TYPE_INFO),
"FDO_DATA",
sizeof(FDO_DATA),
&_WDF_FDO_DATA_TYPE_INFO,
NULL,
};
[11]
嗯,现在还剩下宏:
WDF_DECLARE_CASTING_FUNCTION(FDO_DATA, ToasterFdoGetData)
[12]
尚未展开,这个宏复杂度不高,还请耐心的看下去。其原型如下:
#define WDF_DECLARE_CASTING_FUNCTION(_contexttype, _castingfunction) \
\
WDF_EXTERN_C \
__drv_aliasesMem \
WDF_TYPE_NAME_POINTER_TYPE(_contexttype) \
FORCEINLINE \
_castingfunction( \
_In_ WDFOBJECT Handle \
) \
{ \
return (WDF_TYPE_NAME_POINTER_TYPE(_contexttype)) \
WdfObjectGetTypedContextWorker( \
Handle, \
WDF_GET_CONTEXT_TYPE_INFO(_contexttype)->UniqueType \
); \
}
[13]
上面这段宏中又引用了其他2个宏:WDF_TYPE_NAME_POINTER_TYPE及WDF_GET_CONTEXT_TYPE_INFO,他们初次定义在[3]处;至于WDF_POINTER_TYPE_FDO_DATA,它的定义在[11]处。最终展开后,得到下列结果:
WDF_EXTERN_C
__drv_aliasesMem
FDO_DATA*
FORCEINLINE
ToasterFdoGetData(
_In_ WDFOBJECT Handle
)
{
return (FDO_DATA*)
WdfObjectGetTypedContextWorker(
Handle,
_WDF_FDO_DATA_TYPE_INFO->UniqueType
);
}
[14]
前面说过_WDF_FDO_DATA_TYPE_INFO是个全局变量,当我们在驱动中如此申明上下文:
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(FDO_DATA, ToasterFdoGetData)
并使用ToastFdoGetData时,实际会调用WdfObjectGetTypedContextWorker。如果想搞清楚这个函数的来龙去脉,请移步下一篇博文:
WDF对象上下文2