Visit our Sponsor   Visit our Sponsor
delphi3000.com - the free delphi knowledge platform
delphi3000.com - the free delphi knowledge platform
Have a look at your member-status

connecting people's knowledge


  - Recent ArticlesRSS feed for Recent Articles on delphi3000.com
  - List of All Articles
  - Top Viewed Articles
  - Articles (+Attachem.)
  - Articles Of Interest
  - Categories
  - Top Uploader
  - Search
  - Index

  - My Home
  - Submit an Article
  - My Articles
  - My Personal Data
  - My Bookmarks
  - Activities
  - Login/Logout

  - Sign Up
  - Why Sign Up
  - Newsletter

  - Press
  - Advertise

  - Contact
  - Feedback





Community
Borland
ClubeDelphi
Dr. Bob
UK-BUG
Delphi Meetings
Planeta Delphi







Startblatt.de






Share this article with friendsShare this article with friends
Rate this articleRate this article - to keep the quality of delphi3000.com !
Comment this article or read through previous comments (4)


EventLog change notification in real-timeGo to Fernando Silva's websiteComponent available for this articleFormat this article printer-friendly!Bookmark function is only available for registered users!
Product:
Delphi 5.x (or higher)
Category:
System
Skill Level:
Scoring:
Last Update:
09/16/2002
Search Keys:
delphi delphi3000 article borland vcl code-snippet event log notification CreateEvent NotifyChangeEventLog RegNotifyChangeKeyValue
Times Scored:
5
Visits:
5484
Uploader: Fernando Silva
Company: W3 Computadores, Lda
Reference: N/A
Component Download: http://www.delphi3000.com/article/3358/3358.zip
 
Question/Problem/Abstract:
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.
Answer:



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:


private
  { Private declarations }
  FLastCount: Integer; // last event log count
  FLog: THandle; // handle to the opened log


Because to work with the log we need to open it first, we do that in the Button1.Click;


procedure TForm1.Button1Click(Sender: TObject);
begin
  FLog := OpenEventLog(nil, PChar('Security'));
  Timer1.Enabled := True;
end;


And now we only need to check the event log count


procedure TForm1.Timer1Timer(Sender: TObject);
var
  lCount: Cardinal;

begin
  if GetNumberOfEventLogRecords(FLog, lCount) and (lCount <> FLastCount) and (lCount > 0) then
  begin
    FLastCount := lCount;
    ListBox1.Items.Add('Changed at ' + DateTimeToStr(Now()));
  end;
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:


private
  { Private declarations }
  FLog: THandle; // handle to the opened log
  FEvent: THandle; // handle to the event object
  procedure WaitForChange;



procedure TForm1.Button1Click(Sender: TObject);
begin
  FLog := OpenEventLog(nil, PChar('Security'));
  FEvent := CreateEvent(nil, True, False, nil); // create unnamed object
  NotifyChangeEventLog(FLog, FEvent); // start the event log change notification

  WaitForChange;
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 occurs:
· 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 state.

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.


procedure TForm1.WaitForChange;
var
  lResult: Cardinal;

begin
  // reset event signal, so the system can signal it again
  ResetEvent(FEvent);
  // wait for event to be signalled
  lResult := WaitForSingleObject(FEvent, INFINITE);
  // check event result
  case lResult of
    WAIT_OBJECT_0:
      begin
        ListBox1.Items.Add('Changed at ' + DateTimeToStr(Now()));
        Application.ProcessMessages;
      end;
  end;
  
  // wait for change again
  WaitForChange;
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 nonsignaled, 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 elapse.
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:


procedure TNotifyChangeEventLog.Execute;
var
  lResult: DWORD;

begin
  while (not Terminated) do
  begin
    // reset event signal, so we can get it again
    ResetEvent(FEventHandle);
    // wait for event to happen
    lResult := WaitForSingleObject(FEventHandle, INFINITE);
    // check event result
    case lResult of
      WAIT_OBJECT_0: Synchronize(DoChange);
    else
      Synchronize(DoChange);
    end;
  end;
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 aproved for incorporating the JVCL (JEDI VCL) at http://jvcl.sourceforge.net
Because it steel wasn't aproved I inclued it with this sample.

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.





Please rate this article!
Skill level:
BeginnerExpert

Useful:
No!Very!

Overall rating:
PoorExcellent



Comments to this article
Write a new comment
Very Nice!
    Jonathon Hibbard (Sep 4 2002 6:10PM)

Glad to finally see solid security notification code!
Respond

Attached files missing
    Fernando Silva (Sep 3 2002 5:52PM)

Just to say that there are missing files, but soon they will be attached to this article.
Respond

infinite calls
    Eber Irigoyen (Sep 3 2002 5:43PM)

in your nested call to WaitForChange you'll run out of memory at some point, the solution would just be to put it inside a Repeat Until or some other loop

salu2
EberSys
Respond

RE: infinite calls
Fernando Silva (Sep 3 2002 5:50PM)

You're right. Recursive calls without exiting them, increase memory usage, and a memory error can occur any time.

Fortunally that function was just to experiment the NotifyChangeEventLog in a form, and it is not really used, because it was then substitued by the "real" code within a thread.
Respond














 
Sign up to consume product discounts for Bronze memberships !

read more


  Visit our Sponsor

 

  Community Ad of
Hans Gulö
 
   














 







     
  Copyright © 2000 - 2007 delphi3000.com - All rights reserved. Terms of use. || Privacy
delphi3000.com is a service by bluestep.com IT-Services GmbH (Vienna)