Sie sind auf Seite 1von 31

Using the Windows Event Log from Visual FoxPro

Using the Windows Event Log from Visual FoxPro


Craig Berntson Email: craig@craigberntson.com 3M Health Information Systems Cole Gleave cgleave@yahoo.com 3M Health Information Systems

Overview
In this article, you will learn how to use the Windows Event Log from Visual FoxPro. While you can use Windows Scripting Host (WSH) to read and write to the Windows Event Log, WSH could be disabled due to today s security requirements. Therefore, it is necessary to use the Win32 API to access the log. Using several API calls, you will learn how to read, write, and maintain the log.

Logging Overview
Capturing error conditions is an important part of a robust application. Historically, Visual FoxPro applications have used tables (DBFs) or text files to capture error information. However, other types of logging are available. These include XML files or the Windows Event Log. Using the Windows Event Log is desirable because other file types use proprietary formats, different user interfaces for reporting, and they cannot be merged with logs from other applications. The Windows Event Log is a standard format, stored in a centralized location and single user interface can be used to view log information from a variety of programs. Also, third party software can be purchased that will report on entries stored in the Windows Event Log. In addition to error conditions, the Windows Event Log can be used to store information such as a user logging on, opening a database, starting a file transfer, or other information. The Windows Event Log should not be used as a tracing tool or to record transactional information such as database changes. The Windows Event Log actually contains several types of logs. The standard logs and what they are used for are listed in Table 1. Under Windows 2000 and later you may find User Defined types. I will talk more about these later in this document. Your programs will make registry entries under the Application or a User Defined type. The Windows Event Log is actually a group of files. Each file represents a different log type. The location of these files is specified by Registry entries.

Craig Berntson, Cole Gleave, 2004, 2005 Page 1 of 31

Using the Windows Event Log from Visual FoxPro

Table 1. A listing of the standard log types and their locations. Log Type Description Security Security information such as logon, logoff, password changes, etc. This log is reserved by Windows. System Information from Windows. This log is reserved by Windows. Application Information from application software.

File Location \WinNT\System32\Config\SecEvent.Evt

\WinNT\System32\Config\SysEvent.Evt \WinNT\System32\Config\AppEvent.Evt

Each entry in the event log contains the following information: Date Time Username: The user that wrote the entry. Computer: The name of the computer that wrote the error. Generally, you will use the local event log. However, if you have the proper rights, you can log information to a server or other computer. Source: The application, module, or component that wrote the event. Message Type: Can be Success, Error, Warning, Information, Audit Success or Audit Failure. Category: Specified by a message file. Message files are discussed later in this document Event ID: Specified by a message file. Description ID: Specified by a message file. Binary Data The primary way to view information from the event log is the Event Log Viewer (Figure 1). Under Windows 2000 or later, you access the Viewer from the Administrative Tools applet of Windows Control Panel.

Craig Berntson, Cole Gleave, 2004, 2005 Page 2 of 31

Using the Windows Event Log from Visual FoxPro

Figure 1. The Event Viewer is used to view and manage the Windows Event Log.

When you double-click on an entry, the Event Properties dialog (Figure 2) for that event is displayed. You can see from Figure XX that not all the information is required. For example, the Category and User are not specified. The Description is actually loaded from a message file.

Figure 2. You can view properties of a specific event.

Using the Event Log


There are several things you need to do to use the event log, but by creating generic message files and reusing code, it becomes easier. The three steps to use the Windows Event Log are:
1. 2. 3.

Create the Message Files Register the Event Source Call the Event Log API

Step three consists of several Win32 API calls and is discussed in detail later in this document.

Creating Message Files


Message files are resource DLLs that store the Category, Event ID, and Message ID for a particular event. You can use a separate resource DLL for each item or combine all three items into a single file and message files can be shared by multiple event sources.

Craig Berntson, Cole Gleave, 2004, 2005 Page 3 of 31

Using the Windows Event Log from Visual FoxPro

To create a resource DLL you will need the Resource Compiler, Message Compiler, and Linker. These tools ship with Visual Studio, the Microsoft Platform SDK, and other developer tools. I did an Internet search and got hits for free tools that may give you the same functionality. The following steps walk you through creating a message file.
1.

Create a text file for the messages. This example uses FoxMessages.mc.
LanguageNames = ( English = 0x0409:Messages_ENU ) ;//////////////////////////////////////// ;// Eventlog categories ;// ;// These always have to be the first entries in a message file MessageId = 1 SymbolicName = CATEGORY_ONE Severity = Success Language = English First category event . MessageId = 2 SymbolicName = CATEGORY_TWO Severity = Success Language = English Second category event . ;//////////////////////////////////////// ;// Events MessageId = 1000 SymbolicName = HELLO Language = English Hello World! . MessageId = 1001 SymbolicName = GREETING Language = English Hello %1. How are you? . MessageId = 1002 SymbolicName = GENERIC1 Language = English %1 . MessageId = 1003 SymbolicName = GENERIC2 Language = English Message 1: %1 Message 2: %2

Craig Berntson, Cole Gleave, 2004, 2005 Page 4 of 31

Using the Windows Event Log from Visual FoxPro

2.

In the Windows Command Interpreter, compile the message file using the Message Compiler.
mc FoxMessages.mc

The message compiler outputs the following files: FoxMessages.h: A C++ header file containing the event information FoxMessages.rc: A Resource source file. This will be the input for resource compiler. Messages_ENU.bin: A binary file for US English messages.
3.

In the Windows Command Interpreter, compile the resource file using the Resource Compiler.
rc FoxMessages.rc

The Resource Compiler will create the resource file FoxMessages.res.


4.

In the Windows Command Interpreter, link the resource file.


link -dll -noentry -out:FoxMessages.dll FoxMessages.res

The dll parameter tells the linker to create a .dll file (FoxMessages.dll) and noentry says there will not be entry point. This is required because the linker is not creating a Win32 dll, but rather a resource dll. The resource dll is now ready to use. The sample code that accompanies this article includes the batch file Make.bat that does all three steps for you.

Updating the Registry


Before you can use the resource dll, you need to make a registry entry so that Windows can find it when the event is read and written. You can use the Registry Fox Foundation Class to do this programmatically, make the entry when you install the application, or edit the registry manually using RegEdit. However you do it, the entry goes in HKEY_LOCAL_MACHINE\SYSTEM\ ControlSet001\Services\EventLog\Application. If you want your event information to go under the Application log, use the above location. If you want a custom log, say, MyApps , create a new entry called MyApp at the same level as Application. Next, make an entry for your
Craig Berntson, Cole Gleave, 2004, 2005 Page 5 of 31

Using the Windows Event Log from Visual FoxPro

application name. In this example, I m using HKEY_LOCAL_MACHINE\SYSTEM\ ControlSet001\Services\EventLog\Application\FoxMessages. Here are the steps I used:
1.

Run Regedit and drill down until you reach the above key (Figure 3)

Figure 3. The Registry Editor is used to view and manage entries in the Windows registry.
2.

Right-click on Application and select New | Key from the context menu. Enter FoxMessages as the key. Right-click on FoxMessages and select New | String Value. Enter EventMessageFile. Double-click EventMessageFile. The Edit String dialog (Figure 4) is displayed. Enter the path to your resource dll and click OK.

3. 4.

Figure 4. You can add or change an entry in the registry from the Edit String dialog.
Craig Berntson, Cole Gleave, 2004, 2005 Page 6 of 31

Using the Windows Event Log from Visual FoxPro

5. 6.

Right-click on FoxMessages and select New | DWORD Value. Enter TypesSupported. Double-click on TypesSupported. The Edit DWORD Value dialog (Figure 5) is displayed. Enter 7 for Value data so that the log entry can support all types. The types are listed in Table 2. Make sure Hexadecimal is selected, and then click OK.

Figure 5. You can add or edit a DWORD in the registry using the Edit DWORD Value dialog. Table 2. The Bitmask values for the TypesSupported registry entry. Description Hex Value EVENT_LOG_ERROR_TYPE 0x0001 EVENT_LOG_WARNING_TYPE 0x0002 EVENT_LOG_INFORMATION_TYP 0x0004 E
7. 8.

Binary Value 1 2 4

Add an entry for CategoryMessageFile. The Value will be the same path and file name you entered for EventMessageFile. Add another entry for CategoryCount. The Value will be 2, as that is the number of categories in the message file.

The Registry Editor should now look like Figure 6.

Craig Berntson, Cole Gleave, 2004, 2005 Page 7 of 31

Using the Windows Event Log from Visual FoxPro

Figure 6. The Windows registry after making entries for FoxMessages.

Close the registry editor. 10. Reboot your computer.


9.

You have completed the registry entries needed for your application. Next, I ll show you how to write to the Windows Event Log.

Using the Win32 API


Writing to and reading from the event log requires that you declare Win32 API functions. Some of these functions are part of the Event Logging API. Others are supporting functions. It is common when using the Win32 API to make the function call then call another function to check if an error occurred. The function to call is GetLastError( ). Most Win32 API functions set the error condition when they fail. However, some set the error code when they succeed. Because of this, you should always call GetLastError( ) after making a Win32 API call.
DECLARE INTEGER GetLastError IN WIN32API

Before you can write to an event source, you need to register it. The function returns a handle to the event source or NULL if an error occurs. Pass a NULL or empty string as the first parameter to use the Windows Event Log on the local computer.
DECLARE INTEGER RegisterEventSource IN WIN32API ; STRING UNCServerName, ;

Craig Berntson, Cole Gleave, 2004, 2005 Page 8 of 31

Using the Windows Event Log from Visual FoxPro

STRING SourceName

The parameters for RegisterEventSource( ) are listed in Table 3.


Table 3. A listing of the parameters for the RegisterEventSource function. Parameters Description UNCServerName The server name where the event is logged. If logging to the local machine, pass an empty string. SourceName The source name that is saved to the Registry. Earlier, I named the source, FoxMessage .

When you finish with log, call DeregisterEventSource( ), which has a single parameter, hEventLog. This is the handle that was returned by RegisterEventSource( ).
DECLARE INTEGER DeregisterEventSource IN WIN32API ; INTEGER hEventLog

The ReportEvent( ) function actually records the event into the log. If the message is successfully recorded to the log, the return value is non-zero. If the function fails, a 0 is returned.
DECLARE INTEGER ReportEvent IN WIN32API ; INTEGER EventLog, ; INTEGER Type, ; INTEGER Category, ; INTEGER EventID, ; INTEGER UserSid, ; INTEGER NumStrings, ; INTEGER DataSize, ; STRING @Strings, ; INTEGER RawData

The parameters are defined in Table 4.


Table 4. A listing of the parameters for the ReportEvent function. Parameters Description EventLog Handle to the event log. This is the handle returned by RegisterEventSource( ). Type Event type to log. Category Event category. The categories are defined in the message file. EventID The event identifier as defined in the message file. UserSid Points to the user s security identifier to log the username. Can be NULL if it is not required. NumStrings The number of strings in the @Strings parameter. DataSize The number of bytes in the RawData written to the log. If zero, no RawData is present. @Strings As defined in the Win32 API help file, this is an array of NULL terminated strings to be merged into the message. In VFP, a function will be used to create a string that mimics the C++ array.
Craig Berntson, Cole Gleave, 2004, 2005 Page 9 of 31

Using the Windows Event Log from Visual FoxPro

RawData

Binary data to be stored to the log.

The event type (Type parameter) can be one of five values:


#DEFINE #DEFINE #DEFINE #DEFINE #DEFINE #DEFINE EVENTLOG_SUCCESS EVENTLOG_ERROR_TYPE EVENTLOG_WARNING_TYPE EVENTLOG_INFORMATION_TYPE EVENTLOG_AUDIT_SUCCESS EVENTLOG_AUDIT_FAILURE 0 1 2 4 8 10

When reading the event log, the first thing you need to do is call OpenEventLog( ).
DECLARE INTEGER OpenEventLog IN WIN32API ; STRING UNCServerName, ; STRING UNCSourceName

If the function succeeds, it returns a handle to the event log. If it fails, it returns NULL. The parameters are listed in Table 5.
Table 5. A listing of the parameters for the OpenEventLog function. Parameters Description UNCServerName The server name where the event is logged. If logging to the local machine, pass an empty string. SourceName The source name that is saved to the Registry. Earlier, I named the source, FoxMessage .

When you finish using the event log, you should call the CloseEventLog( ) function.
DECLARE INTEGER CloseEventLog IN WIN32API ; INTEGER hEventLog

The single parameter, hEventLog, is the handle returned by OpenEventLog( ). CloseEventLog( ) will return a non-zero value if it succeeds or zero if it fails. Once the event log is open, it can be read. The event log API allows you to read a single entry at a time using the ReadEventLog( ) function.
DECLARE INTEGER ReadEventLog IN WIN32API ; INTEGER hEventLog, ; INTEGER ReadFlags, ; INTEGER RecordOffset, ; STRING @Buffer, ; INTEGER NumberOfBytesToRead, ; STRING @BytesRead, ; STRING @MinNumberOfBytesNeeded

Craig Berntson, Cole Gleave, 2004, 2005 Page 10 of 31

Using the Windows Event Log from Visual FoxPro

If successful, a non-zero value is returned. Zero is returned if the function fails. The parameters are listed in Table 6.
Table 6. A listing parameters for the ReadEventLog function. Parameters Description hEventLog The handle to the event log. ReadFlags Specifies how to read the log. See the #DEFINEs bellow. RecordOffset The log entry number where the read operation should start. @Buffer A buffer for the data read. NumberOfBytesToRead The size, in bytes, of the buffer. @BytesRead The number of bytes actually read. @MinNumberOfBytesNeede The number of bytes required for the next log entry. d

The values for ReadFlags are listed below.


#DEFINE #DEFINE #DEFINE #DEFINE EVENTLOG_SEQUENTIAL_READ EVENTLOG_SEEK_READ EVENTLOG_FORWARDS_READ EVENTLOG_BACKWARDS_READ 1 2 4 8

Now that you ve seen the primary functions in the Event Log API, it s time to see some additional functions you can do in the Windows Event Log. The first tells you how many records are in the event log.
DECLARE INTEGER GetNumberOfEventLogRecords IN WIN32API ; INTEGER hEventLog, ; STRING @NumberOfRecords

The return value will be nonzero if the function succeeds or zero if it fails. Table 7 lists the parameters.
Table 7. A listing of parameters for the GetNumberOfEventLogRecords function. Parameters Description hEventLog The event log handle. @NumberOfRecords The number of records in the event log.

GetOldestEventLogRecord( ) gets the record number for the oldest event in the log. It returns a nonzero value if successful or zero if it fails. Table 8 lists the parameters.
DECLARE INTEGER GetOldestEventLogRecord IN WIN32API ; INTEGER hEventLog, ; STRING @OldestRecord Table 8. A listing parameters for the GetOldestEventLogRecord function. Parameters Description

Craig Berntson, Cole Gleave, 2004, 2005 Page 11 of 31

Using the Windows Event Log from Visual FoxPro

hEventLog @OldestRecord

The event log handle. The record number of the oldest record in the event log.

That completes the actual event log API calls. There are some additional Win32 API functions that you ll need to use. The first of these is GlobalAlloc( ), which allocates memory on the heap. If the function succeeds, it returns a handle to the allocated memory. If it fails, it returns NULL.
DECLARE INTEGER GlobalAlloc IN WIN32API ; INTEGER Flags, ; INTEGER Bytes

Table 9 defines the parameters.


Table 9. A listing of parameters for the GlobalAlloc function. Parameters Description Flags Indicates how to fill the memory being allocated. For the event log, the memory can be filled with zeros, as defined below. Bytes The number of bytes to allocate.

The following line defines the value to pass for the Flags parameter:
#DEFINE GMEM_ZEROINIT 0x0040

Since memory is allocated, is must also be deallocated. That is the purpose of GlobalFree( ). The single parameter is the handle to the memory, as returned by GlobalAlloc( ). If GlobalFree( ) succeeds in deallocating the memory, it returns NULL. Any other value indicates failure.
DECLARE INTEGER GlobalFree IN WIN32API ; INTEGER Mem

The strings that are used for the @Strings parameter of the ReportEvent function need to be copied from Visual FoxPro memory into the allocated memory. Keep in mind that the Event API function requires a C++ array string. By copying the memory and doing some other hocus-pocus that I will explain later, the VFP string will look like a C++ array string. The CopyMemory( ) function will handle this for us.

DECLARE RtlMoveMemory IN WIN32API AS CopyMemory ; INTEGER Destination, ; STRING Source, ; INTEGER Length

This function has no return value. The parameters are listed in Table 10.

Craig Berntson, Cole Gleave, 2004, 2005 Page 12 of 31

Using the Windows Event Log from Visual FoxPro

Table 10. A listing of parameters for the RtlMoveMemory function. Parameters Description Destination The starting address of the copied block s destination. Source The starting address of the block of memory to copy. Length The size, in bytes, of the block of memory to copy.

One of the optional parameters of the ReportEvent( ) function is UserSid, which is the User s Security ID. The LookupAccountSid( ) function gets that information for you.
DECLARE INTEGER LookupAccountSid IN WIN32API ; STRING SystemName, ; STRING @Sid, ; STRING @Name, ; STRING @cbName, ; STRING @ReferencedDomainName, ; STRING @cbReferencedDomainName, ; STRING @peUse

A nonzero return value indicates success, zero indicates failure. Table 11 lists the parameters.
Table 11. A listing of parameters for the LookupAccountSID function. Parameters Description SystemName The server name to query. Pass an empty string to query the local system. @Sid A SID structure for the account information to look up. @Name The account name corresponding to the @Sid parameter. @cbName The number of bytes contained in the @Name parameter. If the function fails because @Name is too small, this will contain the number of bytes needed. @ReferencedDomainName The domain where the account is found. @cbReferencedDomainNam The number of bytes contained in the @ReferencedDomainName e parameter. If the function fails because @ReferencedDomainName is too small, this will contain the number of bytes needed. @peUse Contains the type of account when the function returns.

The next function, LoadLibraryEx( ) maps an executable module into the address space of the calling process. For the Windows Event Log, it is used to load the message file. Here is the syntax:
DECLARE LONG LoadLibraryEx IN WIN32API ; STRING LibFileName, ; INTEGER RESERVED, ; INTEGER Flags

If the function call succeeds, the return value is a handle to the mapped executable module. If it fails, NULL is returned. Table 12 lists the parameters.
Table 12. A listing of parameters for the LoadLibraryEx function.

Craig Berntson, Cole Gleave, 2004, 2005 Page 13 of 31

Using the Windows Event Log from Visual FoxPro

Parameters LibFileName RESERVED Flags

Description The name of the executable (DLL or EXE) module. Must be NULL The action to take when loading the module. For event log usage, the only value you need to use here is to load the library as a datafile.

#DEFINE LOAD_LIBRARY_AS_DATAFILE

0x00000002

After you finish using the library, you need to release it. The FreeLibrary( ) function will do this. It takes a single parameter, hLibModule, which is the return value of LoadLibraryEx( ). If the function succeeds, it returns a nonzero value. Any other return value indicates failure.
DECLARE INTEGER FreeLibrary IN WIN32API ; LONG hLibModule

You will need to format the message string. The FormatMessageString( ) function does this.
DECLARE INTEGER FormatMessage IN WIN32API ; INTEGER Flags, ; LONG Source, ; INTEGER MessageId, ; INTEGER LanguageId, ; STRING @Buffer, ; INTEGER BufferSize, ; STRING @Arguments

FormatMessage( ) returns the number of bytes in the output buffer. If it fails, it returns a zero. Table 13 lists the parameters.
Table 13. A listing parameters for the FormatMessage function. Parameters Description Flags A series of bit flags that specify aspects of the formatting process and how to interpret the Source parameter. See the FORMAT_MESSAGE constants below. Source Specifies the location of the message definition. This will be the resource file loaded by LoadLibraryEx. MessageId The Message Id from the Message file. LanguageId The language Id. You can use 0 if no language is specified. @Buffer Used to return the formatted message. BufferSize The maximum number of bytes that can be stored in @Buffer. @Arguments Values that are used as insert values in the message.

#DEFINE FORMAT_MESSAGE_ALLOCATE_BUFFER 256 #DEFINE FORMAT_MESSAGE_IGNORE_INSERTS 512

Craig Berntson, Cole Gleave, 2004, 2005 Page 14 of 31

Using the Windows Event Log from Visual FoxPro

#DEFINE FORMAT_MESSAGE_FROM_HMODULE #DEFINE FORMAT_MESSAGE_ARGUMENT_ARRAY

2048 8192

The last API function is ExpandEnvironmentStrings( ). This function expands environment variable strings and replaces them with their user-defined values.
DECLARE INTEGER ExpandEnvironmentStrings IN WIN32API ; STRING @Src, ; STRING @Dst, ; INTEGER nSize

If the function succeeds, the return value is the number of characters stored in the destination buffer. If the number of characters is greater than the size of the destination buffer, the return value is the size of the buffer required to hold the expanded strings. If the function fails, the return value is zero. Table 14 lists the parameters.
Table 14. A list of parameters for the ExpandEnvironmentStrings function. Parameters Description @Src The environment variable string to expand. Must be of the form: %variableName%. @Dst The buffer to receive the value specified by @Src. nSize The maximum number of bytes that can be held by @Dst.

That completes the Win32 API functions. However, there is still just a bit more to cover before looking at the full code. I haven t completely showed you how to do the conversion from a VFP string to the C++ array string. The following VFP functions will do this:
* Adapted from KB Article ID: Q181289 *-- The following function converts a long integer to an ASCII *-- character representation of the passed value in low-high format. ****************** FUNCTION LongToStr ****************** * Passed : 32-bit non-negative numeric value (lnLongval) * Returns : ascii character representation of passed value in low-high * format (lcRetstr) * Example : * m.long = "999999" * m.longstr = long2str(m.long) LPARAMETERS lnLongval LOCAL lnI, lcRetstr lcRetstr = "" FOR lnI = 24 TO 0 STEP -8 lcRetstr = CHR(INT(lnLongval / (2 ^ lnI))) + lcRetstr lnLongval = MOD(lnLongval, (2 ^ lnI)) NEXT RETURN lcRetstr
Craig Berntson, Cole Gleave, 2004, 2005 Page 15 of 31

Using the Windows Event Log from Visual FoxPro

*-- The following function converts a string in low-high format to a *-- long integer. ****************** FUNCTION StrToLong ****************** * Passed: 4-byte character string (lcLongstr) in low-high ASCII format * Returns: long integer value * Example: * m.longstr = "1111" * m.longval = str2long(m.longstr) LPARAMETERS lcLongstr LOCAL lnI, lnRetval lnRetval = 0 FOR lnI = 0 TO 24 STEP 8 lnRetval = lnRetval + (ASC(lcLongstr) * (2 ^ lnI)) lcLongstr = RIGHT(lcLongstr, LEN(lcLongstr) - 1) NEXT RETURN lnRetval

Writing to the Event Log


Now that you ve seen the functions involved in using the Windows Event Log, it s time to learn how write to the event log. Here is the code.
#define #define #define #define #define #define EVENTLOG_SUCCESS EVENTLOG_ERROR_TYPE EVENTLOG_WARNING_TYPE EVENTLOG_INFORMATION_TYPE EVENTLOG_AUDIT_SUCCESS EVENTLOG_AUDIT_FAILURE 0x0040 0 1 2 4 8 10

#define GMEM_ZEROINIT

DECLARE INTEGER GetLastError IN WIN32API DECLARE INTEGER RegisterEventSource IN WIN32API ; STRING UNCServerName, ; STRING SourceName DECLARE INTEGER DeregisterEventSource IN WIN32API ; INTEGER hEventLog DECLARE INTEGER ReportEvent IN WIN32API ; INTEGER EventLog, ; INTEGER Type, ; INTEGER Category, ; INTEGER EventID, ; INTEGER UserSid, ; INTEGER NumStrings, ; INTEGER DataSize, ;
Craig Berntson, Cole Gleave, 2004, 2005 Page 16 of 31

Using the Windows Event Log from Visual FoxPro

STRING @Strings, ; INTEGER RawData DECLARE INTEGER GlobalAlloc IN WIN32API ; INTEGER Flags, ; INTEGER Bytes DECLARE INTEGER GlobalFree IN WIN32API ; INTEGER Mem DECLARE RtlMoveMemory in WIN32API as CopyMemory ; INTEGER Destination, ; STRING Source, ; INTEGER Length LOCAL lnHandle, lcName, pName, lcStrings, lnRetVal, ; lcMessage1, lcMessage2, pMessage1, pMessage2, lcMessages lnHandle = RegisterEventSource("", "FoxMessages") IF lnHandle > 0 * No parameter lnRetVal = ReportEvent(lnHandle, EVENTLOG_SUCCESS, 1, 1000, 0, 0, 0, NULL, 0) IF lnRetVal = 0 MESSAGEBOX("ReportEvent Error: " + ALLTRIM(STR(GetLastError()))) ENDIF * Single parameter lcName = "Fred" pName = GlobalAlloc(GMEM_ZEROINIT, LEN(lcName) + 1) CopyMemory(pName, lcName, LEN(lcName)) lcStrings = LongToStr(pName) lnRetVal = ReportEvent(lnHandle, EVENTLOG_SUCCESS, 2, 1001, 0, 1, 0, @lcStrings, 0) IF lnRetVal = 0 MESSAGEBOX("ReportEvent Error: " + ALLTRIM(STR(GetLastError()))) ENDIF IF pName > 0 lnRet = GlobalFree(pName) ENDIF * Multiple parameters lcMessage1 = "This is the first message." lcMessage2 = "This is another message!" pMessage1 = GlobalAlloc(GMEM_ZEROINIT, LEN(lcMessage1) + 1) pMessage2 = GlobalAlloc(GMEM_ZEROINIT, LEN(lcMessage2) + 1) CopyMemory(pMessage1, lcMessage1, LEN(lcMessage1)) CopyMemory(pMessage2, lcMessage2, LEN(lcMessage2)) lcMessages = LongToStr(pMessage1) + LongToStr(pMessage2) lnRetVal = ReportEvent(lnHandle, EVENTLOG_SUCCESS, 1, 1003, 0, 2, 0, @lcMessages, 0) IF lnRetVal = 0 MESSAGEBOX("ReportEvent Error: " + ALLTRIM(STR(GetLastError()))) ENDIF IF pMessage1 > 0

Craig Berntson, Cole Gleave, 2004, 2005 Page 17 of 31

Using the Windows Event Log from Visual FoxPro

lnRet = GlobalFree(pMessage1) ENDIF IF pMessage2 > 0 lnRet = GlobalFree(pMessage2) ENDIF lnRetVal = DeregisterEventSource(lnHandle) ELSE MESSAGEBOX("RegisterEventSource Error: " ; + ALLTRIM(STR(GetLastError()))) ENDIF RETURN ****************** * From KB Article ID: Q181289 ****************** FUNCTION LongToStr * The following function converts a long integer to an ASCII * character representation of the passed value in low-high format. * Passed: 32-bit non-negative numeric value (lnLongval) * Returns: ascii character representation of passed value in low-high * format LPARAMETERS lnLongval LOCAL lnI, lcRetstr lcRetstr = "" FOR lnI = 24 TO 0 STEP -8 lcRetstr = CHR(INT(lnLongval / (2 ^ lnI))) + lcRetstr lnLongval = MOD(lnLongval, (2 ^ lnI)) NEXT RETURN lcRetstr ****************** FUNCTION StrToLong * Convert a string in low-high format to a long integer. * Passed: 4-byte character string (lcLongstr) in low-high ASCII format * Returns: long integer value LPARAMETERS lcLongstr LOCAL lnI, lnRetval lnRetval = 0 FOR lnI = 0 TO 24 STEP 8 lnRetval = lnRetval + (ASC(lcLongstr) * (2 ^ lnI)) lcLongstr = RIGHT(lcLongstr, LEN(lcLongstr) - 1) NEXT RETURN lnRetval

As you can see from this example, the actual FoxPro code is fairly simple. The key to using the event log is defining the API calls and converting FoxPro data types to the C++ data types needed for the calls.

Craig Berntson, Cole Gleave, 2004, 2005 Page 18 of 31

Using the Windows Event Log from Visual FoxPro

Reading from the Event Log


Here s the code to read from the event log. The results are displayed in a Message Box.
#DEFINE #DEFINE #DEFINE #DEFINE EVENTLOG_SEQUENTIAL_READ EVENTLOG_SEEK_READ EVENTLOG_FORWARDS_READ EVENTLOG_BACKWARDS_READ 1 2 4 8 122 38 0 1 2 4 8 10 -2147483646 256 512 2048 8192 0x00000002 && BITSET(0,31)+2

#DEFINE ERROR_INSUFFICIENT_BUFFER #DEFINE ERROR_HANDLE_EOF #DEFINE #DEFINE #DEFINE #DEFINE #DEFINE #DEFINE EVENTLOG_SUCCESS EVENTLOG_ERROR_TYPE EVENTLOG_WARNING_TYPE EVENTLOG_INFORMATION_TYPE EVENTLOG_AUDIT_SUCCESS EVENTLOG_AUDIT_FAILURE

#DEFINE HKEY_LOCAL_MACHINE #DEFINE #DEFINE #DEFINE #DEFINE

FORMAT_MESSAGE_ALLOCATE_BUFFER FORMAT_MESSAGE_IGNORE_INSERTS FORMAT_MESSAGE_FROM_HMODULE FORMAT_MESSAGE_ARGUMENT_ARRAY

#DEFINE LOAD_LIBRARY_AS_DATAFILE #DEFINE GMEM_ZEROINIT 0x0040

DECLARE INTEGER GetLastError IN WIN32API DECLARE INTEGER OpenEventLog IN WIN32API ; STRING UNCServerName, ; STRING UNCSourceName DECLARE INTEGER CloseEventLog IN WIN32API ; INTEGER hEventLog DECLARE INTEGER GetNumberOfEventLogRecords IN WIN32API ; INTEGER hEventLog, ; STRING @NumberOfRecords DECLARE INTEGER GetOldestEventLogRecord IN WIN32API ; INTEGER hEventLog, ; STRING @OldestRecord DECLARE INTEGER ReadEventLog IN WIN32API ; INTEGER hEventLog, ; INTEGER ReadFlags, ; INTEGER RecordOffset, ; STRING @BUFFER, ; INTEGER NumberOfBytesToRead, ; STRING @BytesRead, ; STRING @MinNumberOfBytesNeeded DECLARE INTEGER LookupAccountSid IN WIN32API ;

Craig Berntson, Cole Gleave, 2004, 2005 Page 19 of 31

Using the Windows Event Log from Visual FoxPro

STRING STRING STRING STRING STRING STRING STRING

SystemName, ; @Sid, ; @NAME, ; @cbName, ; @ReferencedDomainName, ; @cbReferencedDomainName, ; @peUse

DECLARE LONG LoadLibraryEx IN WIN32API ; STRING LibFileName, ; INTEGER RESERVED, ; INTEGER FLAGS DECLARE INTEGER FreeLibrary IN WIN32API ; LONG hLibModule DECLARE INTEGER FormatMessage IN WIN32API ; INTEGER FLAGS, ; LONG SOURCE, ; INTEGER MessageId, ; INTEGER LanguageId, ; STRING@ BUFFER, ; INTEGER BufferSize, ; STRING@ Arguments DECLARE INTEGER ExpandEnvironmentStrings IN WIN32API ; STRING@ Src, ; STRING@ Dst, ; INTEGER nSize DECLARE INTEGER GlobalAlloc IN WIN32API ; INTEGER FLAGS, ; INTEGER Bytes DECLARE INTEGER GlobalFree IN WIN32API ; INTEGER Mem DECLARE RtlMoveMemory IN WIN32API AS CopyMemory ; INTEGER Destination, ; STRING SOURCE, ; INTEGER LENGTH ************************************** LOCAL lcUNCSourceName, loReg, hEventLog, lcNumberOfRecords, lnRetVal, ; lcOldestRecord, lcBuffer, lcBytesRead, lcMinNumberOfBytesNeeded lcUNCSourceName = "C:\" SET PROCEDURE TO (ADDBS(HOME()) + "samples\classes\registry.prg") ADDITIVE loReg = CREATEOBJECT("FileReg") * lpUNCSourceName = "3MHIS" hEventLog = OpenEventLog(NULL, lcUNCSourceName) IF hEventLog = 0 MESSAGEBOX("OpenEventLog Error: " + ALLTRIM(STR(GetLastError()))) RETURN

Craig Berntson, Cole Gleave, 2004, 2005 Page 20 of 31

Using the Windows Event Log from Visual FoxPro

ENDIF * Get the number of records in the event log lcNumberOfRecords = SPACE(4) lnRetVal = GetNumberOfEventLogRecords(hEventLog, @lcNumberOfRecords) IF lnRetVal = 0 MESSAGEBOX("GetNumberOfEventLogRecords Error: " + ALLTRIM(STR(GetLastError()))) ELSE MESSAGEBOX("Number of Event Log Records: " + ALLTRIM(STR(StrToLong(lcNumberOfRecords)))) ENDIF * Get the oldest record lcOldestRecord = SPACE(4) lnRetVal = GetOldestEventLogRecord(hEventLog, @lcOldestRecord) IF lnRetVal = 0 MESSAGEBOX("GetOldestEventLogRecord Error: " + ALLTRIM(STR(GetLastError()))) ELSE MESSAGEBOX("Oldest Event Log Record: " + ALLTRIM(STR(StrToLong(lcOldestRecord)))) ENDIF * Loop through messages DO WHILE .T. lcBuffer = SPACE(1) lcBytesRead = SPACE(4) lcMinNumberOfBytesNeeded = SPACE(4) lnRetVal = ReadEventLog(hEventLog, ; BITOR(EVENTLOG_SEQUENTIAL_READ, ; EVENTLOG_FORWARDS_READ), ; 0, ; @lcBuffer, ; LEN(lcBuffer), ; @lcBytesRead, ; @lcMinNumberOfBytesNeeded) IF GetLastError() = ERROR_INSUFFICIENT_BUFFER lcBuffer = SPACE(StrToLong(lcMinNumberOfBytesNeeded)) lcBytesRead = SPACE(4) lcMinNumberOfBytesNeeded = SPACE(4) lnRet = ReadEventLog(hEventLog, ; BITOR(EVENTLOG_SEQUENTIAL_READ, ; EVENTLOG_FORWARDS_READ), ; 0, ; @lcBuffer, ; LEN(lcBuffer), ; @lcBytesRead, ; @lcMinNumberOfBytesNeeded) IF lnRet = 0 MESSAGEBOX("ReadEventLog Error: " + ALLTRIM(STR(GetLastError()))) EXIT ELSE

Craig Berntson, Cole Gleave, 2004, 2005 Page 21 of 31

Using the Windows Event Log from Visual FoxPro

lcMessage = ParseLogRecord(lcBuffer, lcUNCSourceName, loReg) IF MESSAGEBOX(lcMessage, 1) = 2 EXIT ENDIF ENDIF ELSE IF GetLastError() = ERROR_HANDLE_EOF MESSAGEBOX("End of File") EXIT ELSE MESSAGEBOX("ReadEventLog Error: " + ALLTRIM(STR(GetLastError()))) EXIT ENDIF ENDIF ENDDO lnRetVal = CloseEventLog(hEventLog) IF lnRetVal = 0 MESSAGEBOX("CloseEventLog Error: " + ALLTRIM(STR(GetLastError()))) ENDIF RETURN **************************************************** FUNCTION ParseLogRecord LPARAMETERS tcBuffer, tcUNCSourceName, toReg LOCAL lcMessage, lnCounter, lnDataLen, pBuffer lcMessage = "Length: " + ALLTRIM(STR(StrToLong(SUBSTR(tcBuffer, 1, 4)))) + CHR(13) + CHR(10) lcMessage = lcMessage + "Record Number: " + ALLTRIM(STR(StrToLong(SUBSTR(tcBuffer, 9, 4)))) + CHR(13) + CHR(10) lcMessage = lcMessage + "Time Generated: " + TTOC({^1969/12/31 18:00:00} + StrToLong(SUBSTR(tcBuffer, 13, 4))) + CHR(13) + CHR(10) lcMessage = lcMessage + "Time Written: " + TTOC({^1969/12/31 18:00:00} + StrToLong(SUBSTR(tcBuffer, 17, 4))) + CHR(13) + CHR(10) lcMessage = lcMessage + "Event ID: " + ALLTRIM(STR(StrToLong(SUBSTR(tcBuffer, 21, 2)))) + CHR(13) + CHR(10) lcMessage = lcMessage + GetEventType(tcBuffer) lcMessage = lcMessage + "Number of Strings: " + ALLTRIM(STR(StrToLong(SUBSTR(tcBuffer, 27, 2)))) + CHR(13) + CHR(10) lcMessage = lcMessage + "Source Name: " lcSource = "" lnCounter = 57 DO WHILE SUBSTR(tcBuffer, lnCounter, 1) <> CHR(0) lcSource = lcSource + SUBSTR(tcBuffer, lnCounter, 1) lnCounter = lnCounter + 1 ENDDO lcMessage = lcMessage + lcSource lcMessage = lcMessage + CHR(13) + CHR(10) lcMessage = lcMessage + GetEventCategory(tcBuffer, tcUNCSourceName, lcSource, toReg) lnCounter = lnCounter + 1

Craig Berntson, Cole Gleave, 2004, 2005 Page 22 of 31

Using the Windows Event Log from Visual FoxPro

lcMessage = lcMessage + "Computer Name: " DO WHILE SUBSTR(tcBuffer, lnCounter, 1) <> CHR(0) lcMessage = lcMessage + SUBSTR(tcBuffer, lnCounter, 1) lnCounter = lnCounter + 1 ENDDO lcMessage = lcMessage + CHR(13) + CHR(10) lcMessage = lcMessage + GetUserInfo(tcBuffer) lcMessage = lcMessage + GetDescription(tcBuffer, tcUNCSourceName, lcSource, toReg) lnDataLen = StrToLong(SUBSTR(tcBuffer, 49, 4)) IF lnDataLen > 0 lcMessage = lcMessage + "Data: " pBuffer = StrToLong(SUBSTR(tcBuffer, 53, 4)) FOR lnCounter = 1 TO lnDataLen lcMessage = lcMessage + STR(ASC(SUBSTR(tcBuffer, pBuffer + lnCounter, lnDataLen))) NEXT lcMessage = lcMessage + CHR(13) + CHR(10) ENDIF RETURN lcMessage ************************************************* FUNCTION GetEventType LPARAMETERS tcBuffer LOCAL lcRetVal, lnEventType lnEventType = StrToLong(SUBSTR(tcBuffer, 25, 2)) DO CASE CASE lnEventType = EVENTLOG_SUCCESS lcRetVal = "Success" CASE lnEventType = EVENTLOG_ERROR_TYPE lcRetVal = "Error" CASE lnEventType = EVENTLOG_WARNING_TYPE lcRetVal = "Warning" CASE lnEventType = EVENTLOG_INFORMATION_TYPE lcRetVal = "Information" CASE lnEventType = EVENTLOG_AUDIT_SUCCESS lcRetVal = "Audit Success" CASE lnEventType = EVENTLOG_AUDIT_FAILURE lcRetVal = "Audit Failure" ENDCASE lcRetVal = "Event Type: " + lcRetVal + CHR(13) + CHR(10) RETURN lcRetVal ************************************ FUNCTION GetEventCategory LPARAMETERS tcBuffer, tcUNCSourceName, tcSource, toReg LOCAL lcRetVal, lnCategory, lcLookup, lnKey, lcCMF, lnKeyValue, lcCMFx, lnRet, ; hResource, lcBuffer, lnI lcRetVal = "" lnCategory = StrToLong(SUBSTR(tcBuffer, 29, 2))

Craig Berntson, Cole Gleave, 2004, 2005 Page 23 of 31

Using the Windows Event Log from Visual FoxPro

IF lnCategory = 0 lcRetVal = "None" ELSE lcLookUp = "SYSTEM\CurrentControlSet\Services\EventLog\" + ALLTRIM(tcUNCSourceName) + "\" + tcSource lnKey = toReg.OpenKey(lcLookUp, HKEY_LOCAL_MACHINE, .F.) IF lnKey = 0 lcCMF = "" lnKeyValue = toReg.GetKeyValue("CategoryMessageFile", @lcCMF) IF lnKeyValue = 0 lcCMFx = SPACE(255) lnRet = ExpandEnvironmentStrings(@lcCMF, @lcCMFx, 255) hResource = LoadLibraryEx(ALLTRIM(lcCMFx), 0, LOAD_LIBRARY_AS_DATAFILE) IF hResource > 0 lcBuff = SPACE(100) lnRet = FormatMessage(BITOR(FORMAT_MESSAGE_FROM_HMODULE, FORMAT_MESSAGE_IGNORE_INSERTS), ; hResource, ; lnCategory, ; 0, ; @lcBuff, ; 100, ; NULL) IF lnRet > 0 FOR lnI = 1 TO lRet IF SUBSTR(tcBuffer, lnI, 1) = CHR(13) EXIT ELSE lcRetVal = lcRetVal + SUBSTR(tcBuffer, lnI, 1) ENDIF NEXT ELSE lcRetVal = lcRetVal + "(" + ALLTRIM(STR(StrToLong(SUBSTR(tcBuffer, 29, 2)))) + ")" ENDIF FreeLibrary(hResource) ELSE lcRetVal = lcRetVal + "(" + ALLTRIM(STR(StrToLong(SUBSTR(tcBuffer, 29, 2)))) + ")" ENDIF ELSE lcRetVal = lcRetVal + "(" + ALLTRIM(STR(StrToLong(SUBSTR(tcBuffer, 29, 2)))) + ")" ENDIF oReg.CloseKey() ELSE lcRetVal = lcRetVal + "(" + ALLTRIM(STR(StrToLong(SUBSTR(tcBuffer, 29, 2)))) + ")" ENDIF ENDIF lcRetVal = "Event Category: " + lcRetVal + CHR(13) + CHR(10) RETURN lcRetVal

Craig Berntson, Cole Gleave, 2004, 2005 Page 24 of 31

Using the Windows Event Log from Visual FoxPro

************************************************** FUNCTION GetUserInfo LPARAMETERS tcBuffer LOCAL lnUserSIDLength, lnUserSIDOffset, lcRetVal, lcSID, lnI, lcName, cbName, ; lcReferencedDomainName, cbReferencedDomainName, peUse, lnRet lnUserSIDLength = StrToLong(SUBSTR(tcBuffer, 41, 4)) lnUserSIDOffset = StrToLong(SUBSTR(tcBuffer, 45, 4)) lcRetVal = "" IF lnUserSIDLength > 0 lcSid = "" FOR lnI = 1 TO lnUserSIDLength lcSid = lcSid + SUBSTR(tcBuffer, lnUserSIDOffset + lnI, 1) NEXT lcName = SPACE(1) cbName = LongToStr(1) lcReferencedDomainName = SPACE(1) cbReferencedDomainName = LongToStr(1) peUse = SPACE(1) lnRet = LookupAccountSid("", ; @lcSid, ; @lcName, ; @cbName, ; @lcReferencedDomainName, ; @cbReferencedDomainName, ; @peUse) lnRet = GetLastError() IF lnRet = ERROR_INSUFFICIENT_BUFFER lcName = SPACE(StrToLong(cbName)) lcReferencedDomainName = SPACE(StrToLong(cbReferencedDomainName)) lnRet = LookupAccountSid("", ; @lcSid, ; @lcName, ; @cbName, ; @lcReferencedDomainName, ; @cbReferencedDomainName, ; @peUse) IF lnRet = 1 lcRetVal = lcRetVal + lcName ELSE lcRetVal = lcRetVal + "N/A" ENDIF ELSE lcRetVal = lcRetVal + "N/A" ENDIF ELSE lcRetVal = lcRetVal + "N/A" ENDIF lcRetVal = "User: " + lcRetVal + CHR(13) + CHR(10) RETURN lcRetVal

Craig Berntson, Cole Gleave, 2004, 2005 Page 25 of 31

Using the Windows Event Log from Visual FoxPro

*********************************************** PROCEDURE GetDescription LPARAMETERS tcBuffer, tcUNCSourceName, tcSource, toReg LOCAL lcLookup, lnRet, lcEMF, lcEMFx, lnNumStrings, pStr, lcPtrs, lnI, ; hResource, lcBuffer, lnEventNo, lcMessage, lcRetVal lcLookUp = "SYSTEM\CurrentControlSet\Services\EventLog\" + ALLTRIM(tcUNCSourceName) + "\" + tcSource lnRet = toReg.OpenKey(lcLookUp, HKEY_LOCAL_MACHINE, .F.) IF lnRet = 0 lcEMF = "" lnRet = oReg.GetKeyValue("EventMessageFile", @lcEMF) IF lnRet = 0 lnNumStrings = StrToLong(SUBSTR(tcBuffer, 27, 2)) pStr = StrToLong(SUBSTR(cBuffer, 37, 4)) + 1 lcMessage = "" lcPtrs = "" FOR lnI = 1 TO lnNumStrings DO WHILE .T. IF SUBSTR(tcBuffer, pStr, 1) <> CHR(0) lcMessage = lcMessage + SUBSTR(tcBuffer, pStr, 1) pStr = pStr + 1 ELSE lcPtrs = lcPtrs + LongToStr(GlobalAlloc(GMEM_ZEROINIT, LEN(lcMessage) + 1)) CopyMemory(StrToLong(RIGHT(lcPtrs, 4)), lcMessage, LEN(lcMessage)) pStr = pStr + 1 lcMessage = "" EXIT ENDIF ENDDO NEXT lcEMFx = SPACE(255) lnRet = ExpandEnvironmentStrings(@lcEMF, @lcEMFx, 255) hResource = LoadLibraryEx(ALLTRIM(lcEMFx), 0, LOAD_LIBRARY_AS_DATAFILE) IF hResource > 0 lcBuffer = SPACE(1000) lnRet = FormatMessage(BITOR(FORMAT_MESSAGE_FROM_HMODULE, FORMAT_MESSAGE_ARGUMENT_ARRAY, 60), ; hResource, ; StrToLong(SUBSTR(tcBuffer, 21, 2)), ; 0, ; @lcBuffer, ; 10000, ; @lcPtrs) IF lnRet = 0 lnEventNo = StrToLong(SUBSTR(tcBuffer, 21, 2)) lnRet = GetLastError()

Craig Berntson, Cole Gleave, 2004, 2005 Page 26 of 31

Using the Windows Event Log from Visual FoxPro

ELSE lcRetVal = ALLTRIM(lcBuffer) ENDIF FreeLibrary(hResource) ENDIF FOR lnI = 1 TO lnNumStrings lnRet = GlobalFree(StrToLong(SUBSTR(lcPtrs, lnI * 4 - 3, 4))) NEXT ENDIF toReg.CloseKey() ENDIF lcRetVal = IIF(EMPTY(lcRetVal), "", "Description: " + lcRetVal) RETURN lcRetVal ****************** * From KB Article ID: Q181289 ****************** FUNCTION LongToStr * The following function converts a long integer to an ASCII * character representation of the passed value in low-high format. * Passed: 32-bit non-negative numeric value (lnLongval) * Returns: ascii character representation of passed value in low-high * format LPARAMETERS tnLongval LOCAL lnI, lcRetVal lcRetVal = "" FOR lnI = 24 TO 0 STEP -8 lcRetVal = CHR(INT(tnLongVal / (2 ^ lnI))) + lcRetVal lnLongval = MOD(tnLongVal, (2 ^ lnI)) NEXT RETURN lcRetVal ****************** FUNCTION StrToLong * Convert a string in low-high format to a long integer. * Passed: 4-byte character string (lcLongstr) in low-high ASCII format * Returns: long integer value LPARAMETERS tcLongStr LOCAL lcLongStr, lnI, lnRetval lcLongStr = tcLongStr lnRetVal = 0 FOR lnI = 0 TO 24 STEP 8 lnRetVal = lnRetVal + (ASC(lcLongStr) * (2 ^ lnI)) lcLongStr = RIGHT(lcLongStr, LEN(lcLongStr) - 1) NEXT RETURN lnRetVal

You can see that the code that actually reads the log is quite small. Most of the code is for formatting the output.

Craig Berntson, Cole Gleave, 2004, 2005 Page 27 of 31

Using the Windows Event Log from Visual FoxPro

Managing the Event Log


The Windows Event Log requires periodic maintenance. You can schedule this maintenance to occur as needed or manage it yourself. This maintenance is required due to limitations with the Event Log. The Windows Event Log documentation says that the log size for all logs is limited to 4 Gig, but testing at 3M HIS showed that the practical limit is 300 MB. The log size is also limited by memory and other services and applications using the log. The following steps show you how to setup automatic maintenance of the log:
1. 2.

Open the Event Viewer (Figure 1). Right-click on Application and select Properties. The Application Properties dialog (Figure 7) is displayed.

Figure 7. The Application Properties dialog is used to maintain the Event Log.
3. 4. 5.

Use the Log size settings to maintain the size of the Event Log. Click Clear Log to clear all entries from the log Click OK to close the properties dialog.

You can also maintain the log programmatically. The following code shows how to backup the log:
Craig Berntson, Cole Gleave, 2004, 2005 Page 28 of 31

Using the Windows Event Log from Visual FoxPro

DECLARE INTEGER GetLastError IN WIN32API DECLARE INTEGER OpenEventLog IN WIN32API ; STRING UNCServerName, ; STRING UNCSourceName DECLARE INTEGER BackupEventLog IN WIN32API ; INTEGER hEventLog, ; STRING BackupLog DECLARE INTEGER CloseEventLog IN WIN32API ; INTEGER hEventLog *************************************** LOCAL hEventLog, lcBackupLog, lnRet lcUNCSourceName = "C:\" lcBackupLog = "C:\Temp\BackupLog.Evt" hEventLog = OpenEventLog(NULL, lcUNCSourceName) IF hEventLog = 0 MESSAGEBOX("OpenEventLog Error: " + ALLTRIM(STR(GetLastError()))) RETURN ENDIF ERASE (lcBackupLog) lnRet = BackupEventLog(hEventLog, lcBackupLog) IF lnRet = 0 MESSAGEBOX("BackupEventLog Error: " + ALLTRIM(STR(GetLastError()))) ENDIF lnRet = CloseEventLog(hEventLog) IF lnRet = 0 MESSAGEBOX("CloseEventLog Error: " + ALLTRIM(STR(GetLastError()))) ENDIF

The following code shows how to clear the Windows Event Log:
DECLARE INTEGER GetLastError IN WIN32API DECLARE INTEGER OpenEventLog IN WIN32API ; STRING UNCServerName, ; STRING UNCSourceName DECLARE INTEGER ClearEventLog IN WIN32API ; INTEGER hEventLog, ; STRING BackupLog DECLARE INTEGER CloseEventLog IN WIN32API ; INTEGER hEventLog ***************************************

Craig Berntson, Cole Gleave, 2004, 2005 Page 29 of 31

Using the Windows Event Log from Visual FoxPro

LOCAL hEventLog, lcBackupLog, lnRet, lcNewBackup lcUNCSourceName = "C:\" lcBackupLog = "C:\Temp\Backup.Evt" lcNewBackup = NULL hEventLog = OpenEventLog(NULL, lcUNCSourceName) IF hEventLog = 0 MESSAGEBOX("OpenEventLog Error: " + ALLTRIM(STR(GetLastError()))) RETURN ENDIF IF !ISNULL(lcBackupLog) ERASE (lcBackupLog) ENDIF lnRet = ClearEventLog(hEventLog, lcBackupLog) IF lnRet = 0 MESSAGEBOX("ClearEventLog Error: " + ALLTRIM(STR(GetLastError()))) ENDIF lnRet = CloseEventLog(hEventLog) IF lnRet = 0 MESSAGEBOX("CloseEventLog Error: " + ALLTRIM(STR(GetLastError()))) ENDIF

Summary
It may seem that using the Windows Event Log is very complex, but you could create a class that will handle this for you and a generic message file, making the job easier. By using the Windows Event Log, you will centralize event reporting, use a format common to many other applications, and make event reporting easier for the end user.
Craig Berntson is a Microsoft Most Valuable Professional (MVP) for Visual FoxPro, a Microsoft Certified Solution Developer, and President of the Salt Lake City Fox User Group. He wrote the book CrysDev: A Developer s Guide to Integrating Crystal Reports , available from Hentzenwerke Publishing. He has also written for FoxTalk and the Visual FoxPro User Group (VFUG) newsletter. He has spoken at Advisor DevCon, Essential Fox, the Great Lakes Great Database Workshop, Southwest Fox, Microsoft DevDays and user groups around the country. Currently, Craig is a Senior Software Engineer at 3M Health Information Systems in Salt Lake City. You can reach him a craig@craigberntson.com or visit his website, www.craigberntson.com. Cole Gleave is a Senior Software Engineer at 3M Health Information Systems in Salt Lake City. Previously he was Vice-President of Technology at Convenient Automation, specializing in software for the retail industry. He has worked with FoxPro since version 2.6. You can reach him at cgleave@yahoo.com.

Craig Berntson, Cole Gleave, 2004, 2005 Page 30 of 31

This document was created with Win2PDF available at http://www.daneprairie.com. The unregistered version of Win2PDF is for evaluation or non-commercial use only.

Das könnte Ihnen auch gefallen