How to make a .NET Windows Service start right after the installation?

103,658

Solution 1

You can do this all from within your service executable in response to events fired from the InstallUtil process. Override the OnAfterInstall event to use a ServiceController class to start the service.

http://msdn.microsoft.com/en-us/library/system.serviceprocess.serviceinstaller.aspx

Solution 2

I've posted a step-by-step procedure for creating a Windows service in C# here. It sounds like you're at least to this point, and now you're wondering how to start the service once it is installed. Setting the StartType property to Automatic will cause the service to start automatically after rebooting your system, but it will not (as you've discovered) automatically start your service after installation.

I don't remember where I found it originally (perhaps Marc Gravell?), but I did find a solution online that allows you to install and start your service by actually running your service itself. Here's the step-by-step:

  1. Structure the Main() function of your service like this:

    static void Main(string[] args)
    {
        if (args.Length == 0) {
            // Run your service normally.
            ServiceBase[] ServicesToRun = new ServiceBase[] {new YourService()};
            ServiceBase.Run(ServicesToRun);
        } else if (args.Length == 1) {
            switch (args[0]) {
                case "-install":
                    InstallService();
                    StartService();
                    break;
                case "-uninstall":
                    StopService();
                    UninstallService();
                    break;
                default:
                    throw new NotImplementedException();
            }
        }
    }
    
  2. Here is the supporting code:

    using System.Collections;
    using System.Configuration.Install;
    using System.ServiceProcess;
    
    private static bool IsInstalled()
    {
        using (ServiceController controller = 
            new ServiceController("YourServiceName")) {
            try {
                ServiceControllerStatus status = controller.Status;
            } catch {
                return false;
            }
            return true;
        }
    }
    
    private static bool IsRunning()
    {
        using (ServiceController controller = 
            new ServiceController("YourServiceName")) {
            if (!IsInstalled()) return false;
            return (controller.Status == ServiceControllerStatus.Running);
        }
    }
    
    private static AssemblyInstaller GetInstaller()
    {
        AssemblyInstaller installer = new AssemblyInstaller(
            typeof(YourServiceType).Assembly, null);
        installer.UseNewContext = true;
        return installer;
    }
    
  3. Continuing with the supporting code...

    private static void InstallService()
    {
        if (IsInstalled()) return;
    
        try {
            using (AssemblyInstaller installer = GetInstaller()) {
                IDictionary state = new Hashtable();
                try {
                    installer.Install(state);
                    installer.Commit(state);
                } catch {
                    try {
                        installer.Rollback(state);
                    } catch { }
                    throw;
                }
            }
        } catch {
            throw;
        }
    }
    
    private static void UninstallService()
    {
        if ( !IsInstalled() ) return;
        try {
            using ( AssemblyInstaller installer = GetInstaller() ) {
                IDictionary state = new Hashtable();
                try {
                    installer.Uninstall( state );
                } catch {
                    throw;
                }
            }
        } catch {
            throw;
        }
    }
    
    private static void StartService()
    {
        if ( !IsInstalled() ) return;
    
        using (ServiceController controller = 
            new ServiceController("YourServiceName")) {
            try {
                if ( controller.Status != ServiceControllerStatus.Running ) {
                    controller.Start();
                    controller.WaitForStatus( ServiceControllerStatus.Running, 
                        TimeSpan.FromSeconds( 10 ) );
                }
            } catch {
                throw;
            }
        }
    }
    
    private static void StopService()
    {
        if ( !IsInstalled() ) return;
        using ( ServiceController controller = 
            new ServiceController("YourServiceName")) {
            try {
                if ( controller.Status != ServiceControllerStatus.Stopped ) {
                    controller.Stop();
                    controller.WaitForStatus( ServiceControllerStatus.Stopped, 
                         TimeSpan.FromSeconds( 10 ) );
                }
            } catch {
                throw;
            }
        }
    }
    
  4. At this point, after you install your service on the target machine, just run your service from the command line (like any ordinary application) with the -install command line argument to install and start your service.

I think I've covered everything, but if you find this doesn't work, please let me know so I can update the answer.

Solution 3

Visual Studio

If you are creating a setup project with VS, you can create a custom action who called a .NET method to start the service. But, it is not really recommended to use managed custom action in a MSI. See this page.

ServiceController controller  = new ServiceController();
controller.MachineName = "";//The machine where the service is installed;
controller.ServiceName = "";//The name of your service installed in Windows Services;
controller.Start();

InstallShield or Wise

If you are using InstallShield or Wise, these applications provide the option to start the service. Per example with Wise, you have to add a service control action. In this action, you specify if you want to start or stop the service.

Wix

Using Wix you need to add the following xml code under the component of your service. For more information about that, you can check this page.

<ServiceInstall 
    Id="ServiceInstaller"  
    Type="ownProcess"  
    Vital="yes"  
    Name=""  
    DisplayName=""  
    Description=""  
    Start="auto"  
    Account="LocalSystem"   
    ErrorControl="ignore"   
    Interactive="no">  
        <ServiceDependency Id="????"/> ///Add any dependancy to your service  
</ServiceInstall>

Solution 4

You need to add a Custom Action to the end of the 'ExecuteImmediate' sequence in the MSI, using the component name of the EXE or a batch (sc start) as the source. I don't think this can be done with Visual Studio, you may have to use a real MSI authoring tool for that.

Solution 5

To start it right after installation, I generate a batch file with installutil followed by sc start

It's not ideal, but it works....

Share:
103,658
Jader Dias
Author by

Jader Dias

Perl, Javascript, C#, Go, Matlab and Python Developer

Updated on April 04, 2020

Comments

  • Jader Dias
    Jader Dias about 4 years

    Besides the service.StartType = ServiceStartMode.Automatic my service does not start after installation

    Solution

    Inserted this code on my ProjectInstaller

    protected override void OnAfterInstall(System.Collections.IDictionary savedState)
    {
        base.OnAfterInstall(savedState);
        using (var serviceController = new ServiceController(this.serviceInstaller1.ServiceName, Environment.MachineName))
            serviceController.Start();
    }
    

    Thanks to ScottTx and Francis B.

  • Matt Davis
    Matt Davis almost 15 years
    Note that this solution does not require the use of InstallUtil.exe, so you do not have to deliver it as part of your installation program.
  • Matt Davis
    Matt Davis almost 15 years
    This is a nice solution, but still requires the use of the InstallUtil utility. If you're already delivering InstallUtil as part of your installation, this makes the most sense. But, if you want to forego packaging InstallUtil, use the command-line solution.
  • Christian.K
    Christian.K over 14 years
    What's the point with the empty "catch { throw; }" clauses? Also, it is probably not a good idea to hide failures by "Rollback()" as that situation basically leaves the system in an undefined state I guess (you tryied to install a service, failed somewhere in the middle and couldn't undo it). You should at least "show" the user that there is something fishy - or does the Rollback() function write some messages to the console?
  • Matt Davis
    Matt Davis over 14 years
    The rollback does write data to the console. As for the empty catch blocks, it's a debugging thing. I can put a breakpoint at the throw statement to examine any exceptions that may occur.
  • Christian.K
    Christian.K over 14 years
    Ah, never thought of that. Thanks.
  • Steve Reed Sr
    Steve Reed Sr over 12 years
    VB.net code is not bad! For those of us who work in multiple languages it is nice to not have to convert the code from C!
  • avnic
    avnic over 12 years
    HI, works perfect how can i pass paramter on this mathod above to OnStart(string[] args)
  • Matt Davis
    Matt Davis over 12 years
  • wmarbut
    wmarbut over 11 years
    I get the nasty "Cannot install service from command line or debugger..." dialog using this method on a Win7 machine with .NET 4
  • Matt Davis
    Matt Davis over 11 years
    @wmarbut, make sure you run the command prompt as administrator on Win7.
  • wmarbut
    wmarbut over 11 years
    @MattDavis It was. I also tried my dev console which works with InstallUtil.
  • Yogesh
    Yogesh about 11 years
    I am getting error Error: The type or namespace name 'YourServiceType' could not be found (are you missing a using directive or an assembly reference?
  • tsemer
    tsemer almost 11 years
    Nice! IsInstalled can be upgraded to ServiceController.GetServices().Any(s => s.ServiceName == "YourServiceName");
  • bansi
    bansi over 10 years
    YourServiceType is the ProjectInstaller you added to the service which contains ServiceInstaller and ServiceProcessInstaller
  • programmer33
    programmer33 over 10 years
    If you have an issue of 'Sub Main is declared more than once' in VB.NET (VS 2008), simply delete the sub main method from SERVICENAME.Designer.vb in your projects folder.
  • giladrv
    giladrv over 9 years
    I need to pass a parameter from the Main function in the above code to the service code in your first link. How would I do that?
  • Matt Davis
    Matt Davis over 9 years
    @giladrv, you can modify the constructor for YourService to accept a parameter.
  • Matt Davis
    Matt Davis almost 9 years
  • Matt Davis
    Matt Davis almost 9 years
    @giorgi, Windows services are not started by double-clicking the executable. To start a Windows service, you can use the sc command from the command line or the Service console. Without the Main function, a C#-based Windows service won't even compile.
  • Ali Ahmadvand
    Ali Ahmadvand over 8 years
    thank you for use full replay; when i want to start my services with your solution. first asked me to set an user name& password on "set service login" form. and after set that. raise exception on last Catch in "InstallService" method. could you tell me how to solve this problem. thanks a lot
  • Rashmin Javiya
    Rashmin Javiya over 8 years
    Very nice. but would be good if there is UI installer available for non-tech people
  • Squirrelkiller
    Squirrelkiller about 8 years
    @Rashmin Javiya Why would any non-tech people ever manually install and even develop a windows service?
  • Rashmin Javiya
    Rashmin Javiya about 8 years
    @MarlonRegenhardt What I mean is, above solution required to pass command line argument -install and -uninstall for application to understand the behavior whether to install, uninstall or simply execute service. non-technical people might not aware of these command line args.
  • Matt Davis
    Matt Davis about 8 years
    @RashminJaviya, that is why you wrap the commands in a higher-level installer. In our case, we use the "-install" command line argument inside an InstallShield installer. When the user uninstalls from the Add/Remove Programs console, the "-uninstall" command line argument is used. The command line arguments are merely used to make the service scriptable without requiring a dependency on InstallUtil.exe. You could accomplish the same thing wrapping the calls in two .bat files, e.g., install.bat and uninstall.bat.
  • Faizan Mubasher
    Faizan Mubasher over 6 years
    @MattDavis My command line project and Service projects are separate projects. How I can add ProjectInstaller or YourServiceType in my Util class?
  • Charles Owen
    Charles Owen about 6 years
    Thanks, this helped me to figure out how to automatically start a service.
  • AlirezaK
    AlirezaK about 5 years
    @Yogesh, please refer to stackoverflow.com/a/21916030/4444757 for Your Service Type error.
  • AlirezaK
    AlirezaK about 5 years
    @MattDavis, Thanks a lot for the great post, I have a problem like Ali Ahmadvand (8 comments up) when I'd like to start my service with your solution. first asked me to set a user name & password on "set service login" form. and after the set that. raise an exception on last Catch in "InstallService" method. could you tell me how to solve this problem, please?
  • Matt Davis
    Matt Davis about 5 years
    @Tom, in the ServiceProcessInstaller component, there is an Account property. I'm assuming that this is set to User in your case, which is why it's prompting for credentials. I have mine set to LocalService, so I'm never prompted for credentials when installing my service. If you have to keep it as User, then you'll need to find the answer elsewhere as I'm not sure. You can read more about service account types here: docs.microsoft.com/en-us/windows/desktop/services/…
  • whale70
    whale70 over 4 years
    Anyone knows how it is possible to specify a service name for the new service to be created (other than just the name of the executable)?
  • Matt Davis
    Matt Davis over 4 years
    @whale70 See Step 4, Bullet 4 stackoverflow.com/questions/593454/…
  • whale70
    whale70 over 4 years
    @MattDavis, I'd like to user to be able to specify the desired service name when installing the service from the command line, so configuration this name at runtime isn't a viable option for this. E.g. like: 'myapp.exe -install <serviceName>'