rrors. Remote procedure calls (RPCs) manage these tools. Thus, networked operators can examine errors occurring on remotely located compute
rs.
Additionally, NT has a set of APIs to read and manage the error logs. These APIs let the NT programmer build sophisticated error-monitoring and analysis tools that are consistent across applications. In this article, we will demonstrate the various APIs that generate error messages and describe how NT monitors events.
Quick Start
It's often worthwhile to get your code up and running as soon as possible. Here is a quick-and-dirty subroutine that will send an error to the NT error event log:
#include <windows.h>
#include <stdio.h>
void SendLogMessage (char *szMsg)
{
HANDLE hSource;
char *szList[1];
szList[0]=szMsg;
hSource = RegisterEventSource(NULL, "MYPROGRAM");
if (hSource != NULL) {
ReportEvent(hSource,EVENTLOG_INFORMATION_TYPE,
0,ERROR_MESSAGE,NULL,1,0,szList,NULL);
DeregisterEventSource(hSource); }
}
void main (argc, argv)
int argc;
char *argv[];
{ SendLogMessage("This is a
generic information message");
}
RegisterEventSource
is similar to an
open()
function; it returns a handle you can use to send messages to the event log. The parameter "
MYPROGRAM
" identifies which subsystem is sending the error. If "
MYPROGRAM
" is unknown to the event logger, the logger will default to reporting the message to the application log.
This program gets your message to the log, but it defeats the nifty features of NT's error logging. Because "
MYPROGRAM
" is an unknown subsystem to the event logger, you can't set up a filter to display only the "
MYPROGRAM
" errors. Also, the event type is always
EVENTLOG_INFORMATION_TYPE
, the category is always zero, and the event ID is zero (the second, third, and fourth arguments).
The first enhancement you should make is to identify your application name to the event logger. You do this by adding entries into the registry in the event log area (see the listing
"How to Add Entries to the Event Log"
). Note: You must be logged on as administrator to execute this program and change the registry.
After executing this code (you will need a
main()
routine to call
InstallSource()
), you can execute the regedt32 program, select the HKEY_LOCAL_MACHINE window, then the SYSTEM group, CurrentControlSet, Services, Eventlog, Application, and finally (whew!) MYPROGRAM to verify the entries did get created and look reasonable. The value for TypesSupported should be 7.
Meaningful Messages
Having defined your application for the event log, you can look in the event viewer and set a filter under the view menu to display only "MYPROGRAM" errors. However, there is more in this
InstallSource()
subroutine than merely declaring your application name.
Why did we add a value under the MYPROGRAM area called "
EventMessageFile
" and give it a value of "
MYDLL.DLL
"? We also added a value called TypesSuppo
rted, containing the flags
EVENTLOG_ERROR_TYPE
,
EVENTLOG_WARNING_TYPE
, and
EVENTLOG_INFORMATION_TYPE
. Obviously, the TypesSupported flags represent the types of errors that can be reported within the MYPROGRAM subsystem, but what is "
MYDLL.DLL
" and what does it represent?
One of the banes of error logging is the inability to change error messages after the programmer has compiled the code and sent it to production to be installed. Imagine an error-reporting system that separates the error messages from the application program, so that the error messages can be changed to accommodate understandable English rather than the jargon that programmers often produce. (We especially like the message we've seen from many different programmers: "Invalid error code.") Or imagine being able to change messages to another language entirely, without changing one line of code.
The NT developers created the concept of a message DLL. Such a DLL contains "almost" no code and is s
imply a shell to hold message strings. The message strings are generated outside the DLL and can be implemented in a manner similar to other resources (e.g., dialog boxes and ICONs). In other words, the resource compiler binds the messages into the DLL. The complete DLL code looks like this:
#include <windows.h>
BOOL WINAPI DllMain(HINSTANCE hDLL,
DWORD dwReason, LPVOID lpReserved)
{ return(TRUE);
}
A message compiler included in Microsoft Visual C++ generates the resource file used to create the noncode contents of the DLL. The input source to the message compiler follows a simple format to represent the error message. It contains the error code's symbolic name, the error code, and the error text. Dynamically generated error messages are also supported.
Here's a simple example of the message file:
MessageId=1
SymbolicName=TEST_ERROR_1
Language=English
This is error event code 1.
.
MessageId=2
SymbolicName=TEST_ERROR_2
Language=English
This is event code 2.
.
MessageId=999
SymbolicName=ERROR_MESSAGE
Language=English
%1
.
The output from the message compiler is an
.h
file that you include in your application program. This
.h
file contains the symbolic names of your error message along with the associated error code. An
.rc
source file and a
.bin
file with the actual messages to be input to the DLL compiler are also generated.
If you save the preceding source in a file called
mydll.mc
, you can create the
.h
,
.rc
, and
.bin
files with the command
mc mydll.mc
.
To generate the
MYDLL.DLL
, simply create a new project in Visual C++ of type DLL, add the message DLL source and the output
.rc
and
.bin
files from the message compiler, and then build the DLL. You should then copy the generated DLL to the system DLL directory, and the
.h
file to your applications directory for inclusion in your a
pplication. (A help file that describes all the features of the message compiler is found in the Visual C++
msdev\bin
directory and is called
mc.hlp
.)
The listing
"Event-Logging Application"
is the complete source to a console application that registers itself to the event logger and then sends messages based on our sample message file.
Last Log
As NT moves into the applications server environment, NT programmers can do it right and generate consistent error reports that easily plug into error management systems. We hope we've shown that the NT event-logging system can be something useful, not something to ignore.
void InstallSource()
{
HKEY hk;
int disposition, allowed;
char szName[256];
strcpy(szName,"SYSTEM\\CurrentControlSet\\
Services\\
Eventlog\\Application\\");
strcat(szName,"MYPROGRAM");
if (RegCreateKeyEx(HKEY_LOCAL_MACHINE,szNam
e,
0,NULL,REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS,
NULL,&hk,&disposition))
{ printf("Unable to create registry key");
return;
}
strcpy (szName,"%SystemRoot%\\System\\MYDLL.DLL");
if
(RegSetValueEx(hk,"EventMessageFile",0,
REG_EXPAND_SZ,
(LPBYTE)szName,strlen(szName)+1))
{ printf("Unable to create/set registry
value (message DLL name)");
return;
}
allowed=EVENTLOG_ERROR_TYPE
EVENTLOG_WARNING_TYPE
EVENTLOG_INFORMATION_TYPE;
if (RegSetValueEx(hk,"TypesSupported",0,REG_DWORD,
(LPBYTE)&allowed,sizeof(DWORD)))
{ printf("Unable to create/set registry value (message types)");
return;
}
RegCloseKey(hk);
}
#include "mydll.h"
#include <windows.h>
#include <stdio.h>
void SendLogMessage (HANDLE hSource,
DWORD errcode, WORD errtype, char *szMsg)
{ char *szList[1];
if (szMsg!=NULL)
{ szList[0]=szMsg;
ReportEvent(hSource,er
rtype,0,errcode,
NULL,1,0,szList,NULL);
}
else
ReportEvent(hSource,errtype,0,errcode,
NULL,0,0,NULL,NULL);
}
void main (argc, argv)
int argc;
char *argv[];
{ HANDLE hSource;
InstallSource();
hSource = RegisterEventSource(NULL,
"MYPROGRAM");
SendLogMessage(hSource,TEST_ERROR_1,
EVENTLOG_WARNING_TYPE,NULL);
SendLogMessage(hSource,TEST_ERROR_2,
EVENTLOG_ERROR_TYPE,NULL);
SendLogMessage(hSource,ERROR_MESSAGE,
EVENTLOG_INFORMATION_TYPE,
"This is a generic information message");
DeregisterEventSource(hSource);
}
Terry Frederick is a consultant working for Sprint. He has 27 years of programming experience. You can reach him at
terryf@sound.net
.