Click here to Skip to main content
15,886,362 members
Articles / Programming Languages / C#
Article

EventLog and resource-only DLL orchestration

Rate me:
Please Sign up or sign in to vote.
4.85/5 (16 votes)
20 Mar 20059 min read 77.8K   1.5K   39   7
Learn how to create and integrate customized resource-only DLLs into your .NET application for Windows event logging.

Image 1

Figure 1 Default event log message displayed

Contents

Introduction

Have you ever seen the message in Figure 1 and wondered about this link displayed below your message text? The link is added automatically every time your .NET application writes a message into the event log. Unfortunately, the MSDN library does not offer direct instructions for .NET developers on how to get rid of that link which diverts curious visitors to Microsoft's Help and Support center. Maybe, it would be a good idea to display here a different link diverting visitors to your own web based support center (see Figure 2) or even discard the link completely. In this article, I will show you an easy approach to how to discard or replace that annoying default link with your own customized message.

Image 2

Figure 2 Event log displaying a customized link

Limitations

The approach I will discuss assumes you are familiar with the System.Diagnostics.EventLog class. As a matter of fact, you should know, that this class offered by the .NET Framework 1.1 is a limited subset over a broader range of possibilities available through the corresponding SDK functions. David Field has already posted an article on the CodeProject, Enhanced EventLog writing for .NET Applications, describing how to gain unlimited access to these core Win32 functions via P/Invokes. Of course, you can use that class, however my approach works also with the standard System.Diagnostics.EventLog class and you are not forced to use enhanced wrapper classes.

In this article, I would rather prefer remaining in the realm of the managed EventLog class than going beyond its boundaries. Although the talk is about C# and .NET, you will be forced to take a short look at a simple C++ project in order to create a customized resource-only dynamic link library. There is no C++ programming skills required to understand and practice the approach I'm going to show you.

The complete solution described

Background & Starting points

The reason the mentioned link to the Microsoft's Help and Support center appears, lies in the fact, that the .NET Framework uses a pre-built resource-only DLL by default, which contains a large but completely empty message text resource table. This resource-only DLL is located at <systemroot>\Microsoft.NET\Framework\v1.1.4322 \EventLogMessages.dll. This absolute path is written into the registry value EventMessageFile every time a new event source is created by the EventLog.CreateEventSource method. The registry key follows the pattern like:

  • HKLM\SYSTEM\CurrentControlSet\Services\Eventlog\<Log>\<Source> EventMessageFile

The default values of <Log> are well known, like Application, Security or System. Customized logs can also be created, e.g., StefanoLog. The <Source> is either set to known values like COM, COM+, ASP.NET 1.1 etc., or to custom values, e.g., StefanoSource (see Figure 3).

Image 3

Figure 3 The Event log registry key

The good thing is, we can safely replace the value of the EventMessageFile with any path pointing to a location of a valid custom resource-only DLL. Thus the task we want to accomplish reduces to the following two major and a few minor steps:

  • Creating a customized resource-only DLL.
    • Create a message text file (.mc)
    • Compile the message text file using the Message Compiler tool to a resource script file (.rc)
    • Compile the resource script file using a Resource Compiler tool to a resource table (.res)
    • Link the resource table file (.res) into a valid resource-only DLL
  • Altering the appropriate EventMessageFile registry value.

Do not be afraid of those minor steps, even if you are not familiar with the terms listed. The first major tasks can be easily accomplished using a simple batch file with just three lines of commands (see Figure 4). The customdefs.mc is a message text file containing the customized Message ID values (see Figure 5). The syntax of that message text file is somewhat strange and old fashioned. Today, architects would rather prefer a kind of XML configuration file instead. You can read more about the exact syntax at MSDN, in the article Message Text Files.

mc customdefs.mc
rc -r -fo customdefs.res customdefs.rc
link -dll -noentry -out:customdefs.mc.dll customdefs.res /MACHINE:X86

Figure 4 The batch file content

MessageId=0x0 Severity=Success Facility=Application
SymbolicName=SUCCESS_GENERAL
Language=English
This is success: %1
.

MessageId=0x1 Severity=Success Facility=Application
SymbolicName=SUCCESS_FIXED_MESSAGE
Language=English
This is a fixed length message. Hallo!!!
.

MessageId=0x2 Severity=Success Facility=Application
SymbolicName=SUCCESS_FIXED_MESSAGEWITHLINK
Language=English
This is a fixed length message displaying a link to the support web site.
%r%r%rClick here to learn more about this error http://www.codeproject.com
.

Figure 5 Message text file

The content of a message text file is composed of building blocks starting with a hex value of a customized Message ID and ending with a dot and an empty new line. Note also the %1 placeholders for the string you will send calling the WriteEntry() method later on.

Be aware, the message text file also supports localization. In this case, make sure the message text file is saved in Unicode encoding format (due to the additional characters which appear in most languages). The message compiler is aware of Unicode content using the "-u" switch: mc -u customdefs.mc. Figure 6 shows the part of a message text file supporting two languages, English and German.

LanguageNames=(English=0x409:MSG00409)
LanguageNames=(German=0x407:MSG00407)


MessageId=0x0 Severity=Success Facility=Application
SymbolicName=SUCCESS_GENERAL
Language=English
This is success: %1
.

Language=German
Das ist eine Erfolgsmeldung: %1
.

MessageId=0x1 Severity=Success Facility=Application
SymbolicName=SUCCESS_FIXED_MESSAGE
Language=English
This is a fixed length message.Hallo!!!
.

Language=German
Das ist eine Meldung von fixer Länge. Hallo!!!
.

Figure 6 Localized message text file

The list of projects

The solution consists of four particular projects, listed below in their exact build order reflecting the dependencies:

  • MakeCustomsource NMake project, which is a C++ batch
  • DefineInserter C# Console Application project
  • EventLogDemo C# Windows Application project
  • Setup and Deployment project which packages the primary output of the Windows Application and the linked resource-only DLL

The MakeCustomsource project

As mentioned above, a single trip into the realm of a C++ project is assumed at this stage. You have to create a Makefile project in order to create a resource-only DLL. Clicking on the right mouse button in the Solution Explorer pane and choosing "Add New Project...", a dialog box appears. Select "Visual C++ Projects / General / Makefile Project" (Figure 7). In the project's "NMake" property, make sure you added a single batch file customsource.bat (content shown in Figure 4) both to the "Build Command Line" and "Rebuild Command Line" values (Figure 8). This project creates the resource-only DLL and an additional C++ header file holding the customized Message ID values. The C++ header is used in the next project (DefineInserter) to parse it and inject automatically all those predefined Message ID values into your C# source as a single enumeration enum messageCodes : int.

Image 4

Figure 7 Creating a Makefile Project

Image 5

Figure 8 NMake properties

The DefineInserter project

This Console Application project is a bridge between the previous MakeCustomsource project and the main Widows Application project. The Console Application simply takes the C++ header (generated at the previous step) and injects all found Message ID values as an enumeration into the C# source code. Therefore, the Console Application assumes two command line arguments, first the name of the C++ header and second the name of the C# source file. The Console Application injects and updates the enumeration between the marked sections in the Form1 class. The marks are configured like C++ style comments as shown in Figure 9.

The DefineInserter project has also a post-build event defined. On successful build, the Console Application is started and refreshes the C# source file with the recently built Message ID values. This kind of an approach makes sure, that every build time, you have the actualized enumeration according to the recent message text file definitions, ready to use in the WriteEntry() method's third (integer type) parameter.

C#
public class Form1 : System.Windows.Forms.Form
{
    // ******* READ THIS INFORMATION CAREFULLY ******************* 
    // Do not remove at any reason the BEGIN & END comments below!
    // ***********************************************************
    /* BEGIN MESSAGE TEXT CONSTANTS */
    enum messageCodes : int
    {
        SUCCESS_GENERAL                  = 0x00000000,
        SUCCESS_FIXED_MESSAGE            = 0x00000001,
        SUCCESS_FIXED_MESSAGEWITHLINK    = 0x00000002,
        SUCCESS_MESSAGE_SHORT            = 0x00000003,
        SUCCESS_MESSAGE_MIDDLE           = 0x00000004,
        SUCCESS_MESSAGE_LONG             = 0x00000005,
        SUCCESS_WARNING_GENERAL          = 0x00000100,
        SUCCESS_ERROR_GENERAL            = 0x00000200,
    }
    /* END MESSAGE TEXT CONSTANTS */
    ...
}

Figure 9 The marked section in the C# source code file

The EventLogDemo project

This Windows Application project is the main demo application. At every startup, it checks and tries to create both a new custom event log and a new custom event source. The .NET Framework behaves in a little strange manner, as creating a new event source not only creates the requested event log (parent) and the event source in it (child), but also creates a new event source within the event log using the very same name as the event log (see Figure 3). Thus, for a single event log (parent), two event sources (children) are created. The Windows Application (Figure 10) also overrides at startup one of those custom event sources' EventMessageFile value, with the absolute path of the custom resource-only DLL customdefs.mc.dll, whereas the other event source remains referencing the .NET default EventLogMessages.dll.

You can switch between these two event sources using the CheckBox control at the top of the application. Use the Write button and note the different formats as they appear in the EventViewer.

Image 6

Figure 10 The tester Windows Application

Creating event logs and event sources is a registry operation which requires write access to the HKLM hive. Therefore, you have to run the application using an appropriate Administrative account, otherwise the operation will fail creating the custom event source. This limitation and a solution is discussed also in the knowledge base article number 842795. You receive the "Requested registry access is not allowed" error message when you try to create a custom event log. If your application has to run in a user context with reduced privileges, I suggest you create your event sources immediately at deployment time.

The single ComboBox shown in Figure 10 contains all the Message ID values edited and created in the message text file earlier. Every time you build the solution, the ComboBox is actualized automatically.

Due to the limitations of the EventLog class, every Message ID can be used in a combination with the three different EventLogEntryType enumerations (Information, Warning, Error). However in the message text file, every Message ID has the predefined Severity set to Success. Bear in mind, that at any full level event log usage scenario (ReportEvent()), for warnings and errors, you have to declare Message ID values set to severities Warning or Error (see Figure 11). These severities would generate unsigned integer enumeration values inappropriate for the WriteEntry() method. For this reason, in the recent solution, you are advised to use severities set to Success for whatever warning or error you want to report.

MessageId=0x200 Severity=Error Facility=Application
SymbolicName=ERROR_GENERAL
Language=English
Error: %1
.

Figure 11 Message text with Severity set to Error

The Setup project

This deployment project just packages the tester Windows Application assembly and the custom resource-only DLL file. This would be also the right place to extend the project and create the custom event source as a deployment custom action, e.g., using the Installer class.

Conclusion

In this article, you learned the basics about how to deal with message text files and particularly how to create a resource-only DLL for .NET Applications based on a message text file. Using customized resource-only DLLs, you can divert visitors to your company's Help and Support web site, catch the sent information and display appropriate resources for the users. Customized resource-only DLLs are a great place for storing fixed messages intended to be written into a Windows Event-Log. Resource-only DLLs can also be easily localized.

Reference articles

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Europe Europe
I started my professional career learning FORTRAN in 1979. Later I graduated Master of Science in power engineering and wrote assembly language programs in the mid 80s in order to control huge and heavy equipments at a hydro power station on the Danube River. Next I graduated PhD in control engineering. At that time I spent long nights to learn and practice both Basic- and C languages on my precious Atari ST. Do you remember the classical book from Ritchie and Kernighan? In the 90s I used Visual C++ working for the Vienna City Government. In the new millennium I joined Microsoft, passed exams for MCAD and now I’m working as consultant serving for software developers in Austria. Please take a note, my articles are not checked by the product teams, therefore they are reflecting just my personal experience.

Comments and Discussions

 
QuestionHow to read event full description from remote machine ? Pin
remotehuang19-Jun-07 5:21
remotehuang19-Jun-07 5:21 
QuestionHow to remove message dll Pin
jsl_6314624-Jul-06 6:38
jsl_6314624-Jul-06 6:38 
QuestionCategory resources Pin
RomanI6-May-06 4:05
RomanI6-May-06 4:05 
QuestionQuestions Pin
Corvus Corax10-Feb-06 15:48
Corvus Corax10-Feb-06 15:48 
AnswerRe: Questions Pin
Stefan Repas18-Feb-06 4:18
Stefan Repas18-Feb-06 4:18 
Generalhi Pin
hossein hadi20-Dec-05 5:22
hossein hadi20-Dec-05 5:22 
GeneralGreat article Stefan! Pin
id10t18-Aug-05 8:18
id10t18-Aug-05 8:18 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.