Automatically install and start service

All the help stuff I have read, mentions using the command line InstallUtil.exe to do a service installation, followed by a manual start. Is it possible to have the service exe itself do the install and start-up to make distribution and handling a bit easier for non-techies (after all, users are used to just double clicking an exe file to have an app run, and it would be good to have the same functionality with a service.)




Answer this question

Automatically install and start service

  • Codeme

    Hi Russ,

    You still have many options. I would like to point out the following.

    1) Windows applications, and command line applications are capable of accepting parameters. You are welcome to include my ServiceInstaller code to install and remove the service from a popup window. You may even include the popup window in your service. I must point out that it has long been a standard that service applications never interact with the desktop. They are typically executed under a user account other than the logged on user, one of many reasons why services do not interact with the desktop. They may however expose functionality to a desktop application though some kind of IPC mechanism, but that is a topic for another time.

    2) My sample code may be incorporated into a simple windows executable to install or remove the service.

    3) Your client should probably concentrate on his or her requirements and let you incorporate whatever .NET code is needed to achieve that result.

    4) All the installutil utility does is call the Install() method on the included install class anyway. If you would rather leave out the service description and merely invoke that method after loading the service assembly it will actually achieve the same result.

    Kind Regards, and good luck

    Craig


  • Sumit Kumar

    Hi Russ,

    A little code is required in order to do this. My suggestion is to change your project to a console application rather than a windows application. It will still be a service, and you will still be able to use installUtil to install the service. You will however also be capable of running the service from the command line.

    You would then call the following code, based on a command line parameter, to install or remove the service. I typically use -install and -remove.

    You are welcome to use the following code, which was written for .net 1.1. It will also work for .net 2.0. I have used the API in order to provide a service description, something that is left out of the default dotnet service infrastructure.

    Another clever trick you can try, is if the application is executed from the console, check the service manager to see if the application is already running as a service, if not, then run it in the console. This is very useful for debugging. You are able to step through the application instead of having to use attach process.

    A simple check could be done as follows

    ServiceController sc = new ServiceController("Service Name");

    switch (sc.Status) {

    case ServiceControllerStatus.StartPending: {

    System.ServiceProcess.ServiceBase[] ServicesToRun;

    ServicesToRun = new System.ServiceProcess.ServiceBase[] { new JSE.CBS.Service.CBSService() };

    System.ServiceProcess.ServiceBase.Run(ServicesToRun);

    }

    break;

    case ServiceControllerStatus.Stopped: { // the service is not running.

    JSE.CBS.Service.CBSService service = new JSE.CBS.Service.CBSService();

    service.OnStart(null);

    Console.WriteLine("Service Started...");

    Console.ReadLine();

    service.OnStop();

    }

    break;

    default: // the service is paused or waiting or running or something.

    Debug.WriteLine("The service is currently running, a debug instance cannot be started");

    Console.ReadLine();

    break;

    }

    Here is the code to install or remove the service.

    /// <summary>

    /// Utility class to install or uninstall a service from the service

    /// control manager.

    /// </summary>

    class ServiceInstaller {

    #region DLL Library Imports

    /// <summary>

    /// Import functions from the service control manager API.

    /// </summary>

    [StructLayout(LayoutKind.Sequential)]

    public struct SERVICE_DESCRIPTION {

    public string lpDescription;

    }

    [DllImport("advapi32.dll", SetLastError=true, EntryPoint="ChangeServiceConfig2")] public static extern int ChangeServiceDescription( IntPtr hService, int dwInfoLevel, [ MarshalAs( UnmanagedType.Struct ) ] ref SERVICE_DESCRIPTION lpInfo );

    [DllImport("advapi32.dll", SetLastError=true)] public static extern IntPtr OpenSCManager(string lpMachineName,string lpSCDB, int scParameter);

    [DllImport("Advapi32.dll", SetLastError=true)] public static extern IntPtr CreateService(IntPtr SC_HANDLE, string lpSvcName, string lpDisplayName, int dwDesiredAccess, int dwServiceType, int dwStartType, int dwErrorControl, string lpPathName, string lpLoadOrderGroup, int lpdwTagId, string lpDependencies, string lpServiceStartName, string lpPassword);

    [DllImport("advapi32.dll", SetLastError=true)] public static extern void CloseServiceHandle(IntPtr SCHANDLE);

    [DllImport("advapi32.dll", SetLastError=true)] public static extern int StartService(IntPtr SVHANDLE,int dwNumServiceArgs,string lpServiceArgVectors);

    [DllImport("advapi32.dll", SetLastError=true)] public static extern IntPtr OpenService(IntPtr SCHANDLE,string lpSvcName,int dwNumServiceArgs);

    [DllImport("advapi32.dll", SetLastError=true)] public static extern int DeleteService(IntPtr SVHANDLE);

    #endregion DLL Library Imports

    #region Constant declarations required by the service control API.

    /// <summary>

    /// constants required by the service control library.

    /// </summary>

    private const int SERVICE_WIN32_OWN_PROCESS = 0x00000010;

    private const int SERVICE_DEMAND_START = 0x00000003;

    private const int SERVICE_ERROR_NORMAL = 0x00000001;

    private const int STANDARD_RIGHTS_REQUIRED = 0xF0000;

    private const int SERVICE_QUERY_CONFIG = 0x0001;

    private const int SERVICE_CHANGE_CONFIG = 0x0002;

    private const int SERVICE_QUERY_STATUS = 0x0004;

    private const int SERVICE_ENUMERATE_DEPENDENTS = 0x0008;

    private const int SERVICE_START =0x0010;

    private const int SERVICE_STOP =0x0020;

    private const int SERVICE_PAUSE_CONTINUE =0x0040;

    private const int SERVICE_INTERROGATE =0x0080;

    private const int SERVICE_USER_DEFINED_CONTROL =0x0100;

    private const int SERVICE_DELETE = 0x10000;

    private const int SC_MANAGER_CONNECT = 0x0001;

    private const int SC_MANAGER_CREATE_SERVICE = 0x0002;

    private const int SC_MANAGER_ENUMERATE_SERVICE = 0x0004;

    private const int SC_MANAGER_LOCK = 0x0008;

    private const int SC_MANAGER_QUERY_LOCK_STATUS = 0x0010;

    private const int SC_MANAGER_MODIFY_BOOT_CONFIG = 0x0020;

    private const int SERVICE_CONFIG_DESCRIPTION = 0x0001;

    private const int SC_MANAGER_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED |

    SC_MANAGER_CONNECT |

    SC_MANAGER_CREATE_SERVICE |

    SC_MANAGER_ENUMERATE_SERVICE |

    SC_MANAGER_LOCK |

    SC_MANAGER_QUERY_LOCK_STATUS |

    SC_MANAGER_MODIFY_BOOT_CONFIG);

    private const int SERVICE_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED |

    SERVICE_QUERY_CONFIG |

    SERVICE_CHANGE_CONFIG |

    SERVICE_QUERY_STATUS |

    SERVICE_ENUMERATE_DEPENDENTS |

    SERVICE_START |

    SERVICE_STOP |

    SERVICE_PAUSE_CONTINUE |

    SERVICE_INTERROGATE |

    SERVICE_USER_DEFINED_CONTROL);

    private const int SERVICE_AUTO_START = 0x00000002;

    #endregion

    /// <summary>

    /// Private constructor ensures that the utility class is used statically.

    /// </summary>

    private ServiceInstaller() {

    }

    /// <summary>

    /// This method installs and runs the service in the service control manager.

    /// </summary>

    /// <param name="svcPath">The complete path of the service.</param>

    /// <param name="svcName">Name of the service.</param>

    /// <param name="svcDispName">Display name of the service.</param>

    public static void InstallService(string svcPath, string svcName, string svcDispName, string svcUserName, string svcPassword, bool start) {

    IntPtr sc_handle = IntPtr.Zero;

    try {

    sc_handle = OpenSCManager(null,

    null,

    SC_MANAGER_CREATE_SERVICE);

    if (sc_handle.ToInt32() != 0) {

    IntPtr sv_handle = CreateService(sc_handle,

    svcName,

    svcDispName,

    SERVICE_ALL_ACCESS,

    SERVICE_WIN32_OWN_PROCESS,

    SERVICE_AUTO_START,

    SERVICE_ERROR_NORMAL,

    svcPath,

    null, 0, null, svcUserName, svcPassword);

    if(sv_handle.ToInt32() == 0)

    throw new APIException();

    SERVICE_DESCRIPTION description;

    description.lpDescription = DataService.DESCRIPTION;

    int hResult = ChangeServiceDescription(sv_handle, SERVICE_CONFIG_DESCRIPTION, ref description);

    if (start)

    hResult = StartService(sv_handle,0,null);

    if(hResult == 0)

    throw new APIException();

    }

    else

    throw new APIException();

    }

    finally {

    if (sc_handle != IntPtr.Zero)

    CloseServiceHandle(sc_handle);

    }

    }

    /// <summary>

    /// This method uninstalls the service from the service conrol manager.

    /// </summary>

    /// <param name="svcName">Name of the service to uninstall.</param>

    public static void UnInstallService(string svcName) {

    IntPtr sc_hndl = OpenSCManager(null, null, SC_MANAGER_ALL_ACCESS);

    if(sc_hndl.ToInt32() == 0)

    throw new APIException();

    IntPtr svc_hndl = OpenService(sc_hndl,

    svcName,

    SERVICE_DELETE);

    if(svc_hndl.ToInt32() == 0)

    throw new APIException();

    int result = DeleteService(svc_hndl);

    if (result == 0)

    throw new APIException();

    CloseServiceHandle(sc_hndl);

    }

    }


  • Frank2808

    Russ,

    You may certainly do that.

    Your other option, which I mentioned - but have never tried, is to load your very own service assembly from your very own executable, and try and call the Install() method on the ServiceInstaller derived class.

    My wrox bible indicates that installutil merely does that anyway.

    /it


  • Wicket

    Hi Craig

    Are you suggesting here that I could utilise your install function from within my service exe so that it would self install If so, that is just what I am looking for! Unfortunately I do not even know what a Service Assembly is, so it will be difficult without a bit more advice from you please.

    I have 2 classes in my service exe (which has the sole task of instantiating the MainFunctions class, which then carries out all the funtionality of the solution.) The code for these two classes is outlined below (just the relevant parts). When I use InstallUtil it certainly installs a service with the right name, but as yet this service does not actually seem to be working (the logging from the static Logging class, for example, does not happen) Perhaps you could give me a hint as to what I may be doing wrong here, and also indicate how and where I would call your install function from within the code here, assuming I had your code as a 3rd class.

    public class CPServerService : ServiceBase

    private System.ComponentModel.Container components = null;

    private MainFunctions library;

    public CPServerService()

    {

    InitializeComponent();

    Logging.WriteToLog(1,1,"C&P Service Start-up");

    library = new MainFunctions();

    }

    static void Main()

    {

    System.ServiceProcess.ServiceBase[] ServicesToRun;

    ServicesToRun = new System.ServiceProcess.ServiceBase[] { new CPServerService() };

    System.ServiceProcess.ServiceBase.Run(ServicesToRun);

    }

    private void InitializeComponent()

    {

    components = new System.ComponentModel.Container();

    this.ServiceName = "CPServerService";

    }

    (This below came straight out of a book on C# and I have only just started "playing" with it)

    [RunInstaller(true)]

    public partial class CPInstaller : Installer

    {

    ServiceProcessInstaller m_ServiceProcessInstaller;

    ServiceInstaller m_ServiceInstaller;

    public CPInstaller()

    {

    InitializeComponent();

    }

    void InitializeComponent()

    {

    m_ServiceProcessInstaller = new ServiceProcessInstaller();

    m_ServiceInstaller = new ServiceInstaller();

    m_ServiceProcessInstaller.Account = ServiceAccount.LocalSystem;

    m_ServiceProcessInstaller.Password = null;

    m_ServiceProcessInstaller.Username = null;

    m_ServiceInstaller.DisplayName = "CPServerService";

    m_ServiceInstaller.ServiceName = "CPServerService";

    m_ServiceInstaller.StartType = ServiceStartMode.Automatic;

    Installer[] installers = { m_ServiceProcessInstaller, m_ServiceInstaller };

    Installers.AddRange(installers);

    }

    }



  • SAADEDIN

    Hi Craig

    For some reason I am not being alerted when you reply.

    However I am interested in just using the install if I can. I shall have a look at your code and see if I can make sense of it. If not you may find me coming back with a few more questions! I will probably try the windows exe approach if I can. Do I assume that if I put Install and uninstall buttons on a form, I can simply put your install and uninstall functions in the button click handlers



  • sailajam

    If you are making an msi installation package you will have a project installer class based on System.Configuration.Install. Installer. Override the OnAfterInstall method to add the start command as follows:

    protected override void OnAfterInstall(System.Collections.IDictionary savedState)

    {

    base.OnAfterInstall(savedState);

    System.ServiceProcess.ServiceController serviceController

    = new System.ServiceProcess.ServiceController(this.serviceInstaller.ServiceName);

    serviceController.Start();

    }

    Now when the installation is complete the service will be started.



  • davser

    Hi Craig

    Thanks for all this, but if at all possible I would like to steer clear of consol functions, as my client has specified a windows based service code. In fact I get over the debugging problem by putting all the functional code in classes within a library. I then call this library either from a very simple hidden form which simply calls the "main" class in the library, or a very simple service which does exactly the same. Both the calling mechanisms are only a few lines of code long, so I can debug the library functionality using the form based application and be confident that the same behaviour will be exhibited when it is called by the service! Very useful.

    I have no problem in getting the service installed using installUtil.exe functionality provided by vs2005's SDK command window, but it would be very good if I could just have clicked on the server exe to get things installed and running. However, maybe this is not on! Perhaps an easy alternative would be to have a simple windows exe which called the installUtil.exe, (after maybe checking if it were installed already, although I don't quite see how) to install and start the service, and then die.

    On the other hand, maybe I'm just trying to be too clever. This will only be installed a very few times in its life, so maybe I should just leave the client to install it manually using InstallUtil.exe (not so satisfying though!)

    Russ



  • Automatically install and start service