Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Andrzejek on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Anyone used VFP to read the windows event log?

Status
Not open for further replies.

GriffMG

Programmer
Mar 4, 2002
6,318
FR
I am looking at reading the event log to look for possible 'probes' and update the firewall rules automatically

Anyone done this already?

Regards

Griff
Keep [Smile]ing

There are 10 kinds of people in the world, those who understand binary and those who don't.

I'm trying to cut down on the use of shrieks (exclamation marks), I'm told they are !good for you.

There is no place like G28 X0 Y0 Z0
 
Thank you Gerrit

I found this article
It ought to be possible to read the log from within VFP, without exporting the logs... but if I can't
get my head round it... B-)

Regards

Griff
Keep [Smile]ing

There are 10 kinds of people in the world, those who understand binary and those who don't.

I'm trying to cut down on the use of shrieks (exclamation marks), I'm told they are !good for you.

There is no place like G28 X0 Y0 Z0
 
For starters, Griff:

Code:
o = CreateObject("WindowsEventLog",".","Application")
? o.GetNumberOfRecords()
Displays some ten thousands for me, and trying to read the log (this class only reads a full log or nothing) takes a looong time.
But I don't think this is intended to read one of the standard logs, but one you make yourself, for your own application.

Chriss
 
Hi Chriss

The link I found actually works, albeit it is hardly the two line answer you've given!

It is a slow process though.

I claim no ownership or authorship of the code below, 99.99% taken from the github link
above and dropped into a single .prg

Code:
#DEFINE EVENTLOG_SEQUENTIAL_READ 1
#DEFINE EVENTLOG_FORWARDS_READ 4
#DEFINE ERROR_INSUFFICIENT_BUFFER 122
#DEFINE WE_START_DATETIME {^1970/01/01 00:00:00}
#DEFINE ERROR_HANDLE_EOF 38
#DEFINE EVENTLOGRECORD_SIZE 56
#DEFINE MAX_BUFFER_SIZE 0x7ffff
#DEFINE EVENTLOG_FULL_INFO 0
#DEFINE FACILITY_NULL 0

LOCAL welog As WindowsEventLog,;  
	werecord As WindowsEventRecord, nReccount  

  
welog = CREATEOBJECT("WindowsEventLog",;  
	NULL, "Security")  
  
WAIT WINDOW NOWAIT "Reading log records..."  
nReccount = welog.ReadLogRecords()  
WAIT CLEAR  
  
CREATE CURSOR csResult ( recordnumber I,;  
	timegenerated T, timewritten T,;  
	eventid I, eventtype I, numstrings I,;  
	eventcategory I, stringoffset I,;  
	datalength I, dataoffset I, strings M,;  
	eventdata M, eventbuffer M,;  
	sysname C(32), source C(200))  
  
FOR EACH werecord IN welog.WindowEventRecords  
	WITH werecord  
		INSERT INTO csResult VALUES (.recordnumber,;  
			WE_START_DATETIME+.timegenerated,;  
			WE_START_DATETIME+.timewritten,;  
			.eventid, .eventtype, .numstrings,;  
			.eventcategory, .stringoffset,;  
			.datalength, .dataoffset,;  
			.strings, .eventdata, .eventbuffer,;  
			.sysname, .source)  
	ENDWITH  
NEXT

SELECT csResult
GO TOP
BROWSE
QUIT


DEFINE CLASS WindowsEventLog As Session
PROTECTED servername, logname, hEventLog
	WindowEventRecords=NULL
	servername=NULL
	logname=NULL
	hEventLog=0
	errorno=0
	errormessage=""

PROCEDURE Init(cServerName As String, cLogName As String)
	THIS.declare
	THIS.logname = m.cLogName
	THIS.servername = m.cServerName
	THIS.WindowEventRecords = CREATEOBJECT("Collection")

PROCEDURE Destroy
	THIS.CloseLog

PROCEDURE ResetError
	THIS.SetError(0, "")

PROCEDURE SetError(nErrorNo, cErrorMessage)
	THIS.errorno=m.nErrorNo
	THIS.errormessage=m.cErrorMessage

FUNCTION GetNumberOfRecords() As Number
	IF NOT THIS.OpenLog()
		RETURN -1
	ENDIF

	LOCAL nRecno, nResult
	nRecno=0
	
	nResult = GetNumberOfEventLogRecords(;
		THIS.hEventLog, @nRecno)

	IF nResult = 0
		THIS.SetError(GetLastError(),;
			"Call to GetNumberOfEventLogRecords() failed.")
		nRecno = -1
	ENDIF

	THIS.CloseLog
RETURN m.nRecno

FUNCTION IsLogFull() As Boolean
* indicates whether the event log is full
	LOCAL nResult, nLogFull, nBufsize
	STORE 0 TO nLogFull, nBufsize

	IF NOT THIS.OpenLog()
		RETURN .F.
	ENDIF

	nResult = GetEventLogInformation(THIS.hEventLog,;
		EVENTLOG_FULL_INFO, @nLogFull, 4, @nBufsize)

	IF nResult = 0
		THIS.SetError(GetLastError(),;
			"Call to GetEventLogInformation() failed.")
	ENDIF
	THIS.CloseLog
RETURN (m.nLogFull <> 0)

FUNCTION ClearLog(cBackupFile As String) As Boolean
	IF NOT THIS.OpenLog()
		RETURN .F.
	ENDIF

	IF EMPTY(m.cBackupFile)
		cBackupFile=NULL
	ENDIF
	
	LOCAL nResult, nError
	nResult = ClearEventLog(THIS.hEventLog, m.cBackupFile)

	IF m.nResult = 0
		THIS.SetError(GetLastError(),;
			"Call to ClearEventLog() failed.")
	ENDIF
	THIS.CloseLog
RETURN (m.nResult <> 0)

PROCEDURE ReadLogRecords
	DO WHILE THIS.WindowEventRecords.Count > 0
		THIS.WindowEventRecords.Remove(1)
	ENDDO
	
	THIS.CloseLog
	THIS.hEventLog = OpenEventLog(THIS.servername, THIS.logname)

	IF THIS.hEventLog = 0
		THIS.SetError(GetLastError(), "Call to OpenEventLog() failed.")
		RETURN 0
	ENDIF

	LOCAL nRecno, nReccount, nIndex, oBuffer As PChar, nBufsize,;
		nBytesRead, hBuffer, nBytesNeeded, nResult, nLastError,;
		nReadCount

	STORE 0 TO nRecno, nReccount
	= GetOldestEventLogRecord(THIS.hEventLog, @nRecno)
	= GetNumberOfEventLogRecords(THIS.hEventLog, @nReccount)

	nBufsize = MAX_BUFFER_SIZE
	oBuffer = CREATEOBJECT("PChar", REPLICATE(CHR(0), nBufsize))

	DO WHILE .T.
		oBuffer.SetValue(REPLICATE(CHR(0), nBufsize))
		hBuffer = oBuffer.GetAddr()
		STORE 0 TO nBytesRead, nBytesNeeded

		nResult = ReadEventLog(THIS.hEventLog,;
			BITOR(EVENTLOG_SEQUENTIAL_READ, EVENTLOG_FORWARDS_READ), 0,;
			m.hBuffer, nBufsize, @nBytesRead, @nBytesNeeded)

		IF nResult <> 0
			THIS.ProcessBuffer(@oBuffer, m.nBytesRead)
			LOOP
		ENDIF

		nLastError = GetLastError()
		DO CASE
		CASE nLastError = ERROR_HANDLE_EOF
			EXIT
		OTHERWISE
			THIS.SetError(m.nLastError, "ReadEventLog call failed.")
			EXIT
		ENDCASE
	ENDDO

	THIS.CloseLog
RETURN THIS.WindowEventRecords.Count

PROTECTED PROCEDURE ProcessBuffer(oBuffer As PChar, nBytesRead As Number)
	LOCAL hBuffer, cMBuffer, cBuffer, nOffset, nRecordLength,;
		werecord As WindowsEventRecord, cStrings

	hBuffer = oBuffer.GetAddr()
	cMBuffer = SUBSTR(oBuffer.GetValue(), 1, m.nBytesRead)
	nOffset=1

	DO WHILE .T.
		nRecordLength = buf2dword(SUBSTR(m.cMBuffer, nOffset, 4))
		cBuffer = SUBSTR(m.cMBuffer, nOffset, nRecordLength)

		werecord = CREATEOBJECT("WindowsEventRecord")
		WITH werecord
			.eventbuffer = m.cBuffer
			.recordnumber = buf2dword(SUBSTR(cBuffer, 9,4))
			.timegenerated = buf2dword(SUBSTR(cBuffer, 13,4))
			.timewritten = buf2dword(SUBSTR(cBuffer, 17,4))
			.eventid = buf2dword(SUBSTR(cBuffer, 21,4))
			.eventtype = buf2word(SUBSTR(cBuffer, 25,2))
			.numstrings = buf2word(SUBSTR(cBuffer, 27,2))
			.eventcategory = buf2word(SUBSTR(cBuffer, 29,2))
			.stringoffset = buf2dword(SUBSTR(cBuffer, 37,4))
			.datalength = buf2dword(SUBSTR(cBuffer, 49,4))
			.dataoffset = buf2dword(SUBSTR(cBuffer, 53,4))
			
			IF .numstrings > 0
				cStrings = SUBSTR(cBuffer, .stringoffset+1)
				.strings = SUBSTR(cStrings, 1,;
					AT(CHR(0), cStrings, .numstrings))
			ENDIF
			
			IF .datalength > 0
				.eventdata = SUBSTR(cBuffer, .dataoffset+1, .datalength)
			ENDIF
			
			cStrings = SUBSTR(cBuffer, EVENTLOGRECORD_SIZE+1)
			.source = SUBSTR(cStrings, 1, AT(CHR(0), cStrings)-1)
			.sysname = SUBSTR(cStrings, AT(CHR(0), cStrings)+1)
			.sysname = SUBSTR(.sysname, 1, AT(CHR(0), .sysname)-1)
		ENDWITH

		THIS.WindowEventRecords.Add(werecord)
		werecord=NULL

		nOffset = m.nOffset + m.nRecordLength
		IF nOffset > LEN(m.cMBuffer) - EVENTLOGRECORD_SIZE
			EXIT
		ENDIF
	ENDDO

PROTECTED FUNCTION OpenLog() As Boolean
	IF THIS.hEventLog = 0
		THIS.hEventLog = OpenEventLog(;
			THIS.servername, THIS.logname)
	ENDIF
RETURN (THIS.hEventLog <> 0)

PROTECTED PROCEDURE CloseLog
	IF THIS.hEventLog <> 0
		= CloseEventLog(THIS.hEventLog)
		THIS.hEventLog = 0
	ENDIF

PROTECTED PROCEDURE declare
	DECLARE INTEGER GetLastError IN kernel32
	DECLARE INTEGER CloseEventLog IN advapi32 INTEGER hEventLog

	DECLARE INTEGER OpenEventLog IN advapi32;
		STRING lpUNCServerName, STRING lpSourceName

	DECLARE INTEGER GetOldestEventLogRecord IN advapi32;
		INTEGER hEventLog, LONG @OldestRecord

	DECLARE INTEGER GetNumberOfEventLogRecords IN advapi32;
		INTEGER hEventLog, LONG @NumberOfRecords

	DECLARE INTEGER ReadEventLog IN advapi32;
		INTEGER hEventLog, LONG dwReadFlags, LONG dwRecordOffset,;
		INTEGER lpBuffer, LONG nNumberOfBytesToRead,;
		LONG @pnBytesRead, LONG @pnMinNumberOfBytesNeeded

	DECLARE INTEGER ClearEventLog IN advapi32;
		INTEGER hEventLog, STRING lpBackupFileName

	DECLARE INTEGER GetEventLogInformation IN advapi32;
		INTEGER hEventLog, LONG dwInfoLevel, LONG @lpBuffer,;
		LONG cbBufSize, LONG @pcbBytesNeeded

ENDDEFINE

DEFINE CLASS WindowsEventRecord As Session
	eventbuffer=""
	recordnumber=0
	timegenerated=0
	timewritten=0
	eventid=0
	eventtype=0
	numstrings=0
	eventcategory=0
	stringoffset=0
	datalength=0
	dataoffset=0
	strings=""
	eventdata=""
	source=""
	sysname=""

FUNCTION GetString(nIndex As Number) As String
* returns a string from event record data
* specified by string index
	IF THIS.numstrings < nIndex
		RETURN ""
	ENDIF

	LOCAL nPosStart, nPosEnd
	nPosEnd = AT(CHR(0), THIS.strings, nIndex)
	nPosStart = IIF(m.nIndex=1, 1, AT(CHR(0), THIS.strings, nIndex-1)+1)
RETURN SUBSTR(THIS.strings, nPosStart, nPosEnd-nPosStart)

FUNCTION UtcSecondsToDateTime(nSeconds As Number) As Datetime
RETURN WE_START_DATETIME + m.nSeconds

ENDDEFINE

DEFINE CLASS PChar As Session  && pointer to string
PROTECTED hMem

PROCEDURE Init(lcString)
	THIS.hMem = 0
	THIS.setValue(lcString)

PROCEDURE Destroy
	THIS.ReleaseString

FUNCTION GetAddr
RETURN THIS.hMem

FUNCTION GetValue
	LOCAL lnSize, lcBuffer
	lnSize = THIS.getAllocSize()
	lcBuffer = SPACE(lnSize)

	IF THIS.hMem <> 0
		DECLARE RtlMoveMemory IN kernel32 As MemToStr;
			STRING @, INTEGER, INTEGER
		= MemToStr(@lcBuffer, THIS.hMem, lnSize)
	ENDIF
RETURN lcBuffer

FUNCTION GetAllocSize
	DECLARE INTEGER GlobalSize IN kernel32 INTEGER hMem
RETURN Iif(THIS.hMem=0, 0, GlobalSize(THIS.hMem))

PROCEDURE SetValue(lcString)
#DEFINE GMEM_FIXED 0
	THIS.ReleaseString

	DECLARE INTEGER GlobalAlloc IN kernel32 INTEGER, INTEGER
	DECLARE RtlMoveMemory IN kernel32 As StrToMem;
		INTEGER, STRING @, INTEGER

	LOCAL lnSize
	lcString = lcString + Chr(0)
	lnSize = Len(lcString)
	THIS.hMem = GlobalAlloc(GMEM_FIXED, lnSize)
	IF THIS.hMem <> 0
		= StrToMem(THIS.hMem, @lcString, lnSize)
	ENDIF

PROCEDURE ReleaseString
	IF THIS.hMem <> 0
		DECLARE INTEGER GlobalFree IN kernel32 INTEGER
		= GlobalFree (THIS.hMem)
		THIS.hMem = 0
	ENDIF
ENDDEFINE

FUNCTION buf2dword(cBuffer)
RETURN Asc(SUBSTR(cBuffer, 1,1)) + ;
	BitLShift(Asc(SUBSTR(cBuffer, 2,1)),  8) +;
	BitLShift(Asc(SUBSTR(cBuffer, 3,1)), 16) +;
	BitLShift(Asc(SUBSTR(cBuffer, 4,1)), 24)

FUNCTION buf2word(lcBuffer)
RETURN Asc(SUBSTR(lcBuffer, 1,1)) + ;
       Asc(SUBSTR(lcBuffer, 2,1)) * 256


Regards

Griff
Keep [Smile]ing

There are 10 kinds of people in the world, those who understand binary and those who don't.

I'm trying to cut down on the use of shrieks (exclamation marks), I'm told they are !good for you.

There is no place like G28 X0 Y0 Z0
 
Griff,

it is really beyond me, how you cannot see this.

my usage exameple is using the class definition you liknked to. Yes, OF COURSE, you have to first establish this whole class from the link you've given.

Here's how the code starts, Griff:
Code:
#DEFINE EVENTLOG_SEQUENTIAL_READ 1
...some more constatn definitions
DEFINE CLASS WindowsEventLog As Session
PROTECTED servername, logname, hEventLog
...

And there is no usage example, it's just a class definition. What do you do with a class, Griff? You do a CREATEOBJECT, and then you call methods.

My short code needs this class definition, it's just the usage example, Griff, but it's all you need additionally to the class definiiotn. Is that clearer, now?


Chriss
 
Chris

I did not realise your example was using the class in the link...




Regards

Griff
Keep [Smile]ing

There are 10 kinds of people in the world, those who understand binary and those who don't.

I'm trying to cut down on the use of shrieks (exclamation marks), I'm told they are !good for you.

There is no place like G28 X0 Y0 Z0
 
Griff,

my code:
Code:
o = CreateObject("[highlight #FCE94F]WindowsEventLog[/highlight]",...

class definition:
Code:
DEFINE CLASS [highlight #FCE94F]WindowsEventLog[/highlight] As Session

Do I really need to point out my few lines need this class definition?

Chriss
 
In this case you did!
I hadn't even read the class definition when I posted my link!

Regards

Griff
Keep [Smile]ing

There are 10 kinds of people in the world, those who understand binary and those who don't.

I'm trying to cut down on the use of shrieks (exclamation marks), I'm told they are !good for you.

There is no place like G28 X0 Y0 Z0
 
@Strongm

I'll take a look

Regards

Griff
Keep [Smile]ing

There are 10 kinds of people in the world, those who understand binary and those who don't.

I'm trying to cut down on the use of shrieks (exclamation marks), I'm told they are !good for you.

There is no place like G28 X0 Y0 Z0
 
Indeed, that you can use "." as servername for the local computer is a strong hint WMI is involved anyway. With WMI you should be able to do an actual query from a log, too, which gives a higher flexibility about which entries you want to read.

Chriss
 
Griff,

have you even searched the forum?

in thread184-763970 Mike Gaganon is using WMI to read from the event log.

Chriss
 
I did not find that, I did search for Windows Event Log, but I didn't find that, thank you... trouble with searches, sometimes you need to know the answer
to phrase the question!


Regards

Griff
Keep [Smile]ing

There are 10 kinds of people in the world, those who understand binary and those who don't.

I'm trying to cut down on the use of shrieks (exclamation marks), I'm told they are !good for you.

There is no place like G28 X0 Y0 Z0
 
And Griff,

here's an adaption of the query, if you only want to read errors that were logged since November 2022, it's not standalone code, it's what you change in Mike Gagnons code:

Code:
...Mike Gagnons code..
colLoggedEvents = loWMIService.ExecQuery ;
    ("Select * from Win32_NTLogEvent Where Logfile = 'Application' and TimeWritten>='20221101' and Type='Error'")
...Mike Gagnons code...

Notice the terms for a Win32_NTLogEvent.Type are locale specific, for example 'Erreur' instead of 'Error' in French.


Chriss
 
Griff, if you search for [Windows Event Log] that highlights "Windows" "Event" and "Log" in the results. If you want to search for Windows Event Logs, search for "Windows Event Logs"...

search_lu4ogg.png


Chriss
 
Thanks Chriss... just like the google search B-)

Regards

Griff
Keep [Smile]ing

There are 10 kinds of people in the world, those who understand binary and those who don't.

I'm trying to cut down on the use of shrieks (exclamation marks), I'm told they are !good for you.

There is no place like G28 X0 Y0 Z0
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top