就Windows開發者及系統管理員來說,Windows Vista日志相比以前,無疑有了一個很大的提高。對開發者來說,Vista的日志記錄對多種事件與日志選項,都表現出統一一致性;而對網絡管理員及IT專家,它提供了豐富的人機界面用於管理事件。新的日志記錄方式,也只能通過Windows SDK中新的本機函數來實現,下面就來看看怎樣在程序中實現新的記錄方式。
創建並編譯清單文件
如果程序中使用了新的API,且要把事件記錄到日志中,就需要創建一個清單文件(這裡補充一點,如果未使用新的API,或僅是使用托管程序,日志記錄還是繼續使用原來的ReportEvent* API)。清單文件是程序中所有事件基於XML格式的表現形式,我們可使用命令行的消息編譯器(MC.exe),利用清單文件來生成一個頭文件(*.h)及一個資源文件(*.rc)——僅限於用在C++工程中。(有關事件日志清單文件的詳細說明,請查閱MSDN聯機文檔,在本文中只提及了其中的一小部分。)
在清單文件中,最重要的元素就是指明了哪個“通道”(channel)可以被寫入,“通道”是Vista事件日志中一個新的概念,它根據接收者及卷,為事件提供了單獨的輸出。示例程序中使用了兩個通道:操作性(Operational)通道及調試(Debug)通道,以下的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="http://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
Windows Vista自帶的wevtutil工具,將會解析清單文件並添加所需的設置到Vista日志記錄中;如果未運行wevtutil,事件仍可被成功引發,但在事件日志中將不可見。
如果一切正常,事件就會如期出現在日志記錄中,如下圖。