其實竹林蹊徑中已經詳述了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]
順帶一提,"#"用于将宏參數變成字元串,後面馬上會看到它的應用。
b).WDF_TYPE_DEFAULT_SECTION_NAME也是個簡單的宏:
#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)
[6]
這個中間結果還是含有2個宏,還不是最終結果,就像做除法時餘數大于除數一樣----有待進一步處理。
首先展開WDF_DECLARE_TYPE_AND_GLOBALS:
#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)