68

Tengo 3 proyectos en mi solución de VS. Una de ellas es una aplicación web, la segunda es un servicio de Windows y la última es un proyecto de instalación para mi aplicación web.¿Cómo instalar un servicio de Windows mediante programación en C#?

Lo que quiero es al final de la instalación de la aplicación web en mi proyecto de configuración, dentro de mi acción personalizada para intentar instalar mi servicio de Windows dado que tengo la ubicación del ensamblado para entonces.

Respuesta

67

autorización, aquí es lo que realmente trabajó para mí, ha sido probado en varias máquinas con diferentes sistemas operativos (Vista, XP, Win2k, servidor Win2003)

El código ha sido tomado del here, por lo que el crédito completo se le otorga a quien haya escrito este código.

Una vez que se agrega el DLL o archivo de origen en su proyecto asegúrese de agregar el espacio de nombres ServiceTools y entonces usted tiene acceso a algunas funciones muy útiles como ...

//Installs and starts the service 
ServiceInstaller.InstallAndStart("MyServiceName", "MyServiceDisplayName", "C:\PathToServiceFile.exe"); 

//Removes the service 
ServiceInstaller.Uninstall("MyServiceName"); 

//Checks the status of the service 
ServiceInstaller.GetServiceStatus("MyServiceName"); 

//Starts the service 
ServiceInstaller.StartService("MyServiceName"); 

//Stops the service 
ServiceInstaller.StopService("MyServiceName"); 

//Check if service is installed 
ServiceInstaller.ServiceIsInstalled("MyServiceName"); 

espero que esto ayude.

+3

He publicado una respuesta que contiene algunas correcciones de errores al código que ha publicado. Eche un vistazo al hilo de discusión en el que encontró el código, y verá que el autor del código se da cuenta de que hay algunos errores en él. –

+0

La publicación a la que me refiero es: http://www.tech-archive.net/Archive/VB/microsoft.public.vb.winapi/2006-08/msg00253.html Además, encontré otro error o dos donde el identificador del servicio no fue lanzado. –

+6

Esa solución parece luchar contra una hidra de 25 cabezas cuando todo lo que quiere es un paseo tranquilo en el parque ... tiene que haber una manera más simple – Newtopian

28

Por favor, eche un vistazo a this article.


En ocasiones es posible que desee instalar un servicio de Windows mediante programación, pero la máquina de destino no tiene InstallUtil.exe.

agregar una referencia a System.Configuration.Install

usar el siguiente código.

Tenga en cuenta que el exeFileName es el InstallerClass .exe y no el ServiceClass .exe.

public static void InstallService(string exeFilename) 
{ 
    string[] commandLineOptions = new string[1] { "/LogFile=install.log" }; 

    System.Configuration.Install.AssemblyInstaller installer = new System.Configuration.Install.AssemblyInstaller(exeFilename, commandLineOptions); 

    installer.UseNewContext = true;  
    installer.Install(null);  
    installer.Commit(null); 

} 

Para desinstalar:

public static void UninstallService(string exeFilename) 
{ 
    string[] commandLineOptions = new string[1] { "/LogFile=uninstall.log" }; 

    System.Configuration.Install.AssemblyInstaller installer = new System.Configuration.Install.AssemblyInstaller(exeFilename, commandLineOptions); 

    installer.UseNewContext = true;  
    installer.Uninstall(null); 

} 
+0

he intentado ya, pero no funciona. Sin excepciones o lo que sea, simplemente no instala el servicio – Konstantinos

+0

Puede haber un problema de ruta completa o de credenciales> U puede intentar escribir un servicio simple. ¿También ha intentado utilizar la URL anterior donde SCM llama a las API apropiadas usando P/Invoke en C# – lakshmanaraj

+0

lo intentaré de la manera difícil y vendré con retroalimentación, thx – Konstantinos

65

Encontré varios errores en el código que reutilizó y los solucioné y también los limpié un poco. De nuevo, el código original está tomado de here.

public static class ServiceInstaller 
{ 
    private const int STANDARD_RIGHTS_REQUIRED = 0xF0000; 
    private const int SERVICE_WIN32_OWN_PROCESS = 0x00000010; 

    [StructLayout(LayoutKind.Sequential)] 
    private class SERVICE_STATUS 
    { 
     public int dwServiceType = 0; 
     public ServiceState dwCurrentState = 0; 
     public int dwControlsAccepted = 0; 
     public int dwWin32ExitCode = 0; 
     public int dwServiceSpecificExitCode = 0; 
     public int dwCheckPoint = 0; 
     public int dwWaitHint = 0; 
    } 

    #region OpenSCManager 
    [DllImport("advapi32.dll", EntryPoint = "OpenSCManagerW", ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)] 
    static extern IntPtr OpenSCManager(string machineName, string databaseName, ScmAccessRights dwDesiredAccess); 
    #endregion 

    #region OpenService 
    [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)] 
    static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, ServiceAccessRights dwDesiredAccess); 
    #endregion 

    #region CreateService 
    [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)] 
    private static extern IntPtr CreateService(IntPtr hSCManager, string lpServiceName, string lpDisplayName, ServiceAccessRights dwDesiredAccess, int dwServiceType, ServiceBootFlag dwStartType, ServiceError dwErrorControl, string lpBinaryPathName, string lpLoadOrderGroup, IntPtr lpdwTagId, string lpDependencies, string lp, string lpPassword); 
    #endregion 

    #region CloseServiceHandle 
    [DllImport("advapi32.dll", SetLastError = true)] 
    [return: MarshalAs(UnmanagedType.Bool)] 
    static extern bool CloseServiceHandle(IntPtr hSCObject); 
    #endregion 

    #region QueryServiceStatus 
    [DllImport("advapi32.dll")] 
    private static extern int QueryServiceStatus(IntPtr hService, SERVICE_STATUS lpServiceStatus); 
    #endregion 

    #region DeleteService 
    [DllImport("advapi32.dll", SetLastError = true)] 
    [return: MarshalAs(UnmanagedType.Bool)] 
    private static extern bool DeleteService(IntPtr hService); 
    #endregion 

    #region ControlService 
    [DllImport("advapi32.dll")] 
    private static extern int ControlService(IntPtr hService, ServiceControl dwControl, SERVICE_STATUS lpServiceStatus); 
    #endregion 

    #region StartService 
    [DllImport("advapi32.dll", SetLastError = true)] 
    private static extern int StartService(IntPtr hService, int dwNumServiceArgs, int lpServiceArgVectors); 
    #endregion 

    public static void Uninstall(string serviceName) 
    { 
     IntPtr scm = OpenSCManager(ScmAccessRights.AllAccess); 

     try 
     { 
      IntPtr service = OpenService(scm, serviceName, ServiceAccessRights.AllAccess); 
      if (service == IntPtr.Zero) 
       throw new ApplicationException("Service not installed."); 

      try 
      { 
       StopService(service); 
       if (!DeleteService(service)) 
        throw new ApplicationException("Could not delete service " + Marshal.GetLastWin32Error()); 
      } 
      finally 
      { 
       CloseServiceHandle(service); 
      } 
     } 
     finally 
     { 
      CloseServiceHandle(scm); 
     } 
    } 

    public static bool ServiceIsInstalled(string serviceName) 
    { 
     IntPtr scm = OpenSCManager(ScmAccessRights.Connect); 

     try 
     { 
      IntPtr service = OpenService(scm, serviceName, ServiceAccessRights.QueryStatus); 

      if (service == IntPtr.Zero) 
       return false; 

      CloseServiceHandle(service); 
      return true; 
     } 
     finally 
     { 
      CloseServiceHandle(scm); 
     } 
    } 

    public static void InstallAndStart(string serviceName, string displayName, string fileName) 
    { 
     IntPtr scm = OpenSCManager(ScmAccessRights.AllAccess); 

     try 
     { 
      IntPtr service = OpenService(scm, serviceName, ServiceAccessRights.AllAccess); 

      if (service == IntPtr.Zero) 
       service = CreateService(scm, serviceName, displayName, ServiceAccessRights.AllAccess, SERVICE_WIN32_OWN_PROCESS, ServiceBootFlag.AutoStart, ServiceError.Normal, fileName, null, IntPtr.Zero, null, null, null); 

      if (service == IntPtr.Zero) 
       throw new ApplicationException("Failed to install service."); 

      try 
      { 
       StartService(service); 
      } 
      finally 
      { 
       CloseServiceHandle(service); 
      } 
     } 
     finally 
     { 
      CloseServiceHandle(scm); 
     } 
    } 

    public static void StartService(string serviceName) 
    { 
     IntPtr scm = OpenSCManager(ScmAccessRights.Connect); 

     try 
     { 
      IntPtr service = OpenService(scm, serviceName, ServiceAccessRights.QueryStatus | ServiceAccessRights.Start); 
      if (service == IntPtr.Zero) 
       throw new ApplicationException("Could not open service."); 

      try 
      { 
       StartService(service); 
      } 
      finally 
      { 
       CloseServiceHandle(service); 
      } 
     } 
     finally 
     { 
      CloseServiceHandle(scm); 
     } 
    } 

    public static void StopService(string serviceName) 
    { 
     IntPtr scm = OpenSCManager(ScmAccessRights.Connect); 

     try 
     { 
      IntPtr service = OpenService(scm, serviceName, ServiceAccessRights.QueryStatus | ServiceAccessRights.Stop); 
      if (service == IntPtr.Zero) 
       throw new ApplicationException("Could not open service."); 

      try 
      { 
       StopService(service); 
      } 
      finally 
      { 
       CloseServiceHandle(service); 
      } 
     } 
     finally 
     { 
      CloseServiceHandle(scm); 
     } 
    } 

    private static void StartService(IntPtr service) 
    { 
     SERVICE_STATUS status = new SERVICE_STATUS(); 
     StartService(service, 0, 0); 
     var changedStatus = WaitForServiceStatus(service, ServiceState.StartPending, ServiceState.Running); 
     if (!changedStatus) 
      throw new ApplicationException("Unable to start service"); 
    } 

    private static void StopService(IntPtr service) 
    { 
     SERVICE_STATUS status = new SERVICE_STATUS(); 
     ControlService(service, ServiceControl.Stop, status); 
     var changedStatus = WaitForServiceStatus(service, ServiceState.StopPending, ServiceState.Stopped); 
     if (!changedStatus) 
      throw new ApplicationException("Unable to stop service"); 
    } 

    public static ServiceState GetServiceStatus(string serviceName) 
    { 
     IntPtr scm = OpenSCManager(ScmAccessRights.Connect); 

     try 
     { 
      IntPtr service = OpenService(scm, serviceName, ServiceAccessRights.QueryStatus); 
      if (service == IntPtr.Zero) 
       return ServiceState.NotFound; 

      try 
      { 
       return GetServiceStatus(service); 
      } 
      finally 
      { 
       CloseServiceHandle(service); 
      } 
     } 
     finally 
     { 
      CloseServiceHandle(scm); 
     } 
    } 

    private static ServiceState GetServiceStatus(IntPtr service) 
    { 
     SERVICE_STATUS status = new SERVICE_STATUS(); 

     if (QueryServiceStatus(service, status) == 0) 
      throw new ApplicationException("Failed to query service status."); 

     return status.dwCurrentState; 
    } 

    private static bool WaitForServiceStatus(IntPtr service, ServiceState waitStatus, ServiceState desiredStatus) 
    { 
     SERVICE_STATUS status = new SERVICE_STATUS(); 

     QueryServiceStatus(service, status); 
     if (status.dwCurrentState == desiredStatus) return true; 

     int dwStartTickCount = Environment.TickCount; 
     int dwOldCheckPoint = status.dwCheckPoint; 

     while (status.dwCurrentState == waitStatus) 
     { 
      // Do not wait longer than the wait hint. A good interval is 
      // one tenth the wait hint, but no less than 1 second and no 
      // more than 10 seconds. 

      int dwWaitTime = status.dwWaitHint/10; 

      if (dwWaitTime < 1000) dwWaitTime = 1000; 
      else if (dwWaitTime > 10000) dwWaitTime = 10000; 

      Thread.Sleep(dwWaitTime); 

      // Check the status again. 

      if (QueryServiceStatus(service, status) == 0) break; 

      if (status.dwCheckPoint > dwOldCheckPoint) 
      { 
       // The service is making progress. 
       dwStartTickCount = Environment.TickCount; 
       dwOldCheckPoint = status.dwCheckPoint; 
      } 
      else 
      { 
       if (Environment.TickCount - dwStartTickCount > status.dwWaitHint) 
       { 
        // No progress made within the wait hint 
        break; 
       } 
      } 
     } 
     return (status.dwCurrentState == desiredStatus); 
    } 

    private static IntPtr OpenSCManager(ScmAccessRights rights) 
    { 
     IntPtr scm = OpenSCManager(null, null, rights); 
     if (scm == IntPtr.Zero) 
      throw new ApplicationException("Could not connect to service control manager."); 

     return scm; 
    } 
} 


public enum ServiceState 
{ 
    Unknown = -1, // The state cannot be (has not been) retrieved. 
    NotFound = 0, // The service is not known on the host server. 
    Stopped = 1, 
    StartPending = 2, 
    StopPending = 3, 
    Running = 4, 
    ContinuePending = 5, 
    PausePending = 6, 
    Paused = 7 
} 

[Flags] 
public enum ScmAccessRights 
{ 
    Connect = 0x0001, 
    CreateService = 0x0002, 
    EnumerateService = 0x0004, 
    Lock = 0x0008, 
    QueryLockStatus = 0x0010, 
    ModifyBootConfig = 0x0020, 
    StandardRightsRequired = 0xF0000, 
    AllAccess = (StandardRightsRequired | Connect | CreateService | 
       EnumerateService | Lock | QueryLockStatus | ModifyBootConfig) 
} 

[Flags] 
public enum ServiceAccessRights 
{ 
    QueryConfig = 0x1, 
    ChangeConfig = 0x2, 
    QueryStatus = 0x4, 
    EnumerateDependants = 0x8, 
    Start = 0x10, 
    Stop = 0x20, 
    PauseContinue = 0x40, 
    Interrogate = 0x80, 
    UserDefinedControl = 0x100, 
    Delete = 0x00010000, 
    StandardRightsRequired = 0xF0000, 
    AllAccess = (StandardRightsRequired | QueryConfig | ChangeConfig | 
       QueryStatus | EnumerateDependants | Start | Stop | PauseContinue | 
       Interrogate | UserDefinedControl) 
} 

public enum ServiceBootFlag 
{ 
    Start = 0x00000000, 
    SystemStart = 0x00000001, 
    AutoStart = 0x00000002, 
    DemandStart = 0x00000003, 
    Disabled = 0x00000004 
} 

public enum ServiceControl 
{ 
    Stop = 0x00000001, 
    Pause = 0x00000002, 
    Continue = 0x00000003, 
    Interrogate = 0x00000004, 
    Shutdown = 0x00000005, 
    ParamChange = 0x00000006, 
    NetBindAdd = 0x00000007, 
    NetBindRemove = 0x00000008, 
    NetBindEnable = 0x00000009, 
    NetBindDisable = 0x0000000A 
} 

public enum ServiceError 
{ 
    Ignore = 0x00000000, 
    Normal = 0x00000001, 
    Severe = 0x00000002, 
    Critical = 0x00000003 
} 

Háganme saber si alguien encuentra algo erróneo con este código.

+0

gracias lars lo comprobaré una vez que tenga algo de tiempo libre :) – Konstantinos

+0

¿Alguien ha probado este código todavía? estoy pensando en usarlo. – rev

+10

Utilizándolo en producción –

4

Al utilizar el proyecto Topshelf puede instalar llamando a su ejecutable:

MyService.exe install 

Topshelf también se ocupa de otras tuberías de servicio de Windows.

11

Después de crear una instancia de una clase de instalador para mi servicio (muy básico) todo lo que tenía que hacer es llamar a:

ManagedInstallerClass.InstallHelper(new string[] { 
    Assembly.GetExecutingAssembly().Location }); 

para instalarlo y

ManagedInstallerClass.InstallHelper(new string[] { "/u", 
    Assembly.GetExecutingAssembly().Location }); 

para desinstalar el servicio . El código de llamada está, aquí, en el mismo ensamblaje que el ejecutable del servicio.

para instalar el servicio a través de la línea de comandos, todo lo que tuve que hacer es conectar esto al ejecutable mediante los argumentos de la línea de comandos y probar System.Environment.UserInteractive para saber si el servicio está ejecutándose o alguien intentando instalarlo; desinstálelo y voila ... no funky cosas de interoperabilidad ... sin punteros goteando ...

Total de alrededor de 20 líneas de código repartidas en dos clases hizo el truco.

para reemplazar InstallUtil sólo echar un vistazo a ManagedInstallerClass.InstallHelper

+0

Simple. Podría haber llegado a la misma conclusión al reflexionar en InstallUtil.exe. Hace exactamente lo mismo. Además, tiene algunas correcciones de codificación de consola interesantes. Instalar y desinstalar funciona bien. (Recuerde ejecutarlo como administrador ...) – ygoe

1

En este artículo he leído todos los mensajes y comentarios Bu todavía no sé ¿Cómo puedo configurar Tipo de cuenta y el método StartType cuando va a añadir el servicio de ventanas. Este ejemplo de código funciona bien a mi lado solo agrega un servicio propio del sistema local) Pero preparo el programa de instalación, debo pensar StartMode y Método de cuenta de usuario debido al sistema de clientes.
Hay todas las apariencias que ServiceBootFlag enumeración proporcionan StartType pero Tipo de cuenta sigue siendo un problema.

[DllImport("advapi32.dll", EntryPoint = "CreateServiceA")] 

    private static extern IntPtr CreateService(IntPtr hSCManager, string 

    lpServiceName, string lpDisplayName, ServiceRights dwDesiredAccess, int 

    dwServiceType, ServiceBootFlag dwStartType, ServiceError dwErrorControl, 

    string lpBinaryPathName, string lpLoadOrderGroup, IntPtr lpdwTagId, string 

    lpDependencies, string lp, string lpPassword); 
3

Como me enfrenté al desafío de instalar servicios programáticamente que se ejecutan bajo un determinado usuario. Extendí el método InstallAndStart para hacer uso de lp y lpPassword ...

No mucho, pero podría ayudar.

public static void InstallAndStart(
    string serviceName, 
    string displayName, 
    string fileName, 
    string username, 
    string password) 
{ 
    IntPtr scm = OpenSCManager(ScmAccessRights.AllAccess); 

    try 
    { 
     IntPtr service = OpenService(
      scm, 
      serviceName, 
      ServiceAccessRights.AllAccess); 

     if (service == IntPtr.Zero) 
      service = CreateService(
       scm, 
       serviceName, 
       displayName, 
       ServiceAccessRights.AllAccess, 
       SERVICE_WIN32_OWN_PROCESS, 
       ServiceBootFlag.AutoStart, 
       ServiceError.Normal, 
       fileName, 
       null, 
       IntPtr.Zero, 
       null, 
       username, 
       password); 

     if (service == IntPtr.Zero) 
      throw new ApplicationException("Failed to install service."); 

     try 
     { 
      StartService(service); 
     } 
     finally 
     { 
      CloseServiceHandle(service); 
     } 
    } 
    finally 
    { 
     CloseServiceHandle(scm); 
    } 
} 
0

uso por debajo de código para instalar el servicio de Windows con C#:

public void InstallWinService(string winServicePath) 
{ 
     try 
     { 
      ManagedInstallerClass.InstallHelper(new string[] { winServicePath}); 
     } 
     catch (Exception) 
     { 

      throw; 
     } 
} 
Cuestiones relacionadas