I've written some classes that writes to and reads from the Windows event log. The TRBWindowsEventLogs class contains the writer and reader objects, when creating the object from this class it requires the application name to be passed, this is can be different is required to the application, but is also used to retrieve the log entries.
unit uWindowsEvents;
interface
uses classes, Windows, SvcMgr, Vcl.StdCtrls, Generics.Collections;
type
{ /----------------------------------------------------------------------------------------------------------------- }
TRBWindowsEvent = class(TObject)
strict private
fRecordNumber: integer;
fMessage: string;
fComputerName: string;
fEventData: string;
fLogFile: string;
fCategory: string;
fEventCode: integer;
public
property Category: string read fCategory write fCategory;
property ComputerName: string read fComputerName write fComputerName;
property EventCode: integer read fEventCode write fEventCode;
property Message: string read fMessage write fMessage;
property RecordNumber: integer read fRecordNumber write fRecordNumber;
property LogFile: string read fLogFile write fLogFile;
property EventData: string read fEventData write fEventData;
procedure Populate(aEvent: OLEVariant);
end;
{ /----------------------------------------------------------------------------------------------------------------- }
IRBEventReaderOutput = interface
['{4ADA872A-C1DA-4B3B-BE67-0F628C61039C}']
procedure AddEventLog(aEventLog: TRBWindowsEvent);
procedure SaveToFile(aFileName: string);
function OutputString: string;
end;
{ /----------------------------------------------------------------------------------------------------------------- }
TRBWindowsEventLogsReader = class(TObjectList<TRBWindowsEvent>)
strict private
fApplicationName: string;
fMaxNumberOfEntries: integer;
function EventQuery: string;
procedure GetWindowsEventLogs;
procedure AddErrorMessage(aErrorMessage: string);
public
property MaxNumberOfEntries: integer read fMaxNumberOfEntries write fMaxNumberOfEntries;
constructor Create(aApplicationName: string; aMaxNumberOfEntries: integer);
procedure PopulateEvents(aReaderOutput: IRBEventReaderOutput);
end;
{ /----------------------------------------------------------------------------------------------------------------- }
TRBWindowsEventLogsWriter = class(TObject)
strict private
fWindowsEventLogger: TEventLogger;
public
constructor Create(aApplicationName: string);
destructor Destroy; override;
procedure WriteInformationToWindowsEvents(aMessage: string);
procedure WriteErrorToWindowsEvents(aMessage: string);
end;
{ /----------------------------------------------------------------------------------------------------------------- }
TRBWindowsEventLogs = class(TObject)
strict private
fApplicationName: string;
fWriter: TRBWindowsEventLogsWriter;
fReader: TRBWindowsEventLogsReader;
public
property Writer: TRBWindowsEventLogsWriter read fWriter;
property Reader: TRBWindowsEventLogsReader read fReader;
constructor Create(aApplicationName: string); overload;
constructor Create(aApplicationName: string; aMaxNumberOfEntries: integer); overload;
destructor Destroy; override;
end;
implementation
uses SysUtils, ComObj, ActiveX, System.Variants, DateUtils;
{ TRBWindowsEvent }
procedure TRBWindowsEvent.Populate(aEvent: OLEVariant);
var
insertion: array of String;
i: integer;
begin
fCategory := string(aEvent.Category);
fComputerName := string(aEvent.ComputerName);
fEventCode := integer(aEvent.EventCode);
fMessage := string(aEvent.Message);
fRecordNumber := integer(aEvent.RecordNumber);
fLogFile := string(aEvent.LogFile);
if not VarIsNull(aEvent.InsertionStrings) then
begin
insertion := aEvent.InsertionStrings;
for i := VarArrayLowBound(insertion, 1) to VarArrayHighBound(insertion, 1) do
begin
fEventData := fEventData + insertion[i];
end;
end;
end;
{ TRBWindowsEvents }
constructor TRBWindowsEventLogs.Create(aApplicationName: string);
begin
Create(aApplicationName, 100);
end;
constructor TRBWindowsEventLogs.Create(aApplicationName: string; aMaxNumberOfEntries: integer);
begin
inherited Create;
fApplicationName := aApplicationName;
fWriter := TRBWindowsEventLogsWriter.Create(aApplicationName);
fReader := TRBWindowsEventLogsReader.Create(aApplicationName, aMaxNumberOfEntries);
end;
destructor TRBWindowsEventLogs.Destroy;
begin
FreeAndNil(fReader);
FreeAndNil(fWriter);
inherited;
end;
{ TRBWindowsEventLogsWriter }
constructor TRBWindowsEventLogsWriter.Create(aApplicationName: string);
begin
inherited Create;
fWindowsEventLogger := TEventLogger.Create(aApplicationName);
end;
destructor TRBWindowsEventLogsWriter.Destroy;
begin
FreeAndNil(fWindowsEventLogger);
inherited;
end;
procedure TRBWindowsEventLogsWriter.WriteErrorToWindowsEvents(aMessage: string);
begin
fWindowsEventLogger.LogMessage(aMessage, EVENTLOG_ERROR_TYPE);
end;
procedure TRBWindowsEventLogsWriter.WriteInformationToWindowsEvents(aMessage: string);
begin
fWindowsEventLogger.LogMessage(aMessage, EVENTLOG_INFORMATION_TYPE);
end;
{ TRBWindowsEventLogsReader }
constructor TRBWindowsEventLogsReader.Create(aApplicationName: string; aMaxNumberOfEntries: integer);
begin
inherited Create;
fApplicationName := aApplicationName;
fMaxNumberOfEntries := aMaxNumberOfEntries;
end;
function TRBWindowsEventLogsReader.EventQuery: string;
begin
Result := 'SELECT * FROM Win32_NTLogEvent Where SourceName = "' + fApplicationName +
'" AND Logfile = "Application" AND TimeGenerated >= "' + DateTimeToStr(IncDay(Now(), -1)) + '"';
end;
procedure TRBWindowsEventLogsReader.AddErrorMessage(aErrorMessage: string);
var
event: TRBWindowsEvent;
begin
event := TRBWindowsEvent.Create;
event.Category := 'Error';
event.Message := aErrorMessage;
Add(event);
end;
procedure TRBWindowsEventLogsReader.GetWindowsEventLogs;
const
wbemForwardOnly = 32;
wbemReturnImmediately = 16;
var
SWbemLocator: OLEVariant;
WMIService: OLEVariant;
WbemObjectSet: OLEVariant;
WbemObject: OLEVariant;
oEnum: IEnumvariant;
iValue: LongWord;
iCount: integer;
event: TRBWindowsEvent;
begin
try
Clear;
iCount := 0;
SWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
WMIService := SWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
WbemObjectSet := WMIService.ExecQuery(EventQuery(), 'WQL', wbemReturnImmediately + wbemForwardOnly);
oEnum := IUnknown(WbemObjectSet._NewEnum) as IEnumvariant;
while oEnum.Next(1, WbemObject, iValue) = 0 do
begin
event := TRBWindowsEvent.Create;
event.Populate(WbemObject);
Add(event);
WbemObject := Unassigned;
inc(iCount);
if iCount > fMaxNumberOfEntries then
begin
Break;
end;
end;
except
on E: EOleException do
AddErrorMessage(Format('EOleException %s %x', [E.Message, E.ErrorCode]));
on E: Exception do
AddErrorMessage(E.Classname + ':' + E.Message);
end;
end;
procedure TRBWindowsEventLogsReader.PopulateEvents(aReaderOutput: IRBEventReaderOutput);
var
event: TRBWindowsEvent;
begin
GetWindowsEventLogs;
for event in Self do
begin
aReaderOutput.AddEventLog(event);
end;
end;
end.
To use these classes I've created some output classes implemented from the IRBEventReaderOutput interface.
unit uWindowsEventsOutput;
interface
uses classes, Windows, uWindowsEvents, System.JSON;
type
{ /----------------------------------------------------------------------------------------------------------------- }
TStringsReaderOutput = class(TInterfacedObject, IRBEventReaderOutput)
strict private
fStrings: TStringList;
public
procedure AddEventLog(aEventLog: TRBWindowsEvent);
procedure SaveToFile(aFileName: string);
function OutputString: string;
constructor Create;
destructor Destroy; override;
end;
{ /----------------------------------------------------------------------------------------------------------------- }
TJSONReaderOutput = class(TInterfacedObject, IRBEventReaderOutput)
strict private
fJSON_Array: TJSONArray;
public
procedure AddEventLog(aEventLog: TRBWindowsEvent);
procedure SaveToFile(aFileName: string);
function OutputString: string;
constructor Create;
destructor Destroy; override;
end;
{ /----------------------------------------------------------------------------------------------------------------- }
TCSVReaderOutput = class(TInterfacedObject, IRBEventReaderOutput)
strict private
fCSVString: string;
procedure AddHeader;
procedure AddLine(aLine: string);
public
procedure AddEventLog(aEventLog: TRBWindowsEvent);
procedure SaveToFile(aFileName: string);
function OutputString: string;
end;
implementation
uses SysUtils;
{ TStringsReaderOutput }
constructor TStringsReaderOutput.Create;
begin
inherited Create;
fStrings := TStringList.Create;
end;
destructor TStringsReaderOutput.Destroy;
begin
FreeAndNil(fStrings);
inherited;
end;
function TStringsReaderOutput.OutputString: string;
begin
Result := fStrings.CommaText;
end;
procedure TStringsReaderOutput.SaveToFile(aFileName: string);
begin
fStrings.SaveToFile(aFileName);
end;
procedure TStringsReaderOutput.AddEventLog(aEventLog: TRBWindowsEvent);
begin
fStrings.Add('Category: ' + aEventLog.Category);
fStrings.Add('Computer Name: ' + aEventLog.ComputerName);
fStrings.Add('Event Code: ' + aEventLog.EventCode.ToString);
fStrings.Add('Message: ' + aEventLog.Message);
fStrings.Add('Record Number: ' + aEventLog.RecordNumber.ToString);
fStrings.Add('Log File: ' + aEventLog.LogFile);
fStrings.Add('Event Data');
fStrings.Add(aEventLog.EventData);
fStrings.Add('-------------------------');
end;
{ TJSONReaderOutput }
constructor TJSONReaderOutput.Create;
begin
inherited Create;
fJSON_Array := TJSONArray.Create;
end;
destructor TJSONReaderOutput.Destroy;
begin
FreeAndNil(fJSON_Array);
inherited;
end;
function TJSONReaderOutput.OutputString: string;
begin
if Assigned(fJSON_Array) then
begin
Result := fJSON_Array.ToJSON;
end;
end;
procedure TJSONReaderOutput.SaveToFile(aFileName: string);
var
sl: TStringList;
begin
sl := TStringList.Create;
try
sl.Text := fJSON_Array.ToJSON;
sl.SaveToFile(aFileName);
finally
sl.Free;
end;
end;
procedure TJSONReaderOutput.AddEventLog(aEventLog: TRBWindowsEvent);
var
JSON_Object: TJSONObject;
begin
JSON_Object := TJSONObject.Create;
JSON_Object.AddPair('category', aEventLog.Category);
JSON_Object.AddPair('computerName', aEventLog.ComputerName);
JSON_Object.AddPair('eventCode', aEventLog.EventCode.ToString);
JSON_Object.AddPair('message', aEventLog.Message);
JSON_Object.AddPair('recordNumber', aEventLog.RecordNumber.ToString);
JSON_Object.AddPair('logFile', aEventLog.LogFile);
JSON_Object.AddPair('eventData', aEventLog.EventData);
fJSON_Array.Add(JSON_Object);
end;
{ TCSVReaderOutput }
procedure TCSVReaderOutput.AddEventLog(aEventLog: TRBWindowsEvent);
var
s: string;
procedure AddValue(aValue: string);
begin
s := s + aValue + ',';
end;
begin
AddHeader;
AddValue(aEventLog.Category);
AddValue(aEventLog.ComputerName);
AddValue(aEventLog.EventCode.ToString);
AddValue(aEventLog.Message);
AddValue(aEventLog.RecordNumber.ToString);
AddValue(aEventLog.LogFile);
AddValue(aEventLog.EventData);
AddLine(s);
end;
procedure TCSVReaderOutput.AddHeader;
begin
if fCSVString = '' then
begin
AddLine('category,computerName,eventCode,message,recordNumber,logFile,eventData,');
end;
end;
procedure TCSVReaderOutput.AddLine(aLine: string);
begin
fCSVString := fCSVString + aLine + chr(13) + chr(10);
end;
function TCSVReaderOutput.OutputString: string;
begin
Result := fCSVString;
end;
procedure TCSVReaderOutput.SaveToFile(aFileName: string);
var
sl: TStringList;
begin
sl := TStringList.Create;
try
sl.Text := fCSVString;
sl.SaveToFile(aFileName);
finally
sl.Free;
end;
end;
end.
Here are some examples of how to use the output classes.
To write to the Events Log.
procedure TfrmWindowsEvents.AddLogBtnClick(Sender: TObject);
begin
fWindowsEvents.Writer.WriteInformationToWindowsEvents(MessageEdt.Text);
end;
To read from the Events Log
procedure TfrmWindowsEvents.StringBtnClick(Sender: TObject);
var
stringsReader: IRBEventReaderOutput;
begin
stringsReader := TStringsReaderOutput.Create;
fWindowsEvents.Reader.PopulateEvents(stringsReader);
stringsReader.SaveToFile('stringsoutput.txt');
MemoEvents.Lines.CommaText := stringsReader.OutputString;
end;
procedure TfrmWindowsEvents.JsonBtnClick(Sender: TObject);
var
jsonReader: IRBEventReaderOutput;
begin
jsonReader := TJSONReaderOutput.Create;
fWindowsEvents.Reader.PopulateEvents(jsonReader);
jsonReader.SaveToFile('jsonoutput.txt');
MemoEvents.Lines.Text := jsonReader.OutputString;
end;
procedure TfrmWindowsEvents.CsvBtnClick(Sender: TObject);
var
csvReader: IRBEventReaderOutput;
begin
csvReader := TCSVReaderOutput.Create;
fWindowsEvents.Reader.PopulateEvents(csvReader);
csvReader.SaveToFile('csvoutput.csv');
MemoEvents.Lines.Text := csvReader.OutputString;
end;
One thing to note is that querying the events can be very slow depending on the amount of logs in the database. You can improve this in the Event Viewer application by selecting Windows Logs > Application and from either the main menu 'Action' or from the right click menu select 'Clear Logs'.