Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

A Better Windows Service Helper

DZone's Guide to

A Better Windows Service Helper

· Performance Zone
Free Resource

Transform incident management with machine learning and analytics to help you maintain optimal performance and availability while keeping pace with the growing demands of digital business with this eBook, brought to you in partnership with BMC.

I’ve updated my Windows service helper so that it also can install your service for you (through the command line).

Easier Debugging

The first thing that you can do is run your Windows service as a console application. That makes debugging a lot easier since you can just start the application (F5) without having to attach to a process.

To be able to do that, you have to add -console as an debugging argument:

Shows the debug settings under project properties

How to configure Visual Studio

Next you’ll have to add the following to your Program.cs:

internal static class Program
{
    private static void Main()
    {
        // Enable running as a console app
        if (WindowsServiceHelper.RunAsConsoleIfRequested<Service1>())
            return;
 
        ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[]
            {
                new Service1()
            };
        ServiceBase.Run(ServicesToRun);
    }
}

Now you can just run the application. A console window will be created automatically.

Install or Uninstall Using Command Line Arguments

You can also enable installing/uninstalling your application by using command arguments.

Just add the following to Program.cs:

internal static class Program
{
    private static void Main()
    {
        // Enable install/uninstall
        if (WindowsServiceHelper.ManageServiceIfRequested(Environment.GetCommandLineArgs()))
            return;
 
        ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[]
            {
                new Service1()
            };
        ServiceBase.Run(ServicesToRun);
    }
}

To install:

yourApp.exe -install

To uninstall:

yourApp.exe -uninstall

The Code

internal static class Program
{
    private static void Main()
    {
        // runs the app as a console application if the command argument "-console" is used
        if (WindowsServiceHelper.RunAsConsoleIfRequested<Service1>())
            return;

        // uses "-install" and "-uninstall" to manage the service.
        if (WindowsServiceHelper.ManageServiceIfRequested(Environment.GetCommandLineArgs()))
            return;

        ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[]
            {
                new Service1()
            };
        ServiceBase.Run(ServicesToRun);
    }
}

public static class WindowsServiceHelper
{
    private static readonly string[] InstallArguments = new[] {"/i", "/install", "-i", "-install"};
    private static readonly string[] UninstallArguments = new[] {"/u", "/uninstall", "-u", "-uninstall"};

    public static void AttachConsole()
    {
        AllocConsole();
    }

    public static bool ManageServiceIfRequested(string[] arguments)
    {
        try
        {
            if (!arguments.Any(x => InstallArguments.Contains(x)) &&
                !arguments.Any(x => UninstallArguments.Contains(x)))
                return false;

            AttachConsole();

            var serviceFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "InstallUtil.InstallLog");
            if (File.Exists(serviceFile))
                File.Delete(serviceFile);

            if (arguments.Any(x => InstallArguments.Contains(x)))
                ManagedInstallerClass.InstallHelper(new[] {Assembly.GetExecutingAssembly().Location});
            else if (arguments.Any(x => UninstallArguments.Contains(x)))
                ManagedInstallerClass.InstallHelper(new[] {"/u", Assembly.GetExecutingAssembly().Location});
            else
                return false;
        }
        catch (Exception exception)
        {
            Console.WriteLine("Error: " + exception.Message);
        }

        Console.WriteLine("Service configuration is done. Press any key to exit console");
        Console.ReadKey();

        return true;
    }


    public static bool RunAsConsoleIfRequested<T>() where T : ServiceBase, new()
    {
        if (!Environment.CommandLine.Contains("-console"))
            return false;

        AttachConsole();

        var service = new T();
        var onstart = service.GetType().GetMethod("OnStart", BindingFlags.Instance | BindingFlags.NonPublic);

        var args = Environment.GetCommandLineArgs().Where(name => name != "-console").ToArray();
        onstart.Invoke(service, new object[] {args});

        Console.WriteLine("Your service named '" + service.GetType().FullName +
                            "' is up and running.\r\nPress 'ENTER' to stop it.");
        Console.ReadLine();

        var onstop = service.GetType().GetMethod("OnStop", BindingFlags.Instance | BindingFlags.NonPublic);
        onstop.Invoke(service, null);
        Console.WriteLine("Service is stopped.  Press any key to exit console");
        Console.ReadKey();
        return true;
    }

    [DllImport("kernel32")]
    private static extern bool AllocConsole();
}


Evolve your approach to Application Performance Monitoring by adopting five best practices that are outlined and explored in this e-book, brought to you in partnership with BMC.

Topics:

Published at DZone with permission of Jonas Gauffin, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}