Run a C# .NET console application as a windows service

Share
This article will guide you through the necessary steps on creating a flexible hybrid application that is a mixture of a console application and a windows service. Takeaways : ⇒ Learn to create a runnable application which can later on be installed as a windows service. ⇒ A working template for your future usages available on Github.

Table Of Contents

Background

Most of the time, when I am assigned a new task like integrating a 3rd party tool or a plugin into my .NET based application, I simply rely on trying it out first hand via a simple Console application and quickly integrating the necessary parts. And that is what probably any .NET developer would do (some might have a different approach).

But, sometimes there are requirements where the task is to create a job or a service with a repetitive flow and made runnable in a scheduled fashion. Some common scenarios are creating a job for delivering messages and updates,  performing backups, doing cleanups and many more of such stuffs.

There are many options to achieve these requirements by using a third party tool. Some of those tools are : Hangfire, Quartz, Topshelf etc.

But, since I would have already tried out and tested the feature via a console application, my next step would be simply to deploy the code as a service. This generally involves, creating a new windows service application and integrating my previously tested code.

But instead of creating a new application, I do some tweaks here and there, improvise the old console application and make it behave as a windows service and vice-versa.  I can then easily install this console app as a windows service into my system whenever I want.

Lets see how it is done …

The Console Application

Basically we start by creating a new console application. Here is a simple console application that simply just writes to a file.

Program.cs file
class Program
{
    static void Main(string[] args)
    {
        DataProcessor dataProcessor = new DataProcessor();
        dataProcessor.Execute();
    }
}
DataProcessor.cs file
using System;
using System.IO;
using System.Reflection;

namespace HybridSvx
{
    internal class DataProcessor
    {

        internal void Execute()
        {
            try
            {
                LogMessage();
            }
            catch (Exception e)
            {
                Console.WriteLine("Exception: " + e.Message);
            }
        }


        /// <summary>
        /// Writes a simple message to a file in the application directory
        /// </summary>
        private void LogMessage()
        {
            string assemblyName = Assembly.GetCallingAssembly().GetName().Name;
            string fileName = string.Format(assemblyName + "-{0:yyyy-MM-dd}.log", DateTime.Now);
            string filePath = AppDomain.CurrentDomain.BaseDirectory + fileName;

            string message = "Execution completed at " + DateTime.Now.ToShortTimeString();

            Console.WriteLine(message);
            using (StreamWriter sw = new StreamWriter(filePath, true))
            {
                sw.WriteLine(message);
                sw.Close();
            }
        }

    }
}

Setting Up The Windows Service

The next step would be to include the necessary libraries and write some code that are required in order to create a windows service. The steps are as follows:

Step 1: Add New Item

In your Visual Studio Console Project ⇒ Right click the project ⇒ Add ⇒ New Item

Add New Windows Service Item
Step 2 : Add a new Windows Service

Search and select Windows Service ⇒ Provide a name eg: ‘HybridSvxService.cs‘ ⇒ Click Add

New Windows Service

This will add a new file which will contain your code that needs to be executed by the windows service.

The Windows service needs to be running continuously, else it will automatically stop after it has completed its execution. Hence, in order to make the service run continuously, we will introduce a simple timer as follows :

using System.ServiceProcess;
using System.Timers;

namespace HybridSvx
{
    partial class HybridSvxService : ServiceBase
    {
        private static Timer aTimer;

        public HybridSvxService()
        {
            InitializeComponent();
        }

        protected override void OnStart(string[] args)
        {
            aTimer = new Timer(10000); // 10 Seconds
            aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
            aTimer.Enabled = true;
        }

        private static void OnTimedEvent(object source, ElapsedEventArgs e)
        {
            DataProcessor dataProcessor = new DataProcessor();
            dataProcessor.Execute();
        }

        protected override void OnStop()
        {
            aTimer.Stop();
        }

    }
}
Step 3 : Setup the Installer Class

You first need to add a reference to the System.Configuration.Install assembly via Project ⇒ Add ⇒ Reference.

Next, Add a new Class with a meaningful name eg ‘HybridSvxServiceInstaller.cs‘ and paste in the following code :

using System.ComponentModel;
using System.Configuration.Install;
using System.ServiceProcess;

namespace HybridSvx
{
    [RunInstaller(true)]
    public class HybridSvxServiceInstaller : Installer
    {
        public HybridSvxServiceInstaller()
        {
            ServiceProcessInstaller serviceProcessInstaller = new ServiceProcessInstaller();
            ServiceInstaller serviceInstaller = new ServiceInstaller();

            // Setup the Service Account type per your requirement
            serviceProcessInstaller.Account = ServiceAccount.LocalSystem;
            serviceProcessInstaller.Username = null;
            serviceProcessInstaller.Password = null;

            serviceInstaller.ServiceName = "HybridSvx";
            serviceInstaller.DisplayName = "HybridSvx Service";
            serviceInstaller.StartType = ServiceStartMode.Automatic;
            serviceInstaller.Description = "My custom hybrid service";

            this.Installers.Add(serviceProcessInstaller);
            this.Installers.Add(serviceInstaller);
        }

    }
}

The above code will provide your windows service with the necessary foundation for custom installation like the Service name and description, service account type etc.

Configure Self Installer

Adding a Self installer within the windows service itself, removes the hurdle of depending on a tool like InstallUtil.exe for installation. This makes the installation much easier and straight forward.

In your project, create a new class file with the name SelfInstaller.cs and paste the following code:

using System.Configuration.Install;
using System.Reflection;

namespace HybridSvx
{
    public class SelfInstaller
    {
        private static readonly string _exePath = Assembly.GetExecutingAssembly().Location;
        public static bool InstallMe()
        {
            bool result;
            try
            {
                ManagedInstallerClass.InstallHelper(new string[] {
                    SelfInstaller._exePath
                });
            }
            catch
            {
                result = false;
                return result;
            }
            result = true;
            return result;
        }

        public static bool UninstallMe()
        {
            bool result;
            try
            {
                ManagedInstallerClass.InstallHelper(new string[] {
                    "/u", SelfInstaller._exePath
                });
            }
            catch
            {
                result = false;
                return result;
            }
            result = true;
            return result;
        }

    }
}

Customize Program.cs

The final tweak would be to customize the Program.cs to support the functioning of both a console application and the windows service.

Summary : If you install the application, it will work as a windows service and if you directly run it, it will behave as a console application.

Code :

using System;
using System.ServiceProcess;

namespace HybridSvx
{
    class Program
    {
        static void Main(string[] args)
        {
            if ((!Environment.UserInteractive))
            {
                Program.RunAsAService();
            }
            else
            {
                if (args != null && args.Length > 0)
                {
                    if (args[0].Equals("-i", StringComparison.OrdinalIgnoreCase))
                    {
                        SelfInstaller.InstallMe();
                    }
                    else
                    {
                        if (args[0].Equals("-u", StringComparison.OrdinalIgnoreCase))
                        {
                            SelfInstaller.UninstallMe();
                        }
                        else
                        {
                            Console.WriteLine("Invalid argument!");
                        }
                    }
                }
                else
                {
                    Program.RunAsAConsole();
                }
            }
        }

        static void RunAsAConsole()
        {
            DataProcessor dataProcessor = new DataProcessor();
            dataProcessor.Execute();
        }

        static void RunAsAService()
        {
            ServiceBase[] servicesToRun = new ServiceBase[]
           {
                new HybridSvxService()
           };
            ServiceBase.Run(servicesToRun);
        }


    }
}

Running the Program

Now, you have a working application that can either run as a console application or be installed as a windows service. Here is how you can run the application in both ways :

As a Console Application

Simply double click to run the application

As a Windows Service
  1. Navigate to the project output directory and locate HybridSvx.exe.
  2. Fire up a command prompt with an administrator privilege.
  3. To Install ⇒ type HybridSvx.exe -i
  4. To Un-innstall ⇒ type HybridSvx.exe -u
  5. To View the service ⇒ Press Win + R ⇒ type services.msc
Services-msc

References

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *