對 Windows 開發人員與系統管理員而言,Windows Vista 事件日誌較以往有極大提升。對開發人員,Vista 日誌記錄對多種事件與選項展現統一致性;對網管與 IT 專家,提供豐富人機介面管理事件。新日誌記錄方式僅能透過 Windows SDK 新原生函數實現,以下探討如何在程式中實作。
若程式使用新 API 且要將事件記錄至日誌,需建立資訊清單檔案(程式中所有事件的 XML 表現形式)。可使用命令列訊息編譯器 MC.exe,利用資訊清單產生標頭檔(*.h)與資源檔(*.rc)——僅限 C++ 專案使用。(詳細說明請查閱 MSDN,本文僅提及部分。)
資訊清單中最重要的元素是指定哪個「通道」可被寫入。「通道」是 Vista 事件日誌新概念,依接收者與磁碟區為事件提供獨立輸出。範例程式使用兩個通道:操作性與偵錯通道,以下 XML 指定程式將使用這兩種通道引發事件:
<channels>
<importChannel chid="C1" name="Application"/>
<channel chid="MyOpChannel"
name="DotNetPerformance-TechalWriting-EventLogSample/Operational"
type="Operational" symbol="DOTNETPERFORMANCE"
isolation="Application" enabled="true"/>
<channel chid="MyDebugChannel"
name="DotNetPerformance-TechalWriting-EventLogSample/Debug"
type="Debug" symbol="DOTNETPERFORMANCE"
isolation="Application" enabled="true"/>
定義好通道後,需定義範本指定引發事件的基本形態。類似舊版事件日誌 API,Vista 也支援 %1 等置換符,方便以當地語系化文字取代,所有訊息內容可在執行期提供:
<template tid="SimpleEvent" message="$(string.SimpleMessage)">
<data name="Message" inType="win:UnicodeString"/>
<UserData>
<SimpleEvent xmlns="https://manifests.microsoft.com/win/2004/08/windows/simpleevent">
<ExceptionMessage>%1</ExceptionMessage>
</SimpleEvent>
</UserData>
</template>
最後將應用程式實際引發的事件加入資訊清單,為簡單起見,每個通道只定義一個事件,皆使用同一範本:
<event value="1" level="win:Informational" template="SimpleEvent"
opcode="win:Info" channel="MyOpChannel" symbol="DNP_OP_EVENT"
message="$(string.SimpleMessage)"/>
<event value="2" level="win:Informational" template="SimpleEvent"
opcode="win:Info" channel="MyDebugChannel" symbol="DNP_DEBUG_EVENT"
message="$(string.SimpleMessage)"/>
現在訊息編譯器 mc.exe 即可將資訊清單編譯為標頭檔與資源檔。
實際在程式中引發事件相當簡單(至少比定義資訊清單簡單)。訊息編譯器產生的標頭檔包含事件與事件發行者定義,可傳遞給 SDK 函數。對上述資訊清單,標頭檔將包含:
EXTERN_C __declspec(selectany) const
GUID DOTNETPERFORMANCE_TECHNICALWRITING_PUBLISHER
= {0x9cde86c9, 0xdfb9, 0x463f, {0xb2, 0xc5, 0x71, 0xee, 0xc2, 0x32, 0xa6, 0x9c}};
#define DOTNETPERFORMANCEOP 0x10
#define DOTNETPERFORMANCEDEBUG 0x0
EXTERN_C __declspec(selectany) const
EVENT_DESCRIPTOR OP_EVENT = {0x1, 0x0, 0x10, 0x4, 0x0, 0x0, 0x8000000000000000};
#define DNP_OP_EVENT_value 0x1
EXTERN_C __declspec(selectany) const
EVENT_DESCRIPTOR DEBUG_EVENT = {0x2, 0x0, 0x0, 0x4, 0x0, 0x0, 0x4000000000000000};
#define DNP_DEBUG_EVENT_value 0x2
#define MSG_SimpleMessage 0xB0000001L
接著在程式碼包含標頭檔:windows.h(主控台程式)、evntprov.h(Windows 事件追蹤)、winevt.h(Vista 日誌宣告);透過 EventRegister 註冊事件發行者:
REGHANDLE hPub = NULL;
EventRegister(&DOTNETPERFORMANCE_TECHNICALWRITING_PUBLISHER, NULL, NULL, &hPub);
引發事件非常簡單:建立事件描述符並傳給 EventWrite:
EVENT_DATA_DESCRIPTOR opEventDesc;
PWSTR pwsOp = L"My Operational Event";
EventDataDescCreate(&opEventDesc, pwsOp, ((ULONG)wcslen(pwsOp)+1)*sizeof(WCHAR));
EventWrite(hPub, &DNP_OP_EVENT, 1, &opEventDesc);
EventDataDescCreate 定義於 evntprov.h,提供較短語法設定 EVENT_DATA_DESCRIPTOR 成員。不再需要寫入事件時,呼叫 EventUnregister(hPub) 取消註冊。
部署前需用以下命令註冊資訊清單:wevtutil install-manifest 清單檔案.xml。Vista 內建 wevtutil 工具會解析資訊清單並加入所需設定至 Vista 日誌;若未執行 wevtutil,事件仍可成功引發,但在事件日誌中不可見。一切正常時,事件將如期出現在日誌中(見下圖)。