|
Write an Entry Successfully
You must know the category numbers, message ID, and error severity of the message entries to program the app to write an event entry successfully (see the MsgData.txt and CatData.txt files in the code download). Currently, this information is hard-coded within the test application. A real program probably knows exactly what log items it needs to write, so I'm not sure it's worthwhile to make this function data-driven.
The TestWrite program must do a fair amount of extra work to function as a demo program. For example, you would normally write messages only to one event log (almost always the Application log). However, for demo purposes, I allow you to write to any log (except the Security log). When you write to an event log, NT searches only for the program's name, not the event log name and program name. The result: To register the same DLL for multiple logs, you must name each of them differently. I use the application's name plus an underscore and the log name's first 10 characters (with spaces removed). Keep in mind that a nondemo program requires little effort from you in deciding which log to write to, whether it's a local or remote log, what error level and message you would write, and so on.
The code in cmdWriteEventLog spends most of its time gathering the options the user selected within the UI, something you typically wouldn't do for logging an event in a real program (see Listing 1). The Category ID and binary data (EventDataBuffer) are optional parameters. If you aren't using a Category resource file, pass a zero for the ID; otherwise, any number you pass is enclosed within parentheses in the event log. Notice that you can't write to the Security event log unless you impersonate the LocalSystem account, but that shouldn't be a big issue because you don't normally need to write to the security log. If necessary, you could create a service (probably not in VB) that runs as LocalSystem and write to the security log from that service. The actual call into the WriteEvent.dll comprises six parameters: the server name (blank for the local server), application name, log type (enumerated value), EventID, the log's name (Application, and so on), and the actual replaceable parameters for the log text as a text array.
The code in WriteEventLog needs to accomplish several tasks before writing to the log (see Listing 2). If you passed a nonblank server name, you must ensure that the string starts with double backslashes. The event log API can't use the text array you passed as is, so you must first translate it into a specialized type variable with 99 items using the TranslateArray() function (you can use between 0 and 99 replaceable parameters in an event log message). The array you pass through the universal defined type (UDT) is an array of string pointers, and you only pass those that are filled innot all 99 items. This function's code is included in this article's code download. The function also returns the number of replaceable parameters included in the passed text array.
Next, you need to register the source for the event log messages (the *.MC file you compiled earlier). Finally, you need to get the current user's SID (see Listing 3). This isn't totally necessary, but it adds a nice professional touch to the log messages. If the ReportEvent() API returns zero, an error occurred writing the event and an error is raised back to the calling application. You should deregister the event source with the DeregisterEventSource() API call after writing the event. Deregistering the event source doesn't remove the Registry values; it merely closes the event log for writing. Compared to all the work you did to get to this point, writing the event itself isn't too difficult.
When you pass the SID to the ReportEvent() API, the user's name appears in the event log record. All you need to do is open a process token for the current process, then get the token information for the user running that process. Normally, this is the user who last logged onto the machine. However, you can run processes as, for example, LocalSystem (as in an NT service), or the application's author can impersonate other users so that process could return users besides the currently logged-on user.
In the past, creating and compiling the necessary *.MC file comprised more than 70 percent of the work in creating well-formed event log messages from VB, at least for me. Every time I needed to do this, I had to look up the information on MSDN to refresh my memory of how to do this correctly. Hopefully, the included CreateMC.exe eases this particular pain. I like to learn new things, but I don't want to remember all the details for years and years.
L.J. Johnson consults for various companies in Dallas. He has programmed in VB since its 1.0 release, and he is a Microsoft MVP and the Ask the NT Pro at www.devx.com/gethelp/. Find him at and www.slightlytiltedsoftware.com.
|