Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does a .NET Windows service require a call to ServiceBase.Run()

I'm fairly new at working with Windows services but I found a peculiar incident and I would like some clarification. I have a Windows service written in C# which I install and start using the command line (great instructions found on stackoverflow). The main method of my service looks like this:

    static void Main(string[] args)
    {
        if (args.Length == 0)
        {
            ServiceBase.Run(new MyServiceName());
        }
        else if (args.Length == 1)
        {
            const string name = "MyServiceName";
            Type type = typeof(MyAssembly);
            switch (args[0])
            {
                case "-install":
                    ServiceUtils.InstallService(name, type);
                    ServiceUtils.StartService(args, name);
                    break;
                case "-uninstall":
                    ServiceUtils.StopService(name);
                    ServiceUtils.UninstallService(name, type);
                    break;
                default:
                    throw new NotImplementedException();
            }
        }
    }

When I debug, I ALWAYS send one parameter (-install) to the application. Because of this, the first if statement (if (args.Length == 0) is NEVER executed. This is expected and my service is installed and started just fine. However, if I remove that if statement and just leave the if (args.Length == 1) statement, my service installs correctly but it does not start and I get the following error:

Cannot start MyServiceName on computer '.'

My question is: Why is the code in the first if statement needed when it is NEVER executed in my application?

Here is the supporting code for the InstallService and StartService methods (which I got from stackoverflow also):

    public static void InstallService(string serviceName, Type t)
    {
        if (IsInstalled(serviceName)) return;

        try
        {
            Assembly a = t.Assembly;
            using (AssemblyInstaller installer = GetInstaller(a))
            {
                IDictionary state = new Hashtable();
                try
                {
                    installer.Install(state);
                    installer.Commit(state);
                }
                catch
                {
                    try
                    {
                        installer.Rollback(state);
                    }
                    catch
                    { }
                    throw;
                }
            }
        }
        catch
        {
            throw;
        }
    }

    public static void StartService(string[] args, string serviceName)
    {
        if (!IsInstalled(serviceName)) return;

        Console.WriteLine("Service is installed.  Attempting to start service.");

        ServiceController sc = new ServiceController();
        sc.ServiceName = serviceName;

        if (sc.Status == ServiceControllerStatus.Stopped)
        {
            Console.WriteLine("Starting {0}: ", sc.ServiceName);
            try
            {
                sc.Start(args);
                sc.WaitForStatus(ServiceControllerStatus.Running);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }
like image 780
my_overflowed_stack Avatar asked Dec 27 '22 22:12

my_overflowed_stack


2 Answers

the first if statement (if (args.Length == 0) is NEVER executed

That's not correct, it is executed. By ServiceController.Start(). You cannot see this because the service controller starts your EXE again, creating another process. A service process this time, not a console process. One that you don't have a debugger attached to. If you remove that if statement then the service immediately exits after getting started. And the service controller correctly complains about that with the "Cannot start MyServiceName" exception message.

like image 61
Hans Passant Avatar answered Jan 21 '23 07:01

Hans Passant


if (args.Length == 0)
{            
    ServiceBase.Run(new MyServiceName());
}

is run when the service is started by the Service Controller, as the Service Controller doesn't pass any arguments in to Main().

If you don't do ServiceBase.Run(new MyServiceName()), then your service will not respond to any commands from the Service Controller, and you get errors as the ones you see.

like image 29
Moose Avatar answered Jan 21 '23 09:01

Moose