Work around limitations of VS 2005 Deployment Projects with Windows Services

I needed to deploy a Windows Service using an MSI. The project was built in VS 2005 using .Net 2.0; so far, so good.

The problem arose when I wanted to have the Service write to its own Event Log, rather than the default Application log. The standard Installer class from System.Configuration.Install, which otherwise does a fine job of installing the Windows Service, proved less than ideal because it automatically creates an EventLogSource with the same name as the Service executable, and sets its event log to be Application.

The solution lies in the way in which the Installer class contains a series of other installers in the Installer.Installers collection, which in turn contain other installer objects.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration.Install;
using System.Diagnostics;

namespace MyService
{
  [RunInstaller(true)]
  public partial class ProjectInstaller : Installer
  {
    public ProjectInstaller()
    {
      InitializeComponent();

      //
      // ProjectInstaller
      //
      this.Installers.AddRange(new System.Configuration.Install.Installer[] {
            this.serviceProcessInstaller1,
            this.serviceInstaller1});
    }

    private void serviceInstaller1_BeforeInstall(object sender, InstallEventArgs e)
    {
      RemoveEventLogInstallers(this, 0);
    }

    ///
    /// Recursive method to remove EventLogInstaller Installers from the Installers collection.
    /// This is needed to avoid automatic setting up of the event logs
    private static void RemoveEventLogInstallers(Installer installer, int depth)
    {
      Console.Out.WriteLine(String.Format("Number of installers at depth {0}: {1}", installer.Installers.Count, depth));
      for (int i = installer.Installers.Count - 1; i >= 0; --i)
      {
        Installer childInstaller = installer.Installers[i];
        Console.Out.WriteLine(String.Format("Installer {0} at depth {1} is of type: {2}", i, depth, installer.GetType().Name));

        RemoveEventLogInstallers(childInstaller, depth + 1); //recurse

        if (childInstaller is EventLogInstaller)
        {
          Console.Out.WriteLine(String.Format("Removing Installer {0} at depth {1} because it is of type: {2}", i, depth, childInstaller.GetType().Name));
          installer.Installers.Remove(childInstaller);
        }

      }
    }

    private void serviceInstaller1_BeforeUninstall(object sender, InstallEventArgs e)
    {
      RemoveEventLogInstallers(this, 0);
    }
  }
}

Search recursively through the entire tree of Installer objects and remove any installer whose type is EventLogInstaller, and the problem is solved! Make sure to do this before Install and before Uninstall, otherwise the (un)installation does not complete successfully.

Join the discussion...

This site uses Akismet to reduce spam. Learn how your comment data is processed.