If not, what would be another similar prefab solution that doesn't require
going through the Win32 API?
Thanks,
Ignacio
Here is an excellent document on the subject. Let me know when you figure out how to
read..
Writing Messages
to the Microsoft
Event Log using
Delphi
FMI Solutions
© FMI Solutions 2002 Page 1
An application can write to the event log by using the following API routines. For
details on the parameters of these functions see the API documentation.
. RegisterEventSource - This opens a handle to the event log, on a local or
remote machine.
. ReportEvent - This is used to actually write the event.
To write to the event log in a simple fashion simply involves calling Register event
source with the computer name (UNC) of the event log you wish to use (nil for
local) and the event source name. The event source name is typically the name of
the application, but can be anything descriptive. Once an event source has been
registered, events can be written by calling ReportEvent, using the handle
returned by RegisterEventSource. For example :
VAR EventLog : Thandle;
.
.
EventLog := RegisterEventSource(nil,PChar('MyApplication'));
.
.
VAR MyMsg:Array[0..2] Of PChar;
.
.
MyMsg[0]:= 'A test event message';
ReportEvent(EventLog,EVENTLOG_INFORMATION_TYPE,0,0,nil,1,0,@MyMsg,n
il);
However the event text written to the event log will be prefixed by
"The description for Event ID ( 0 ) in Source (MyApplication ) cannot be
found. The local computer may not have the necessary registry information or
message DLL files to display messages from a remote computer. The following
information is part of the event:"
(Nb : This message is specific to Windows 2000 and will appear slightly differently
on other Windows platforms).
To eliminate this text, it is necessary to enter some registry values as follows and
define string resources (this can be done in any component of your software, not
necessarily the application which is reporting the events. The relevant registry
entries are described below. Code examples assume that the resources for event
messages and categories are located in the same executable that is writing the
events, category keys are optional.
The reason for these registry entries and the string resources is that the event log
uses the string an application writes to it as a format argument, and it needs to
know where the format specifier for the this string is. Also category information
can be specified in a file for the event viewer to user, rather that just display a
number or the typical "None". So, the most simple format specifier is %1, which
will just output the input string. For more information on format specifiers and
arguments see the API documentation for FormatMessage.
© FMI Solutions 2002 Page 2
Registry Entries
Create the following registry key :
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\Applicatio
n\<AppName>
The application name used in the above registry key must match the source
name used when calling RegisterEventSource, because the event viewer will use
this source name to find the registry entries below.
Create the following values:
Value Name Type Description
CategoryCount
(Optional) :Integer
The number of event categories
you have (this is a max index not
really a count and does not seem to
cause a problem if it does not
match the number of categories
actually defined)
CategoryMessageFile
(Optional)
String
The file which contains the category
text string resources
EventMessageFile String
The file which contains the event
text string resources
TypesSupported Integer Types of events allowed
Example code to create registry entries:
Top of Form
VAR Reg : TRegistry;
RegKey : String;
AppPath : String;
AppName : String;
NumCategories : Integer;
begin
Reg := TRegistry.Create;
try
AppPath := Application.ExeName;
AppName := 'pEventLogging';
Delete(AppName,Length(AppName)-4,4); //Remove Extension
NumCategories := 2;
RegKey :=
Format('SYSTEM\CurrentControlSet\Services\Eventlog\Application\%s',[AppName
]);
Reg.RootKey := HKEY_LOCAL_MACHINE;
Reg.OpenKey(RegKey,True);
Reg.WriteString('CategoryMessageFile',AppPath); //Own filename
Reg.WriteString('EventMessageFile',AppPath); //Own filename
Reg.WriteInteger('CategoryCount',NumCategories); //Max category index
Reg.WriteInteger('TypesSupported',EVENTLOG_SUCCESS or
EVENTLOG_ERROR_TYPE or
EVENTLOG_WARNING_TYPE or
EVENTLOG_INFORMATION_TYPE); //Allow all types
Reg.CloseKey;
EventLog := RegisterEventSource(nil,PChar(AppName));
finally
Reg.Free;
end; //try..finallyVAR Reg : TRegistry;
© FMI Solutions 2002 Page 3
RegKey : String;
AppPath : String;
AppName : String;
NumCategories : Integer;
begin
Reg := TRegistry.Create;
try
AppPath := Application.ExeName;
AppName := 'MyApplication';
NumCategories := 2;
RegKey :=
Format('SYSTEM\CurrentControlSet\Services\Eventlog\Application\%s',[AppName
]);
Reg.RootKey := HKEY_LOCAL_MACHINE;
Reg.OpenKey(RegKey,True);
Reg.WriteString('CategoryMessageFile',AppPath); //Own filename
Reg.WriteString('EventMessageFile',AppPath); //Own filename
Reg.WriteInteger('CategoryCount',NumCategories); //Max category index
Reg.WriteInteger('TypesSupported',EVENTLOG_SUCCESS or
EVENTLOG_ERROR_TYPE or
EVENTLOG_WARNING_TYPE or
EVENTLOG_INFORMATION_TYPE);
Reg.CloseKey;
EventLog := RegisterEventSource(nil,PChar(AppName));
finally
Reg.Free;
end; //try..finally
Message and category resources
The above information entered in the registry informs the event log of where to
look for message strings and category strings based on the source name that an
application uses to write to the event log. So since we have told the event log to
look at our application we need to include these resources in our executable. This
involves creating a message table containing the strings. This process involves
the following steps:
. Writing a message table source file (a .mc file).
. Compiling the .mc file with Microsoft's message compiler.
. Link the resulting information into your Delphi application.
There are examples and information on writing .mc files in the Windows platform
SDK documentation and on various websites, including MSDN, however the
documentation is not very clear so below is an example of the minimum
information required in a message table source file :
;//Example Message source file exmess.mc
MessageId=0
Language=English
%1
.
MessageId=1
Language=English
Category1
.
MessageId=2
Language=English
Category2
© FMI Solutions 2002 Page 4
.
Lines starting with ;// are comments and are not compiled. This example file
contains three string resources, one event message format specifier and two
category resources, although it could have contained just the first resource. Each
resource is separated by a single full stop on it's own line, and the file is
terminated by a full stop on it's own line - if there is not a line break after the
last full stop the file will not compile.
The fist line of a resource specifies it's MessageID (index), which an application
will use to refer to the string later. The next line specifies the language of the
resource, in this case "English" specifies international English, the default
language of Windows platforms. For information on multilingual resources see the
message compiler help. The last line specifies the actual string resource text.
In the case of resource 0 the text is "%1", which means that the output will be
the same as the input. The message could be prefixed with the text "An Event
Message" by making the string resource text "An Event Message %1". More
information on format strings can be found in the API documentation for
FormatMessage and in the Message Compiler help.
Resources detailing categories do not take format arguments. As can be seen in
the example .mc file we have defined two categories "Category1" and
"Category2".
The next stage is to compile the .mc file using the Microsoft Message Compiler
(MC.exe), which can be obtained from Microsoft, and is part of the Windows
Platform SDK. The example .mc file named "exmess.mc" can be compiled from
the command line as follows :
mc exmess.mc
This will produce three files exmess.rc, bin00001.msg and exmess.h. Exmess.h
can be used as a header file referencing the message resources by their symbolic
name if specified (not included in the example). The .bin file is a compiled binary
message table resource, and the .rc is a windows resource file. This can be
compiled into a .res Delphi resource file using the Delphi resource compiler
(brcc32.exe) or more simply can be directly added to the Delphi project by just
adding the file in the project manager, where Delphi will automatically compile it
during the build process.
Writing events with categories
Once an application has the resources linked in, and the relevant registry entries,
or code to create them, the application can write events to the event log as
previously described, but without the error prefix and can also include a category
index:
VAR EventLog : Thandle;
.
.
EventLog := RegisterEventSource(nil,PChar('MyApplication'));
.
.
VAR MyMsg:Array[0..2] Of PChar;
.
© FMI Solutions 2002 Page 5
.
MyMsg[0]:= 'A test event message';
ReportEvent(EventLog,EVENTLOG_INFORMATION_TYPE,1,0,nil,1,0,@MyMsg,n
il);
The above code will now write an event to the event log, with the text "A test
event message" and because of the 1 following the EventLogType parameter, it
will be a "Category1" event.
This is achieved because by specifying 0 as the event id, which is mapped to the
format specifier in resource 0 ("%1"), passing "A test event message" as a
parameter which will output "A test event message". Similarly the category is
specified as 1 which will map to the text in resource 1 ("Category1").
The Event Log maintains a "live link" to the message and category resource files
specified in the registry, which means that when a user wishes to view an event
log the Event Viewer will access the resource files to display the event details.
This means that if you create a number of events using a specific resource file
and then change the values in the resource file and refresh the Event Viewer, the
event text and categories will also change depending on the resource changes.
Similarly if the resource file is deleted or the registry entries are removed or
corrupted then the event log will not be able to access the resources and will
display the error message as detailed at the beginning of this document as a
prefix to the event text and the category index will be displayed as the category.
oops sorry, did not notice you said "Delphi's" event log..
OutputDebugString
> If not, what would be another similar prefab solution that doesn't require
> going through the Win32 API?
Oh, sorry. ODS is an API function, but its entire purpose is to send
status strings to the debugger. If there's a Delphi library function
that does the same thing, then I'm sure it was introduced for Kylix
compatibility and that the Windows implementation of it calls ODS.
You can also use an advanced breakpoint. You can set a message to send
to the log, and you can also indicate an expression to evaluate and have
the result sent to the log.
--
Rob
See, I'm not nuts. Or rather, not THAT nuts ;)
http://sourceforge.net/projects/jcl/
Cheers,
Ignacio
Well Rob, you were right. It is in fact a wrapper for ODS. And Delphi's
Event Log traps ODS messages, so it's all good anyways.
Cheers,
Ignacio