views:

4047

answers:

9

I am having difficulty reliably creating / removing event sources during the installation of my .Net Windows Service.

Here is the code from my ProjectInstaller class:

// Create Process Installer
ServiceProcessInstaller spi = new ServiceProcessInstaller();
spi.Account = ServiceAccount.LocalSystem;

// Create Service
ServiceInstaller si = new ServiceInstaller();
si.ServiceName = Facade.GetServiceName();
si.Description = "Processes ...";
si.DisplayName = "Auto Checkout";
si.StartType = ServiceStartMode.Automatic;

// Remove Event Source if already there
if (EventLog.SourceExists("AutoCheckout"))
    EventLog.DeleteEventSource("AutoCheckout");

// Create Event Source and Event Log     
EventLogInstaller log = new EventLogInstaller();
log.Source = "AutoCheckout";
log.Log = "AutoCheckoutLog";

Installers.AddRange(new Installer[] { spi, si, log });

The facade methods referenced just return the strings for the name of the log, service, etc.

This code works most of the time, but recently after installing I started getting my log entries showing up in the Application Log instead of the custom log. And the following errors are in the log as well:

The description for Event ID ( 0 ) in Source ( AutoCheckout ) cannot be found. The local computer may not have the necessary registry information or message DLL files to display messages from a remote computer. You may be able to use the /AUXSOURCE= flag to retrieve this description; see Help and Support for details.

For some reason it either isn't properly removing the source during the uninstall or it isn't creating it during the install.

Any help with best practices here is appreciated.

Thanks!

In addition, here is a sample of how I am writing exceptions to the log:

// Write to Log EventLog.WriteEntry(Facade.GetEventLogSource(), errorDetails, EventLogEntryType.Error, 99);

Regarding stephbu's answer: The recommended path is an installer script and installutil, or a Windows Setup routine.

I am using a Setup Project, which performs the installation of the service and sets up the log. Whether I use the installutil.exe or the windows setup project I believe they both call the same ProjectInstaller class I show above.

I see how the state of my test machine could be causing the error if the log isn't truely removed until rebooting. I will experiment more to see if that solves the issue.

EDIT: I'm interested in a sure fire way to register the source and the log name during the installation of the service. So if the service had previously been installed, it would remove the source, or reuse the source during subsequent installations.

I haven't yet had an opportunity to learn WiX to try that route.

+2  A: 

Couple of things here

Creating Event Logs and Sources on the fly is pretty frowned up. primarily because of the rights required to perform the action - you don't really want to bless your applications with that power.

Moreover if you delete an event log or source the entry is only truely deleted when the server reboots, so you can get into wierd states if you delete and recreate entries without bouncing the box. There are also a bunch of unwritten rules about naming conflicts due to the way the metadata is stored in the registry.

The recommended path is an installer script and installutil, or a Windows Setup routine.

stephbu
Rebooting has not solved the problem and I have used both InstallUtil and the Windows setup installer, neither one properly create the Log Source...
Jason Stevenson
Also the event source are not be created on the fly, they are being created during installation, which is executed as an admin.
Jason Stevenson
+1  A: 

The best recommendation would be to not use the Setup Project in Visual Studio. It has very severe limitations. I had very good results with WiX

Albert
Thanks Albert I am downloading it now and will give it a try tomorrow!
Jason Stevenson
+2  A: 

I have to agree with stephbu about the "weird states" that the event log gets into, I've run into that before. If I were to guess, some of your difficulties lie there.

However, the best way that I know of to do event logging in the application is actually with a TraceListener. You can configure them via the service's app.config:

http://msdn.microsoft.com/en-us/library/system.diagnostics.eventlogtracelistener.aspx

There is a section near the middle of that page that describes how to use the EventLog property to specify the EventLog you wish to write to.

Hope that helps.

Zachary Yates
The EventLogTraceListener would still need the EventLogSource to exist, if not it will try to create it and that would require admin priveledges. My service will not run as an admin, the user installing the service will; which is why I create the EventLog and EventLogSource during the setup.
Jason Stevenson
A: 

I experienced some similar weird behaviour because I tried to register an event source with the same name as the service I was starting.

I notice that you also have the DisplayName set to the same name as your event Source.

On starting the service up, we found that Windows logged a "Service started successfully" entry in the Application log, with source as the DisplayName. This seemed to have the effect of registering Application Name with the application log.

In my event logger class I later tried to register Application Name as the source with a different event log, but when it came to adding new event log entries they always got added to the Application log.

I also got the "The description for Event ID ( 0 ) in Source" message several times.

As a work around I simply registered the message source with a slightly different name to the DisplayName, and it's worked ever since. It would be worth trying this if you haven't already.

John Sibly
Yeah, I actually do have two different names, so that the start/stop is logged in the Application Log, while the app events are all logged in the custom log.This is an issue that only arose on my development machine. Perhaps because of the registering/unregistering too much...
Jason Stevenson
A: 

I am having the same problems. In my case it seems that Windows installer is adding the event source which is of the same name as my service automatically and this seems to cause problems. Are you using the same name for the windows service and for the log source? Try changing it so that your event log source is called differently then the name of the service.

A: 

The problem comes from installutil which by default registers an event source with your services name in the "Application" EventLog. I'm still looking for a way to stop it doing this crap. It would be really nice if one could influence the behaviour of installutil :(

nojetlag
+5  A: 

The ServiceInstaller class automatically creates an EventLogInstaller and puts it inside its own Installers collection. Try this code:

ServiceProcessInstaller serviceProcessInstaller = new ServiceProcessInstaller();
serviceProcessInstaller.Password = null;
serviceProcessInstaller.Username = null;
serviceProcessInstaller.Account = ServiceAccount.LocalSystem;

// serviceInstaller
ServiceInstaller serviceInstaller = new ServiceInstaller();
serviceInstaller.ServiceName = "MyService";
serviceInstaller.DisplayName = "My Service";
serviceInstaller.StartType = ServiceStartMode.Automatic;
serviceInstaller.Description = "My Service Description";
// kill the default event log installer
serviceInstaller.Installers.Clear(); 

// Create Event Source and Event Log     
EventLogInstaller logInstaller = EventLogInstaller();
logInstaller.Source = "MyService"; // use same as ServiceName
logInstaller.Log = "MyLog";

// Add all installers
this.Installers.AddRange(new Installer[] {
   serviceProcessInstaller, serviceInstaller, logInstaller
});
helb
A: 

Following helb's suggestion resolved the problem for me. Killing the default event log installer, at the point indicated in his example, prevented the installer from automatically registering my Windows Service under the Application Event log.

Far too much time was lost attempting to resolve this frustrating quirk. Thanks a million!

FWIW, I could not modify the code within my designer-generated ProjectInstaller class without causing VS to carp about the mods. I scrapped the designer-generated code and manually entered the class.

A: 

I also followed helb's suggestion, except that I basically used the standard designer generated classes (the default objects "ServiceProcessInstaller1" and "ServiceInstaller1"). I decided to post this since it is a slightly simpler version; and also because I am working in VB and people sometimes like to see the VB-way.

As tartheode said, you should not modify the designer-generated ProjectInstaller class in the ProjectInstaller.Designer.vb file, but you can modify the code in the ProjectInstaller.vb file. After creating a normal ProjectInstaller (using the standard 'Add Installer' mechanism), the only change I made was in the New() of the ProjectInstaller class. After the normal "InitializeComponent()" call, I inserted this code:

  ' remove the default event log installer 
  Me.ServiceInstaller1.Installers.Clear()

  ' Create an EventLogInstaller, and set the Event Source and Event Log      
  Dim logInstaller As New EventLogInstaller
  logInstaller.Source = "MyServiceName"
  logInstaller.Log = "MyCustomEventLogName"

  ' Add the event log installer
  Me.ServiceInstaller1.Installers.Add(logInstaller)

This worked as expected, in that the installer did not create the Event Source in the Application log, but rather created in the new custom log file.

However, I had screwed around enough that I had a bit of a mess on one server. The problem with the custom logs is that if the event source name exists associated to the wrong log file (e.g. the 'Application' log instead of your new custom log), then the source name must first be deleted; then the machine rebooted; then the source can be created with association to the correct log. The Microsoft Help clearly states (in the EventLogInstaller class description):

The Install method throws an exception if the Source property matches a source name that is registered for a different event log on the computer.

Therefore, I also have this function in my service, which is called when the service starts:

   Private Function EventLogSourceNameExists() As Boolean
      'ensures that the EventSource name exists, and that it is associated to the correct Log 

      Dim EventLog_SourceName As String = Utility.RetrieveAppSetting("EventLog_SourceName")
      Dim EventLog_LogName As String = Utility.RetrieveAppSetting("EventLog_LogName")

      Dim SourceExists As Boolean = EventLog.SourceExists(EventLog_SourceName)
      If Not SourceExists Then
         ' Create the source, if it does not already exist.
         ' An event log source should not be created and immediately used.
         ' There is a latency time to enable the source, it should be created
         ' prior to executing the application that uses the source.
         'So pass back a False to cause the service to terminate.  User will have 
         'to re-start the application to make it work.  This ought to happen only once on the 
         'machine on which the service is newly installed

         EventLog.CreateEventSource(EventLog_SourceName, EventLog_LogName)  'create as a source for the SMRT event log
      Else
         'make sure the source is associated with the log file that we want
         Dim el As New EventLog
         el.Source = EventLog_SourceName
         If el.Log <> EventLog_LogName Then
            el.WriteEntry(String.Format("About to delete this source '{0}' from this log '{1}'.  You may have to kill the service using Task Manageer.  Then please reboot the computer; then restart the service two times more to ensure that this event source is created in the log {2}.", _
            EventLog_SourceName, el.Log, EventLog_LogName))

            EventLog.DeleteEventSource(EventLog_SourceName)
            SourceExists = False  'force a close of service
         End If
      End If
      Return SourceExists
   End Function

If the function returns False, the service startup code simply stops the service. This function pretty much ensures that you will eventually get the correct Event Source name associated to the correct Event Log file. You may have to reboot the machine once; and you may have to try starting the service more than once.

Tod Flak