88

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.

Jader Dias
  • 81,082
  • 147
  • 410
  • 611

8 Answers8

183

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.

Community
  • 1
  • 1
Matt Davis
  • 43,149
  • 15
  • 89
  • 118
  • 13
    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 Jul 28 '09 at 17:48
  • 3
    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? – Christian.K Sep 29 '09 at 05:17
  • 5
    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. – Matt Davis Sep 29 '09 at 12:30
  • Ah, never thought of that. Thanks. – Christian.K Sep 30 '09 at 15:00
  • HI, works perfect how can i pass paramter on this mathod above to OnStart(string[] args) – avnic Dec 19 '11 at 16:02
  • http://msdn.microsoft.com/en-us/library/system.serviceprocess.servicebase.onstart.aspx – Matt Davis Dec 19 '11 at 21:24
  • I get the nasty "Cannot install service from command line or debugger..." dialog using this method on a Win7 machine with .NET 4 – wmarbut Dec 08 '12 at 01:40
  • @wmarbut, make sure you run the command prompt as administrator on Win7. – Matt Davis Dec 08 '12 at 02:18
  • @MattDavis It was. I also tried my dev console which works with InstallUtil. – wmarbut Dec 08 '12 at 19:30
  • 4
    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? – Yogesh Apr 23 '13 at 16:13
  • Nice! IsInstalled can be upgraded to `ServiceController.GetServices().Any(s => s.ServiceName == "YourServiceName");` – tsemer Aug 06 '13 at 12:42
  • 5
    `YourServiceType` is the `ProjectInstaller` you added to the service which contains `ServiceInstaller` and `ServiceProcessInstaller` – bansi Oct 01 '13 at 11:55
  • 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. – Richard Lee Nov 16 '13 at 00:32
  • 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? – giladrv Dec 07 '14 at 15:30
  • @giladrv, you can modify the constructor for `YourService` to accept a parameter. – Matt Davis Dec 08 '14 at 02:58
  • @giorgi http://stackoverflow.com/questions/4350282/c-sharp-windows-service-main-method – Matt Davis Jun 11 '15 at 16:27
  • @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. – Matt Davis Jun 11 '15 at 16:49
  • 1
    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 – Ali Ahmadvand Nov 11 '15 at 07:52
  • Very nice. but would be good if there is UI installer available for non-tech people – Rashmin Javiya Jan 24 '16 at 07:34
  • 1
    @Rashmin Javiya Why would any non-tech people ever manually install and even develop a windows service? – Squirrelkiller Mar 31 '16 at 07:51
  • @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. – Rashmin Javiya Mar 31 '16 at 10:08
  • @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. – Matt Davis Mar 31 '16 at 14:38
  • @MattDavis My command line project and Service projects are separate projects. How I can add `ProjectInstaller` or `YourServiceType` in my Util class? – Faizan Mubasher Sep 14 '17 at 06:38
  • @Yogesh, please refer to https://stackoverflow.com/a/21916030/4444757 for `Your Service Type` error. – Tom Mar 06 '19 at 11:19
  • @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? – Tom Mar 06 '19 at 11:25
  • 1
    @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: https://docs.microsoft.com/en-us/windows/desktop/services/service-user-accounts – Matt Davis Mar 06 '19 at 16:32
  • 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)? – whale70 Nov 22 '19 at 06:15
  • @whale70 See Step 4, Bullet 4 https://stackoverflow.com/questions/593454/easiest-language-for-creating-a-windows-service/593803#593803 – Matt Davis Nov 22 '19 at 08:52
  • @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 ' – whale70 Nov 23 '19 at 10:01
21

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

ScottTx
  • 1,473
  • 8
  • 12
  • 3
    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. – Matt Davis Jul 28 '09 at 17:57
6

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>
Francis B.
  • 6,596
  • 2
  • 30
  • 54
5

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.

Otávio Décio
  • 70,467
  • 17
  • 156
  • 220
4

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

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

Matt
  • 24,106
  • 61
  • 180
  • 291
4

Use the .NET ServiceController class to start it, or issue the commandline command to start it --- "net start servicename". Either way works.

ScottTx
  • 1,473
  • 8
  • 12
4

To add to ScottTx's answer, here's the actual code to start the service if you're doing it the Microsoft way (ie. using a Setup project etc...)

(excuse the VB.net code, but this is what I'm stuck with)

Private Sub ServiceInstaller1_AfterInstall(ByVal sender As System.Object, ByVal e As System.Configuration.Install.InstallEventArgs) Handles ServiceInstaller1.AfterInstall
    Dim sc As New ServiceController()
    sc.ServiceName = ServiceInstaller1.ServiceName

    If sc.Status = ServiceControllerStatus.Stopped Then
        Try
            ' Start the service, and wait until its status is "Running".
            sc.Start()
            sc.WaitForStatus(ServiceControllerStatus.Running)

            ' TODO: log status of service here: sc.Status
        Catch ex As Exception
            ' TODO: log an error here: "Could not start service: ex.Message"
            Throw
        End Try
    End If
End Sub

To create the above event handler, go to the ProjectInstaller designer where the 2 controlls are. Click on the ServiceInstaller1 control. Go to the properties window under events and there you'll find the AfterInstall event.

Note: Don't put the above code under the AfterInstall event for ServiceProcessInstaller1. It won't work, coming from experience. :)

goku_da_master
  • 3,857
  • 1
  • 34
  • 38
0

The easiest solution is found here install-windows-service-without-installutil-exe by @Hoàng Long

@echo OFF
echo Stopping old service version...
net stop "[YOUR SERVICE NAME]"
echo Uninstalling old service version...
sc delete "[YOUR SERVICE NAME]"

echo Installing service...
rem DO NOT remove the space after "binpath="!
sc create "[YOUR SERVICE NAME]" binpath= "[PATH_TO_YOUR_SERVICE_EXE]" start= auto
echo Starting server complete
pause
Community
  • 1
  • 1
Robert Green MBA
  • 1,311
  • 17
  • 34