對於日誌,大部分人的想法應當和我起初的想法一致,只要寫個函數,在這個函數中開啟一個檔案,把需要記錄的事件資訊寫到這個檔案中。然後在需要記錄日誌的地方調用這個函數。
但此時需要考慮的問題有很多,包括檔案名稱的定義,是每次寫日誌都建立一個新檔案還是在一個檔案中寫所有的資訊。如果是同一個檔案如果控制多線程同時寫的問題;還有當記錄檔過大時,如果刪除以前的記錄。讀日誌的方式,包括檢索的功能;安全問題等等。所以不要小看了僅是協助工具功能的日誌機制,但不用擔心,windows已經為我們定義了很完善的日誌架構。
Windows vista引入的CLFS ( Common Log File System)機制,另外一個就是從NT3.5就支援的Event Logging機制,裡面的函數大多是以Elf(Event Log File)開頭的,簡稱為ELF。
Windows XP中定義了3類日誌,分別是應用程式記錄檔(Application),安全日誌(Security)和系統日誌(system),檔案名稱分別為AppEvent.Evt, SecEvent.Evt和SysEvent.Evt.這些檔案都用於儲存註冊表檔案和配置資訊的CONFIG目錄下%SystemRoot%\SYSTEM32\CONFIG\
Windows Vista增加了HardvareEvents和DFS Replication等日誌類別,並且為所有的記錄檔建立了一個單獨的目錄,即%SystemRoot%\SYSTEM32\winevt\Logs目錄,記錄檔的副檔名也由.EVT改為.EVTX。
日誌的配置資訊都是儲存在註冊表
HKEY_LOACAL_MACHINE\SYSTEM\CurrentControlSet\Sercices\Eventlog
下面看產生windows日誌的API及其執行過程:
1. 在註冊表中註冊事件來源。線介紹一下事件來源,事件來源都是註冊在註冊表HKLM\SYSTEM\CurrentControlSet\Services\EventLog\Application\下的,如果需要自己註冊事件來源就可以在這個註冊表下添加一個新的鍵,再加兩個項就可以了。來看個例子,outlook的事件來源
它包含了三個鍵,EvetMessageFile用來指定這個事件來源的訊息檔案(message file)的位置和名稱。訊息檔案的作用可以使用模板來格式化日誌事件或其他訊息,與字串資源和對話方塊資源差不多。可以使用後面介紹的事件ID(Event ID)來尋找對應的顯示資訊,提供一種模板的支援。TypesSupported表示事件所支援的類型,包括EVENTLOG_ERROR_TYPE(0x0001), EVENTLOG_WARBUBG_TYPE(0x0002)等。Version表示事件來源的版本。訊息檔案可以是DLL,exe等有效地PE檔案,編寫訊息檔案的方法這裡就不介紹了,這裡可以寫應用程式本身的路徑。
2. 調用RegisterEventSource API來取得事件來源控制代碼,原型為:
HANDLE RegisterEventSource ( LPCTSTR lpUNCServerName, LPCTSTR lpSourceName)
其中lpUNCServerName表示機器名,如果是在本機操作,添NULL就可以了,lpSourceName為事件來源的名稱,填寫前面建立的鍵名就可以了。如果事件來源的名稱在註冊表中找不到,那麼系統會預設使用應用類日誌下的Application事件來源。
3. 使用ReportEvent API來添加日誌記錄。原型為:
BOOL ReportEvent ( HANLE hEventLog, WORD wType, WORD wCategory, DWORD dwEventID, PSID lpUserSid, DWORD wNumStrings, DWORD dwDataSize, LPCTSTR* lpStrings, LPVOID lpRawData);
第一參數為hEventLog是使用RegisterEventSource得到的事件來源控制代碼;第二個參數wType用來指定事件的類型,可以為如下的常量:EVENTLOG_SUCCESS,EVENTLOG_AUDIT_FAILURE等。第三個參數wCategory用來指定事件在事件來源中的類屬號,其分類規則是由應用程式自己定義的。第四個參數dwEventID用來指定事件的ID號,通過這個ID來定位訊息檔案中所對應的資訊。所以ID也是有應用程式自己定義的。第五個參數lpUserSid用來指定使用者的安全標識,可以設為NULL。第六個參數wNumStrings用來指定第八個參數lpStrings所指向的字串數組所包含的字串指標個數。第八個參數用在事件的Description欄位中顯示資訊,如所示:
第七個參數dwDataSize用來指定第九個參數lpRawData所指向的未經處理資料緩衝區的資料長度。這兩個參數一個設為0,另一個設為NULL就行了。
這樣就可以寫入日子記錄了,來看一下代碼。
首先是註冊事件來源的代碼:
BOOL AddEventSource(CString csName, DWORD dwCategoryCount)
{
HKEY hRegKey = NULL;
DWORD dwError = 0;
TCHAR szPath[ MAX_PATH ];
_stprintf( szPath, _T("SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\%s"), csName );
// Create the event source registry key
dwError = RegCreateKey( HKEY_LOCAL_MACHINE, szPath, &hRegKey );
if (dwError != 0)
{
OutMsg("RegCreateKey failed for %d",GetLastError());
return E_FAIL;
}
// Name of the PE module that contains the message resource
GetModuleFileName( NULL, szPath, MAX_PATH );
// Register EventMessageFile
dwError = RegSetValueEx( hRegKey, _T("EventMessageFile"), 0, REG_EXPAND_SZ, (PBYTE) szPath, (_tcslen( szPath) + 1) * sizeof TCHAR );
if (dwError == 0)
{
// Register supported event types
DWORD dwTypes = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE;
dwError = RegSetValueEx( hRegKey, _T("TypesSupported"), 0, REG_DWORD, (LPBYTE) &dwTypes, sizeof dwTypes );
// If we want to support event categories, we have also to register the CategoryMessageFile.
// and set CategoryCount. Note that categories need to have the message ids 1 to CategoryCount!
if(dwError == 0 && dwCategoryCount > 0 )
{
dwError = RegSetValueEx( hRegKey, _T("CategoryMessageFile"), 0, REG_EXPAND_SZ, (PBYTE) szPath, (_tcslen( szPath) + 1) * sizeof TCHAR );
if (dwError == 0)
dwError = RegSetValueEx( hRegKey, _T("CategoryCount"), 0, REG_DWORD, (PBYTE) &dwCategoryCount, sizeof dwCategoryCount );
}
}
RegCloseKey( hRegKey );
return TRUE;
}
來至《軟體調試》
接下來是添加記錄的過程:
LPCTSTR ppszArgs[]={"I make a trick!!!!!!!"}; // 要顯示的資訊
HANDLE hLog = ::RegisterEventSource( NULL, "MyEventSource"); //之前註冊的事件來源
BOOL bRet = ReportEvent(hLog, EVENTLOG_INFORMATION_TYPE,
0, 1024, NULL, 1, 0, ppszArgs, NULL); // 顯示記錄
除了顯示日子記錄的功能,當然還有其他的API來實現更多的功能,其他的API如所示: