16

I developed c# application, in which the apllication output type is Console Applicatiuon. I want to run this application as service. The Environment.UserInteractive is always true when i run it from visual studio or just double click on .exe.

Below is my code

 static void Main(string[] args)
        {
            // Get the version of the current application.
            Assembly assem = Assembly.GetExecutingAssembly();
            AssemblyName assemName = assem.GetName();
            Version ver = assemName.Version;
            // Console.WriteLine("{0}, Version {1}", assemName.Name, ver.ToString());

            Console.WriteLine("{0} version {1}", assemName.Name, ver.ToString());

            TouchService touchService = new TouchService();


            if (Environment.UserInteractive)
            {
                bool show_help = false;
                bool install_service = false;
                bool uninstall_service = false;
                string servicename = "";

                OptionSet p = new OptionSet()                  
                  .Add("h|?|help", delegate(string v) { show_help = v != null; })
                  .Add("s|servicename=", "name of installed service", delegate(string v) { servicename = v; })
                  .Add("i|install", "install program as a Windows Service. A valid servicename is needed.", delegate(string v) { install_service = v != null; })
                  .Add("u|uninstall", "uninstall program from Windows Services. A valid servicename is needed.", delegate(string v) { uninstall_service = v != null; });

                List<string> extra;
                try
                {
                    extra = p.Parse(args);
                }
                catch (OptionException e)
                {
                    Console.Write("TouchServer: ");
                    Console.WriteLine(e.Message);
                    Console.WriteLine("Try `TouchServer --help' for more information.");
                    return;
                }

                if (show_help)
                {
                    ShowHelp(p);
                    return;
                }

                else if (install_service)
                {
                    IntegratedServiceInstaller Inst = new IntegratedServiceInstaller();
                    Inst.Install(servicename, null, "Provides XML data over HTTP for Touch clients",                                                              
                                 System.ServiceProcess.ServiceAccount.NetworkService,
                                 System.ServiceProcess.ServiceStartMode.Manual);

                    return;
                }

                else if (uninstall_service)
                {
                    IntegratedServiceInstaller Inst = new IntegratedServiceInstaller();
                    Inst.Uninstall(servicename);
                    return;
                }

                // start and run the server,
                // and receive commands from the console
                else
                {

                    touchService.OnStart(args);                   
                    while(true)
                    {
                        Console.Write("TouchServer>");                        
                        string commandLine = Console.ReadLine().ToLower();

                        if (commandLine == "exit" || commandLine == "x")
                        {
                            break;
                        }
                        if (commandLine == "quit" || commandLine == "q")
                        {
                            break;
                        }

                        else if(commandLine == "version" || commandLine == "v")
                        {
                            Console.WriteLine("{0} version {1}", assem.GetName().Name, assem.GetName().Version.ToString());
                        }

                        else if (commandLine == "list" || commandLine == "l")
                        {
                            TouchServer.showURLs = (TouchServer.showURLs == false) ? true : false; 
                            Console.WriteLine("List URLs: {0}", (TouchServer.showURLs ? "active" : "inactive"));
                        }

                        else if (commandLine == "status" || commandLine == "s")
                        {
                            Console.WriteLine("{0,-20} {1,8}", "Name", "Sessions");                            
                            Console.WriteLine("----------------------------");
                            foreach (Site site in TouchServer.siteCollection.All)
                            {
                                Console.WriteLine("{0,-20} {1,8}", site.Name, site.AllSessions.Length);
                            }
                            Console.WriteLine();
                        }
                    }

                    touchService.OnStop();
                }
            }
            **else
            {
                ServiceBase[] ServicesToRun;
                ServicesToRun = new ServiceBase[] 
                { 
                    new TouchService() 
                };
                ServiceBase.Run(ServicesToRun);
            }**

How can i run it as a service, please help me. Thanks in advance sangita

1
  • 1
    +1 Remove all stuff related to installation of a service from console, and other stuff like check for interactivity, etc. I suppose it is not your code anyway, otherwise you whouldn't probably ansewer such an easy question here, after mastering such an advanced task written in your code ;) Commented Sep 15, 2011 at 7:54

4 Answers 4

17

Use File->New Project->Visual C#->Windows->Windows Service,

And Add your main code to OnStart(), and OnStop() event handlers, then install it as a service:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;

namespace MyWindowsService
{
    public partial class Service1 : ServiceBase
    {
        public Service1()
        {
            InitializeComponent();
        }

        protected override void OnStart(string[] args)
        {
        }

        protected override void OnStop()
        {
        }
    }
}
7

Here is the solution that I used, it works beautifully under Visual Studio 2012 and .NET 4.5. When I run in Console mode, it works perfectly, and when I run as a service using installutil.exe, it works perfectly.

Main File

File MainPayload.cs. You can ignore all of the other files, and insert your long running code into this file. Note the CancellationTokenSource, this is so your service can exit quickly when the service stops.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
//using Gurock.SmartInspect; // Only used if we are logging using SmartInspect (see www.SmartInspect.com).
namespace Demos___Service_Plus_Console
{
    /// <summary>
    /// Main entry point for both console and Windows service.
    /// </summary>
    public class MainPayload
    {
        private readonly CancellationTokenSource _cancellationTokenSource;

        /// <summary>
        /// Constructor; do not block in this call; it is for setup only.
        /// </summary>
        public MainPayload(CancellationTokenSource cancellationTokenSource)
        {
            // Do not block in this call; it is for setup only.
            _cancellationTokenSource = cancellationTokenSource;
        }   
        /// <summary>
        /// Long running task here.
        /// </summary>
        public void Run()
        {
            while (_cancellationTokenSource.IsCancellationRequested == false)
            {
                //SiAuto.Main.LogMessage(".");
                Console.WriteLine(".");

                // This will break every N seconds, or immediately if on cancellation token.
                _cancellationTokenSource.Token.WaitHandle.WaitOne(TimeSpan.FromSeconds(1));
            }
            //SiAuto.Main.LogMessage("Exited Run().");
            Console.WriteLine("Exited Run().");
            Thread.Sleep(500); // If we remove this line, then we will miss the final few writes to the console.
        }
    }
}

Supporting Files

File EntryPoint.cs. This is the entry point for both the console app and the service.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
//using Gurock.SmartInspect; // Only used if we are logging using SmartInspect (see www.SmartInspect.com).
namespace Demos___Service_Plus_Console
{
    internal static class EntryPoint
    {
        // Run in console mode.
        private static readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
        private static Task _task;

        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        public static void Main(string[] args)
        {
            //SiAuto.Si.Connections = "pipe(reconnect=\"true\", reconnect.interval=\"10\", backlog.enabled=\"true\", backlog.flushon=\"debug\", backlog.keepopen=\"true\")";
            //SiAuto.Si.Enabled = true;

            if (Environment.UserInteractive)
            {
                // Make sure that we can write to the console.
                StreamWriter standardOutput = new StreamWriter(Console.OpenStandardOutput()) {AutoFlush = true};
                Console.SetOut(standardOutput);

                // If Ctrl-C is pressed in the console, we get to here.
                Console.CancelKeyPress += new ConsoleCancelEventHandler(myHandler);

                MainPayload myMain = new MainPayload(_cancellationTokenSource); // Pass the token into the task.
                _task = Task.Run(() => myMain.Run());

                // Wait for the payload task to finish.
                while (_cancellationTokenSource.IsCancellationRequested == false)
                {
                    // This will break every N seconds, or immediately if cancellation token is pinged.
                    _cancellationTokenSource.Token.WaitHandle.WaitOne(TimeSpan.FromSeconds(10));
                }
            }
            else
            {
                // Run as Windows Service.
                var ServicesToRun = new ServiceBase[]
                    {
                        new ServiceController()
                    };
                ServiceBase.Run(ServicesToRun);
            }

            _task.Wait(TimeSpan.FromSeconds(10)); // Delay for console to write its final output.
        }

        static void myHandler(object sender, ConsoleCancelEventArgs args)
        {
            _cancellationTokenSource.Cancel();
            //SiAuto.Main.LogMessage("CtrlC pressed.");
            Console.WriteLine("CtrlC pressed.");
        }
    }
}

File ProjectInstaller.cs. This is the installer for the service.

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration.Install;
using System.Linq;
using System.Threading.Tasks;

namespace Demos___Service_Plus_Console
{
    [RunInstaller(true)]
    public partial class ProjectInstaller : System.Configuration.Install.Installer
    {
        public ProjectInstaller()
        {
            InitializeComponent();
        }

        private void serviceInstaller1_AfterInstall(object sender, InstallEventArgs e)
        {

        }

        private void serviceProcessInstaller1_AfterInstall(object sender, InstallEventArgs e)
        {

        }
    }
}

File ServiceController.cs. This contains the Start() and Stop() methods for the service.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Demos___Service_Plus_Console
{
    /// <summary>
    /// When running in service mode.
    /// </summary>
    public partial class ServiceController : ServiceBase
    {
        public ServiceController()
        {
            InitializeComponent();
        }

        readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();

        // Initialize payload.
        private MainPayload myMain;

        protected override void OnStart(string[] args)
        {
            myMain = new MainPayload(cancellationTokenSource); // Pass the token into the task.
            Task.Run(() => myMain.Run());
        }

        protected override void OnStop()
        {
            cancellationTokenSource.Cancel();
        }
    }
}

How to install as a Windows Service

To install this as a Windows service, use installutil.exe ServiceName.exe. To uninstall, use installutil.exe ServiceName.exe -u. This means that you will have to open a Visual Studio 2012 x32 command prompt which is under Visual Studio Tools in the Start Menu. If you are compiling in 32-bit mode, use the 32-bit command prompt, and if you are compiling in 64-bit mode, open a 64-bit command prompt (this means the paths are set up properly, as installutil.exe has separate versions depending on whether its 32-bit or 64-bit).

Open up the Services panel, then look for a new service named ServiceController. When you start it, if you have a logging framework turned on, then you will see messages ticking into the log every second.

How I created this code

It looks like a lot of code, but its all based on the Windows Service project template in Visual Studio 2012. I created a new windows service, then used Environment.UserInteracive to switch between the console or the service. I added CancellationTokens to make sure that if the service was stopped, the task would stop quickly as well (even if it was in a delay).

The only other trick is to right click on the gray background of the "Service1.cs" page, then click "Add Installer" which adds the installer. If you don't do this, then installutil.exe will give you an error.

enter image description here

You must also right click on serviceProcessInstaller1 then select Properties and set the Account to LocalService, or else it will ask you for username credentials when you install the service.

Here are the extra References required (these are automatically added when you create a new Windows Service using the template):

  • System.ServiceProcess
  • System.Configuration.Install

You'll notice references to Gurock.SmartInspect and SiAuto. If you want to observe logs created by the running service, you can use this or something else like NLog or log4net.

1
  • I'm using your implementation successfully - thank you, and +1!
    – OnoSendai
    Commented Mar 4, 2015 at 20:52
5

Environment.UserInteractive is false only when it is actually running as a service. When you double click it or launch it from Visual Studio; it runs as a normal console application and desktop is available to it therefore Environment.UserInteractive is true.

You can derive your class from ConsoleService class of Squiggle's code base to make a console app that can also run as a windows service.

public class ConsoleService : ServiceBase
{
    public void RunConsole(string[] args)
    {
        Trace.Listeners.Add(new ConsoleTraceListener());
        OnStart(args);
        Trace.WriteLine(this.ServiceName + " running... Press any key to stop");
        Trace.WriteLine("");
        Console.ReadKey();
        OnStop();
    }

    public static void Run<TService>(string[] args) where TService : ConsoleService, new()
    {
        AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
        if (Environment.UserInteractive)
        {
            try
            {
                string option = args.Length > 0 ? args[0].ToUpperInvariant() : String.Empty;
                switch (option)
                {
                    case "-I":
                    case "/I":
                        ManagedInstallerClass.InstallHelper(new string[] { Assembly.GetCallingAssembly().Location });
                        break;
                    case "-U":
                    case "/U":
                        ManagedInstallerClass.InstallHelper(new string[] { "/U", Assembly.GetCallingAssembly().Location });
                        break;
                    default:
                        new TService().RunConsole(args);
                        break;
                }
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine(ex.Message);
            }
        }
        else
        {
            ServiceBase[] servicesToRun = new ServiceBase[] { new TService() };
            ServiceBase.Run(servicesToRun);
        }
    }

    static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
        if (e.ExceptionObject is Exception)
            Trace.WriteLine(((Exception)e.ExceptionObject).Message);
    }
}
1
  • +1 cool, seems to me that he specifically needs only service behavior except console one Commented Sep 15, 2011 at 7:49
5

I have just tested this in Visual Studio 2013 and it works.

  1. Create new "Windows Service" project.
  2. Change the project Output type to "Console Application"

Here is how my Program.cs looks like:

static class Program
{
  /// <summary>
  /// </summary>
  static void Main()
  {
    if (!Environment.UserInteractive)
    {
      ServiceBase[] ServicesToRun;
      ServicesToRun = new ServiceBase[] 
          { 
              new Service1() 
          };
      ServiceBase.Run(ServicesToRun);
    }
    else
    {
      Console.Write("Hit any key to continue...");
      Console.ReadKey();
    }
  }
}

You can add service install like this

You can install the service with this command: installutil.exe /i YouExeName.exe

You can uninstall the service with this command: installutil.exe /u YouExeName.exe

1
  • If this works without bad side effects, it is nice short and clean. Nice!
    – Cameron
    Commented Feb 13, 2015 at 19:46

Not the answer you're looking for? Browse other questions tagged or ask your own question.