資訊清單檔案格式
資訊清單檔案的檔案格式盡可能從 C++ 和 IDL 借用。 因此,採用一般 C++ SDK 標頭檔並修改為資訊清單檔相當容易。 剖析器完全支援 C 和 C++ 樣式批註,以協助您組織和記錄檔案。
如果您嘗試新增資訊清單檔案或對現有檔案進行變更,最好的方法是只實驗一次。 當您在偵錯工具中發出 !logexts.logi 或 !logexts.loge 命令時,Logger 會嘗試剖析資訊清單檔。 如果遇到問題,它會產生錯誤訊息,這可能表示錯誤。
資訊清單檔是由下列基本元素所組成:模組標籤、類別標籤、函數宣告、COM 介面定義和類型定義。 其他類型的元素也存在,但這些是最重要的專案。
模組標籤
模組標籤只會宣告 DLL 會匯出之後所宣告的函式。 例如,如果您的資訊清單檔是用來記錄來自 Comctl32.dll的函式群組,您會在宣告任何函式原型之前,先包含下列模組標籤:
module COMCTL32.DLL:
模組標籤必須出現在資訊清單檔中的任何函式宣告之前。 資訊清單檔可以包含任意數目的模組標籤。
類別標籤
類似于模組標籤,類別標籤會識別所有後續函式和/或 COM 介面所屬的「類別」。 例如,如果您要建立Comctl32.dll資訊清單檔案,您可以使用下列專案作為類別標籤:
category CommonControls:
資訊清單檔可以包含任意數目的類別標籤。
函式宣告
函式宣告實際上是提示 Loger 記錄專案的內容。 它與 C/C++ 標頭檔中找到的函式原型幾乎完全相同。 格式有一些值得注意的新增專案,這可以透過下列範例最能說明:
HANDLE [gle] FindFirstFileA(
LPCSTR lpFileName,
[out] LPWIN32_FIND_DATAA lpFindFileData);
FindFirstFileA函式會採用兩個參數。 第一個是 lpFileName,這是一個完整路徑 (通常是萬用字元,) 定義搜尋檔案或檔案的位置。 第二個是將用來包含搜尋結果之WIN32_FIND_DATAA結構的指標。 傳回的 HANDLE 用於 未來對 FindNextFileA的呼叫。 如果 FindFirstFileA 傳回INVALID_HANDLE_VALUE,則函式呼叫失敗,而且呼叫 GetLastError 函式即可購買錯誤碼。
HANDLE 類型宣告如下:
value DWORD HANDLE
{
#define NULL 0 [fail]
#define INVALID_HANDLE_VALUE -1 [fail]
};
如果此函式傳回的值是 0 或 -1 (0xFFFFFFFF) ,則 Logger 會假設函式失敗,因為這類值在值宣告中有 [fail] 修飾詞。 (請參閱本節稍後的 [實數值型別] 區段。) 由於函式名稱前面有 [gle] 修飾詞,Logger 會辨識此函式使用 GetLastError 傳回錯誤碼,因此它會擷取錯誤碼並將它記錄到記錄檔。
lpFindFileData參數上的 [out] 修飾詞會通知 Logger 資料結構已由函式填入,並在函式傳回時記錄。
COM 介面定義
COM 介面基本上是可由 COM 物件的用戶端呼叫的函式向量。 資訊清單格式會大量借用 COM 中用來定義介面的介面定義語言 (IDL) 。
請考慮下列範例:
interface IDispatch : IUnknown
{
HRESULT GetTypeInfoCount( UINT pctinfo );
HRESULT GetTypeInfo(
UINT iTInfo,
LCID lcid,
LPVOID ppTInfo );
HRESULT GetIDsOfNames(
REFIID riid,
LPOLECHAR* rgszNames,
UINT cNames,
LCID lcid,
[out] DISPID* rgDispId );
HRESULT Invoke(
DISPID dispIdMember,
REFIID riid,
LCID lcid,
WORD wFlags,
DISPPARAMS* pDispParams,
VARIANT* pVarResult,
EXCEPINFO* pExcepInfo,
UINT* puArgErr );
};
這會宣告衍生自IUnknown的IDispatch介面。 它包含四個成員函式,這些函式會以介面大括弧內的特定順序宣告。 記錄器會藉由取代介面 vtable 中的函式指標, (執行時間所使用的函式指標的實際二進位向量) 本身,來攔截和記錄這些成員函式。 如需記錄器如何擷取介面的詳細資訊,請參閱本節稍後的COM_INTERFACE_PTR類型一節。
類型定義
定義資料類型是資訊清單檔案開發中最重要的 (和最繁瑣) 部分。 資訊清單語言可讓您定義從函式傳入或傳回之數值的人類可讀取標籤。
例如,Winerror.h 會定義名為 「WinError」 的類型,這是大部分 Microsoft Win32 函式傳回的錯誤值清單,以及其對應的人類可讀標籤。 這可讓 Logger 和 LogViewer 以有意義的文字取代非格式的錯誤碼。
您也可以標記位元遮罩內的個別位,以允許 Logger 和 LogViewer 將 DWORD 位元遮罩分成其元件。
資訊清單支援 13 種基本類型。 下表中列出這些欄位。
類型 | 長度 | 顯示範例 |
---|---|---|
Pointer |
4 個位元組 |
0x001AF320 |
無效 |
0 個位元組 |
|
BYTE |
1 個位元組 |
0x32 |
WORD |
2 個位元組 |
0x0A23 |
DWORD |
4 個位元組 |
-234323 |
BOOL |
1 個位元組 |
TRUE |
LPSTR |
長度位元組加上任意數目的字元 |
「快速黑色 fox」 |
LPWSTR |
長度位元組加上任意數目的 Unicode 字元 |
「跳到延遲狗」 |
GUID |
16 個位元組 |
{0CF774D0-F077-11D1-B1BC-00C04F86C324} |
COM_INTERFACE_PTR |
4 個位元組 |
0x0203404A |
value |
相依于基底類型 |
ERROR_TOO_MANY_OPEN_FILES |
遮罩 |
相依于基底類型 |
WS_MAXIMIZED |WS_ALWAYSONTOP |
struct |
相依于封裝類型的大小 |
+ lpRect nLeft 34 nRight 54 nTop 100 nBottom 300 |
資訊清單檔中的類型定義的運作方式類似 C/C++ typedefs。 例如,下列語句會將 PLONG 定義為 LONG 的指標:
typedef LONG *PLONG;
大部分的基本 typedefs 已在 Main.h 中宣告。 您只需要新增元件專屬的 typedefs。 結構定義的格式與 C/C++ 結構類型相同。
有四種特殊類型:value、mask、GUID 和 COM_INTERFACE_PTR。
實值型別
值是分成人類可讀取標籤的基本類型。 大部分函式檔只會參考函式中使用的特定常數 #define 值。 例如,大部分程式設計人員都不知道 GetLastError所傳回之所有程式碼的實際值為何,因此在 LogViewer 中查看隱含的數值並不具説明。 資訊清單值可藉由允許如下列範例所示的值宣告來克服此問題:
value LONG ChangeNotifyFlags
{
#define SHCNF_IDLIST 0x0000 // LPITEMIDLIST
#define SHCNF_PATHA 0x0001 // path name
#define SHCNF_PRINTERA 0x0002 // printer friendly name
#define SHCNF_DWORD 0x0003 // DWORD
#define SHCNF_PATHW 0x0005 // path name
#define SHCNF_PRINTERW 0x0006 // printer friendly name
};
這會宣告衍生自 LONG 的新類型「ChangeNotifyFlags」。 如果這是做為函式參數使用,則會顯示人類可讀取的別名,而不是原始數位。
檢測類型
與實值型別類似,檢測類型是基本類型 (通常是 DWORD) ,會針對具有意義的每個位分成人類可讀取的標籤。 以下列範例為例:
mask DWORD DirectDrawOptSurfaceDescCapsFlags
{
#define DDOSDCAPS_OPTCOMPRESSED 0x00000001
#define DDOSDCAPS_OPTREORDERED 0x00000002
#define DDOSDCAPS_MONOLITHICMIPMAP 0x00000004
};
這會宣告衍生自 DWORD 的新類型,如果用來作為函式參數,則會針對 LogViewer 中的使用者細分個別值。 因此,如果值0x00000005,LogViewer 將會顯示:
DDOSDCAPS_OPTCOMPRESSED | DDOSDCAPS_MONOLITHICMIPMAP
GUID 類型
GUID 是 16 位元組的全域唯一識別碼,在 COM 中廣泛使用。 它們會以兩種方式宣告:
struct __declspec(uuid("00020400-0000-0000-C000-000000000046")) IDispatch;
或
class __declspec(uuid("11219420-1768-11D1-95BE-00609797EA4F")) ShellLinkObject;
第一個方法是用來宣告介面識別碼 (IID) 。 由 LogViewer 顯示時,「IID_」 會附加至顯示名稱的開頭。 第二種方法是用來宣告 CLSID) (類別識別碼。 LogViewer 會將 「CLSID_」 附加至顯示名稱的開頭。
如果 GUID 類型是函式的參數,LogViewer 會比較值與所有宣告的 IID 和 CLSID。 如果找到相符專案,則會顯示 IID 易記名稱。 如果沒有,則會以標準 GUID 標記法顯示 32 十六進位字元值。
COM_INTERFACE_PTR類型
COM_INTERFACE_PTR類型是 COM 介面指標的基底類型。 當您宣告 COM 介面時,實際上會定義衍生自 COM_INTERFACE_PTR 的新類型。 因此,這類類型的指標可以是函式的參數。 如果COM_INTERFACE_PTR基本類型宣告為函式的 OUT 參數,而且有個別的參數具有 [iid] 標籤,則 Logger 會比較傳入的 IID 與所有宣告的 GUID。 如果有相符專案,而且宣告了 COM 介面的名稱與 IID 相同,則記錄器會攔截該介面中的所有函式並加以記錄。
範例如下:
STDAPI CoCreateInstance(
REFCLSID rclsid, //Class identifier (CLSID) of the object
LPUNKNOWN pUnkOuter, //Pointer to controlling IUnknown
CLSCTX dwClsContext, //Context for running executable code
[iid] REFIID riid, //Reference to the identifier of the interface
[out] COM_INTERFACE_PTR * ppv
//Address of output variable that receives
//the interface pointer requested in riid
);
在此範例中, riid 有 [iid] 修飾詞。 這表示 Logger 指出 ppv 中傳回的指標是 riid所識別介面的 COM 介面指標。
您也可以宣告函式,如下所示:
DDRESULT DirectDrawCreateClipper( DWORD dwFlags, [out] LPDIRECTDRAWCLIPPER *lplpDDClipper, IUnknown *pUnkOuter );
在此範例中,LPDIRECTDRAWCLIPPER 定義為 IDirectDrawClipper 介面的指標。 由於 Logger 可以識別 在 lplpDDClipper 參數中傳回的介面類別型,因此不需要任何其他參數上的 [iid] 修飾詞。