Articles   Members Online:
-Article/Tip Search
-News Group Search over 21 Million news group articles.
Member Area
-Account Center
-Top 10 NEW!!
-Submit Article/Tip
-Forums Upgraded!!
-My Articles
-Edit Information
-Become a Member
-Why sign up!
-Chat Online!
-Indexes NEW!!
-Build your resume
-Find a job
-Post a job
-Resume Search
-Link to us
Visit Embarcadero
Embarcadero Community
How to use EventLog change notification in real-time Turn on/off line numbers in source code. Switch to Orginial background IDE or DSP color Comment or reply to this aritlce/tip for discussion. Bookmark this article to my favorite article(s). Print this article
Delphi 6.x
User Rating
No Votes
# Votes
DSP, Administrator
Reference URL:
			Author: Fernando Silva 

I needed a way to be notified in real-time when someone acceded my computer inside 
an intranet. After doing some research, the solution would pass by using the 
Security event log that is used when you activate any audit option.


In the Control Panel\Administrative Tools\Local Security Police\Local Polices\Audit 
Police I could audit various account events; in my case I was specially looking for 
'Audit account logon events' and 'Audit logon events'.
After setting up these to audit success events, I could confirm that 'Security 
event log' really logs these attempts. But, how could I be notified by the system 
when a new event was added to the event log?

The timer - newbies solution

The first solution would be to use a timer that would check the event log count 
every second, and if the count was different from the last one the log has changed.

To test this we need to create a new project. Declare in the private part of the 
form the following:

1   private
2   { Private declarations }
3   FLastCount: Integer; // last event log count
4   FLog: THandle; // handle to the opened log
6   Because to work with the log we need to open it first, we do that in the 
7   Button1.Click;
9   procedure TForm1.Button1Click(Sender: TObject);
10  begin
11    FLog := OpenEventLog(nil, PChar('Security'));
12    Timer1.Enabled := True;
13  end;
15  //And now we only need to check the event log count
17  procedure TForm1.Timer1Timer(Sender: TObject);
18  var
19    lCount: Cardinal;
21  begin
22    if GetNumberOfEventLogRecords(FLog, lCount) and (lCount <> FLastCount) and 
23  (lCount >
24      0) then
25    begin
26      FLastCount := lCount;
27      ListBox1.Items.Add('Changed at ' + DateTimeToStr(Now()));
28    end;
29  end;

If we are pragmatic we know that this solution works in spite of the fact that 
there is a little problem. We know that if this were the best solution, I wouldn't 
be here writing this article ;)

Event object - professional solution

In the Win32 API under the event log group we can find a function that can help us 
detecting when the event log changes.
The NotifyChangeEventLog function lets an application receive notification when an 
event is written to the event log file specified by the hEventLog parameter.
When the event is written to the event log file, the function causes the event 
object specified by the hEvent parameter to become signaled.

Great, this is exactly what we want. But, what is an event object?
An event object is a object that can be created to signal operations between 
different system processes. The event object is under the same category has Mutex, 
Process and Semaphores.

To create an event object we use the CreateEvent function, which creates a named or 
unnamed event object.

The code to test this new option is:

30  private
31  { Private declarations }
32  FLog: THandle; // handle to the opened log
33  FEvent: THandle; // handle to the event object
35  procedure WaitForChange;
37  procedure TForm1.Button1Click(Sender: TObject);
38  begin
39    FLog := OpenEventLog(nil, PChar('Security'));
40    FEvent := CreateEvent(nil, True, False, nil); // create unnamed object
41    NotifyChangeEventLog(FLog, FEvent); // start the event log change notification
43    WaitForChange;
44  end;

The way we have to check if a existent event object is signaled or not is by using 
the WaitForSingleObject function. This function returns when one of the following 
the specified object is in the signaled state.
the time-out interval elapses.

Because, we don't know when the log changes we will not use a time-out interval, so 
the only way of this function returns is when the event object is in the signaled 

So, we will need a way of having a loop, which will be constantly calling 
WaitForSingleObject when it returns. This is the job for the recursive 
WaitForChange method.

45  procedure TForm1.WaitForChange;
46  var
47    lResult: Cardinal;
49  begin
50    // reset event signal, so the system can signal it again
51    ResetEvent(FEvent);
52    // wait for event to be signalled
53    lResult := WaitForSingleObject(FEvent, INFINITE);
54    // check event result
55    case lResult of
56      WAIT_OBJECT_0:
57        begin
58          ListBox1.Items.Add('Changed at ' + DateTimeToStr(Now()));
59          Application.ProcessMessages;
60        end;
61    end;
63    // wait for change again
64    WaitForChange;
65  end;

As you have noticed, this solution has a big problem. The application stops 
responding, but why? Well, WaitForSingleObject function checks the current state of 
the specified object. If the object's state is non signaled, the calling thread 
enters an efficient wait state. The thread consumes very little processor time 
while waiting for the object state to become signaled or the time-out interval to 
Unfortunaly the 'efficient wait state' makes the calling thread being as it was 
sleeping... sleeping as a rock!

If we didn't need to have an available interface, we could end here. But we need to 
have a working interface, at least to close the application.

The only solution to our case is to have a secondary thread, which will do this 
wait, and will call an event of the main thread when a change occurs.

The main code for this thread is:

66  procedure TNotifyChangeEventLog.Execute;
67  var
68    lResult: DWORD;
70  begin
71    while (not Terminated) do
72    begin
73      // reset event signal, so we can get it again
74      ResetEvent(FEventHandle);
75      // wait for event to happen
76      lResult := WaitForSingleObject(FEventHandle, INFINITE);
77      // check event result
78      case lResult of
79        WAIT_OBJECT_0: Synchronize(DoChange);
80      else
81        Synchronize(DoChange);
82      end;
83    end;
84  end;

The complete project

Because I needed more things than just to know if the event log changed or not, I 
ended up by creating a component to work with the event log.
This component is not just another event log component, because it incorporates 
more things than the usual: read event log, change notifications, functions to 
scroll the event log (First, Last, Eof, Next).

At this moment I've already sent this component to be approved for incorporating 
the JVCL (JEDI VCL) at
Because it steel wasn't approved I included it with this 

Final notes

Why all this trouble to use the event object solution if the timer solution was 
enough? The problem is that the event object solution is safer.

For example, if you wanted to use the RegNotifyChangeKeyValue function that 
notifies the caller about changes to the attributes or contents of a specified 
registry key.
What you would do? You're right, it would almost impossible to use a timer, and would be so simple to use an event object.

Vote: How useful do you find this Article/Tip?
Bad Excellent
1 2 3 4 5 6 7 8 9 10


Share this page
Download from Google

Copyright © Mendozi Enterprises LLC