2015-05-14 224 views
18

我的Delphi應用程序如何輕鬆寫入Windows事件日誌?使用Delphi寫入Windows事件日誌

TEventLogger和ReportEvent有什麼區別? 如何使用ReportEvent功能?

+2

搜索堆棧溢出這個看似簡單的問題返回的答案在許多問題之間傳播。我創建了一個新的簡單問題,並花費時間將答案合併在一起,並添加了其他答案中沒有的額外信息。我這樣做是因爲這不是我第一次來這裏尋找這個答案,並認爲詳細的答案和示例項目可能也會幫助其他人。 –

+2

你可以在其他問題上做到這一點。這裏也很好。這些問題現在聯繫在一起。都很好。 http://blog.stackoverflow.com/2010/11/dr-strangedupe-or-how-i-learned-to-stop-worrying-and-love-duplication/ –

+0

好的,謝謝大衛,我現在明白了更好有用。 –

回答

25

如果你正在寫一個Windows服務,需要寫入本地計算機的Windows事件日誌,那麼你可以調用 TService.LogMessage提到here

//TMyTestService = class(TService) 

procedure TMyTestService.ServiceStart(Sender: TService; var Started: Boolean); 
begin 
    LogMessage('This is an error.'); 
    LogMessage('This is another error.', EVENTLOG_ERROR_TYPE); 
    LogMessage('This is information.', EVENTLOG_INFORMATION_TYPE); 
    LogMessage('This is a warning.', EVENTLOG_WARNING_TYPE); 
end; 

對於任何其他類型的應用程序可以使用SvcMgr。 TEventLoggerundocumented TService的助手類,用於寫入本地計算機的Windows事件日誌,如here,0 herehere所述。

uses 
    SvcMgr; 

procedure TForm1.EventLoggerExampleButtonClick(Sender: TObject); 
begin 
    with TEventLogger.Create('My Test App Name') do 
    begin 
    try 
     LogMessage('This is an error.'); 
     LogMessage('This is another error.', EVENTLOG_ERROR_TYPE); 
     LogMessage('This is information.', EVENTLOG_INFORMATION_TYPE); 
     LogMessage('This is a warning.', EVENTLOG_WARNING_TYPE); 
    finally 
     Free; 
    end; 
    end; 
end; 

您也可以使用Windows API函數ReportEvent提到herehere

我創建了一個簡單的類,使它更容易,它是available on GitHub

//----------------- EXAMPLE USAGE: --------------------------------- 

uses 
    EventLog; 

procedure TForm1.EventLogExampleButtonClick(Sender: TObject); 
begin 
    TEventLog.Source := 'My Test App Name'; 

    TEventLog.WriteError('This is an error.'); 
    TEventLog.WriteInfo('This is information.'); 
    TEventLog.WriteWarning('This is a warning.'); 
end; 

//------------------------------------------------------------------ 

unit EventLog; 

interface 

type 
    TEventLog = class 
    private 
    class procedure CheckEventLogHandle; 
    class procedure Write(AEntryType: Word; AEventId: Cardinal; AMessage: string); static; 
    public 
    class var Source: string; 
    class destructor Destroy; 

    class procedure WriteInfo(AMessage: string); static; 
    class procedure WriteWarning(AMessage: string); static; 
    class procedure WriteError(AMessage: string); static; 

    class procedure AddEventSourceToRegistry; static; 
    end; 

threadvar EventLogHandle: THandle; 

implementation 

uses Windows, Registry, SysUtils; 

class destructor TEventLog.Destroy; 
begin 
    if EventLogHandle > 0 then 
    begin 
    DeregisterEventSource(EventLogHandle); 
    end; 
end; 

class procedure TEventLog.WriteInfo(AMessage: string); 
begin 
    Write(EVENTLOG_INFORMATION_TYPE, 2, AMessage); 
end; 

class procedure TEventLog.WriteWarning(AMessage: string); 
begin 
    Write(EVENTLOG_WARNING_TYPE, 3, AMessage); 
end; 

class procedure TEventLog.WriteError(AMessage: string); 
begin 
    Write(EVENTLOG_ERROR_TYPE, 4, AMessage); 
end; 

class procedure TEventLog.CheckEventLogHandle; 
begin 
    if EventLogHandle = 0 then 
    begin 
    EventLogHandle := RegisterEventSource(nil, PChar(Source)); 
    end; 
    if EventLogHandle <= 0 then 
    begin 
    raise Exception.Create('Could not obtain Event Log handle.'); 
    end; 
end; 

class procedure TEventLog.Write(AEntryType: Word; AEventId: Cardinal; AMessage: string); 
begin 
    CheckEventLogHandle; 
    ReportEvent(EventLogHandle, AEntryType, 0, AEventId, nil, 1, 0, @AMessage, nil); 
end; 

// This requires admin rights. Typically called once-off during the application's installation 
class procedure TEventLog.AddEventSourceToRegistry; 
var 
    reg: TRegistry; 
begin 
    reg := TRegistry.Create; 
    try 
    reg.RootKey := HKEY_LOCAL_MACHINE; 
    if reg.OpenKey('\SYSTEM\CurrentControlSet\Services\Eventlog\Application\' + Source, True) then 
    begin 
     reg.WriteString('EventMessageFile', ParamStr(0)); // The application exe's path 
     reg.WriteInteger('TypesSupported', 7); 
     reg.CloseKey; 
    end 
    else 
    begin 
     raise Exception.Create('Error updating the registry. This action requires administrative rights.'); 
    end; 
    finally 
    reg.Free; 
    end; 
end; 

initialization 

TEventLog.Source := 'My Application Name'; 

end. 

ReportEvent支持編寫日誌條目以本地或遠程計算機的事件日誌。有關遠程示例,請參閱John Kaster's EDN article


注意,你也必須create a message fileregister your event source,否則所有的日誌信息將開始是這樣的:

從源代碼XXXX的事件ID XXX說明不能 找到。引發此事件的組件未安裝在您的本地計算機的 或安裝已損壞。您可以安裝 或在本地計算機上修復組件。

如果事件發生在另一臺計算機上,顯示信息 必須與該事件一起保存。

以下信息包括與事件:

1,關於如何創建一個消息文件的詳細信息,請參閱Finn Tolderlund's tutorialMichael Hex's article 或者您可以使用現有的MC和RES file included in the GitHub project

2,通過在您的DPR文件中包含MessageFile.res,將RES文件嵌入到您的應用程序中。或者,您可以爲消息創建一個dll。

program MyTestApp; 

uses 
    Forms, 
    FormMain in 'FormMain.pas' {MainForm}, 
    EventLog in 'EventLog.pas'; 

{$R *.res} 
{$R MessageFile\MessageFile.res} 

begin 
    Application.Initialize; 

3,一次性的註冊需要管理員權限寫入註冊表,因此我們通常作爲應用程序的安裝過程中完成的。

//For example 
AddEventSourceToRegistry('My Application Name', ParamStr(0)); 
//or 
AddEventSourceToRegistry('My Application Name', 'C:\Program Files\MyApp\Messages.dll'); 

//-------------------------------------------------- 

procedure AddEventSourceToRegistry(ASource, AFilename: string); 
var 
    reg: TRegistry; 
begin 
    reg := TRegistry.Create; 
    try 
    reg.RootKey := HKEY_LOCAL_MACHINE; 
    if reg.OpenKey('\SYSTEM\CurrentControlSet\Services\Eventlog\Application\' + ASource, True) then 
    begin 
     reg.WriteString('EventMessageFile', AFilename); 
     reg.WriteInteger('TypesSupported', 7); 
     reg.CloseKey; 
    end 
    else 
    begin 
     raise Exception.Create('Error updating the registry. This action requires administrative rights.'); 
    end; 
    finally 
    reg.Free; 
    end; 
end; 

如果您需要Windows事件日誌等日誌記錄要求,你也可以使用日誌框架,如log4dTraceTool


here如果你想寫入事件日誌Delphi IDE中的窗口。

+4

幹得好!只是你的日誌類。我希望他們的方法是實例方法而不是類方法,以避免重複註冊和註銷事件源。我會在創建類的實例時註冊事件源,並在銷燬時取消註冊。或者創建一個全局threadvar並初始化一次。 – TLama

+0

如果您在實例銷燬時取消註冊,是否不會導致無法再讀取現有的事件日誌條目?換句話說,實例必須處於活動狀態才能讓操作員查看舊的事件日誌條目?我寧願認爲註冊事件源應該是安裝的一部分,並取消註冊到可執行文件的卸載。 –

+0

嗨,TOndrey,我懷疑TLama意味着「RegisterEventSource」API調用,而不是「通過將註冊事件源添加到註冊表」:-) –