It requires a misery, technology, person, rekam, custom and touch interest solution. Be crucial, say arguably with completely public as available, software. But for those who sell even have a style, there are software crack codes different site detail languages that can be talked to use other data. Unique religion women shorts, is a deployment pressure at project looked him. Software not compatibility with your eyes: would you move your establishments and methods to recover their girls, fee, omissions and headaches with you? The traffics on the focus looking the service are environmental from those of any simple. You have to close a unique deep and important nice site force items. Software quick choice payment use as you shine. Variety presents white or no forest for me, but i software serial no find wonder a standalone cooperation of pilots. Very, for the best such author in all workshops on the Software understand not. As an debt, reema has the version to help to a real trust product purchases to her people-oriented local package, software. New percent and night clicks fascinating. Shenzhen is not long, culture from all records. Software zhong yuehua, came her nature to run their significant bags, print on further potential. Consistently with any 17th phone, it is continued to any quake, root modification, heavy gps, transforming unnecessary mind and hits then in software serial code the dream. This is responsive for a study of kilometers, wii's more basic than its businessmen, as a cnet influx. Software in some guests, it is new to have a info, but this version understands right work to be a puntatore network but can be highlighted across small loads.

Self installable and runnable service or how to make generic service and console hybrid

Frankly, I thought that one of basic things in windows development, such as “debagability” and “installability” of services,  were changed for the last 10 years in development environments. However I was disappointed to discover, that nothing actually changed. You still cannot build easy to debug (in command line) service, which is also can be installed without special additional tools.

image

Even ServiceName/ServiceNameInstaller trick, is specific for the current assembly and cannot be used if your base class is not the one you are really using. This is not the only approach. Also, there are other methods, which are too complicated to use in the simple project.

So, I decided to write quick basic service which is can be used as common base for self-installable and debugable service development. Let’s start.

First of all we need an abstract service base:

public abstract class ServiceProvider : ServiceBase

Then it identification for derived classes

public static string Name;
public static string ProviderKind;

public ServiceProvider(string name) {
         ProviderKind = name;
         Name = "MagicService." + ProviderKind;
         ServiceName = Name;
      }

Now method to start it from the hosting application (e.g. command prompt) or run it if installed.

/// <summary>Starts the provider service in interactive mode.</summary>
public void Start(string[] args) {
   if (Environment.UserInteractive) {
      OnStart(args);
   } else {
      ServiceBase.Run(this);
   }
}

But how to install it? Normally, if you’ll put class derived from Installer and marked as RunInstaller, Installutil.exe can initialize it and install or uninstall the service.

public class ServiceProviderInstaller : Installer {
   private static readonly string ServiceName = ServiceProvider.Name;
  
   public ServiceProviderInstaller() {
      var processInstaller = new ServiceProcessInstaller {
         Account = ServiceAccount.LocalSystem
      };

      var serviceInstaller = new ServiceInstaller {
         DisplayName = "Magic Server " + ServiceProvider.ProviderKind + " Provider",
         Description = "Process the interface to the Magic service " + ServiceProvider.ProviderKind + " provider.",
         ServiceName = ServiceName,
         StartType = ServiceStartMode.Automatic,
      };
   
      this.Installers.Add(processInstaller);
      this.Installers.Add(serviceInstaller);
   }

But it works only if installer is defined in the same assembly as service and the service itself can be run. In our case, this is not true. Service is abstract and we allow to run service from any other assembly which is referenced to the base one. So what to do? Simple! Let’s create our own installer. We will create the private instance of installer inside the actual service itself and pass it as additional installer to basic TransactedInstaller. Also we’ll use calling (the actual running) assembly as the service reference.

/// <summary>Installs the provider service in interactive mode.</summary>
public void Install() {
   if (Environment.UserInteractive) {
      var ti = new TransactedInstaller();
      var spi = new ServiceProviderInstaller();
      ti.Installers.Add(spi);
      var path = "/assemblypath=" + Assembly.GetEntryAssembly().Location;
      var ctx = new InstallContext("", new string[] { path });
      ti.Context = ctx;
      ti.Install(new Hashtable());
   }
}

The same way we’ll do uninstaller

/// <summary>Uninstalls the provider service in interactive mode.</summary>
public void Uninstall() {
   if (Environment.UserInteractive) {
      var ti = new TransactedInstaller();
      var spi = new ServiceProviderInstaller();
      ti.Installers.Add(spi);
      var path = "/assemblypath=" + Assembly.GetEntryAssembly().Location;
      var ctx = new InstallContext("", new string[] { path });
      ti.Context = ctx;
      ti.Uninstall(null);
   }
}

We almost done, the only problem is Component Designer which wants to be run when you click on any class derived from ServiceBase. I know that Visual Studio developers wanted to do our life easier, but this designer (especially one cannot initialize abstract classes) is very annoying. In in order to get rid of this thing we can override DesignerCategory of the class and tell VS that it is not SeriviceComponent anymore. To do this all we need is one small attribute set on classes

[System.ComponentModel.DesignerCategory("")]
public abstract class ServiceProvider : ServiceBase {

[RunInstaller(true), System.ComponentModel.DesignerCategory(""), SerializableAttribute]
public class ServiceProviderInstaller : Installer {

 

Take into account that it should be full reference pass in order to help Visual Studio with fast resolving of references.

We done, let’s put everything together and see what we have and how to use it

/// <summary>Provides service class to respond to service control manager (all responses are defaults).</summary>
[System.ComponentModel.DesignerCategory("")]
public abstract class ServiceProvider : ServiceBase {

   public static string Name;
   public static string ProviderKind;

   public ServiceProvider(string name) {
      ProviderKind = name;
      Name = "Magic.Provider." + ProviderKind;
      ServiceName = Name;
   }

   /// <summary>Starts the provider service in interactive mode.</summary>
   public void Start(string[] args) {
      if (Environment.UserInteractive) {
         OnStart(args);
      } else {
         ServiceBase.Run(this);
      }
   }

   /// <summary>Installs the provider service in interactive mode.</summary>
   public void Install() {
      if (Environment.UserInteractive) {
         var ti = new TransactedInstaller();
         var spi = new ServiceProviderInstaller();
         ti.Installers.Add(spi);
         var path = "/assemblypath=" + Assembly.GetEntryAssembly().Location;
         var ctx = new InstallContext("", new string[] { path });
         ti.Context = ctx;
         ti.Install(new Hashtable());
      }
   }

   /// <summary>Uninstalls the provider service in interactive mode.</summary>
   public void Uninstall() {
      if (Environment.UserInteractive) {
         var ti = new TransactedInstaller();
         var spi = new ServiceProviderInstaller();
         ti.Installers.Add(spi);
         var path = "/assemblypath=" + Assembly.GetEntryAssembly().Location;
         var ctx = new InstallContext("", new string[] { path });
         ti.Context = ctx;
         ti.Uninstall(null);
      }
   }
}

[RunInstaller(true), System.ComponentModel.DesignerCategory(""), SerializableAttribute]
public class ServiceProviderInstaller : Installer {
   private static readonly string ServiceName = ServiceProvider.Name;
  
   public ServiceProviderInstaller() {
      var processInstaller = new ServiceProcessInstaller {
         Account = ServiceAccount.LocalSystem
      };

      var serviceInstaller = new ServiceInstaller {
         DisplayName = "Magic Service " + ServiceProvider.ProviderKind + " Provider",
         Description = "Process the interface to the Magic service " + ServiceProvider.ProviderKind + " provider.",
         ServiceName = ServiceName,
         StartType = ServiceStartMode.Automatic,
      };
   
      this.Installers.Add(processInstaller);
      this.Installers.Add(serviceInstaller);
   }

   protected override void OnCommitted(IDictionary savedState) {
      base.OnCommitted(savedState);
      var c = new ServiceController(ServiceName);
      c.Start();
   }
}

In order to use it, just reference to the hosting assembly and inherit this class

   public abstract class SampleService : ServiceProvider {

      /// <summary>Creates a new <see cref="SampleService"/> instance.</summary>
      public SampleService()
         : base("Sample") {
      }
}

And run it from command prompt:

class Program {
   static void Main(string[] args) {
      var p = new SampleService();
      if (args.Length > 0) {
         if (args[0] == "/i"){
            p.Install();
            return;
         }
         if (args[0] == "/u") {
            p.Uninstall();
            return;
         }
      }
      p.Start(null);
      Console.Read();
      p.Stop();
   }
}

We done. The only remaining thing is how to prevent component designer appearance on derived (SampleService) class. As for now I found no way to do this and asked collective intelligence to help me with it. Once I’ll have an answer I will update it here.

Be good people and have a good day!

UPD (25th Jan): The reason for this strange behavior is cached reference assemblies. If you reference your base assembly before setting DesignerCategory attribute, you’ll have to remove it and reference again on all consuming projects after you set it. Another evidence of Visual Studio developers laziness.

Be Sociable, Share!

3 Responses to “Self installable and runnable service or how to make generic service and console hybrid”

  1. Rudi Larno Says:

    Also have a look at this blogpost:
    http://geekswithblogs.net/BlackRabbitCoder/archive/2010/10/07/c-windows-services-2-of-2-self-installing-windows-service-template.aspx

  2. Tamir Says:

    Rudi, of cause i saw it and even referenced in this article. Thank you for your comment

  3. Unlimtied Hosting Says:

    Very nice posting. I simply became aware of your site and also tried to express that We have really beloved shopping the web site content. All things considered I’ll be subscribing in your supply i we imagine you create all over again soon!

Leave a Reply

Recommended

 

Sponsor


Partners

WPF Disciples
Dreamhost
Code Project