visual una studio servicio instalador hora example ejecutar determinada crear como c# .net windows-services

c# - una - ¿Me estoy ejecutando como un servicio?



ejecutar.exe como servicio de windows (12)

Al igual que Ash, escribo todo el código de procesamiento real en un conjunto de biblioteca de clases separado, que luego fue referenciado por el ejecutable del servicio de Windows, así como una aplicación de consola.

Sin embargo, hay ocasiones en las que es útil saber si la biblioteca de clases se está ejecutando en el contexto del ejecutable del servicio o la aplicación de la consola. La forma en que hago esto es reflexionar sobre la clase base de la aplicación de alojamiento. (Lo siento por el VB, pero me imagino que lo siguiente podría ser c # -ificado con bastante facilidad):

Public Class ExecutionContext '''''' <summary> '''''' Gets a value indicating whether the application is a windows service. '''''' </summary> '''''' <value> '''''' <c>true</c> if this instance is service; otherwise, <c>false</c>. '''''' </value> Public Shared ReadOnly Property IsService() As Boolean Get '' Determining whether or not the host application is a service is '' an expensive operation (it uses reflection), so we cache the '' result of the first call to this method so that we don''t have to '' recalculate it every call. '' If we have not already determined whether or not the application '' is running as a service... If IsNothing(_isService) Then '' Get details of the host assembly. Dim entryAssembly As Reflection.Assembly = Reflection.Assembly.GetEntryAssembly '' Get the method that was called to enter the host assembly. Dim entryPoint As System.Reflection.MethodInfo = entryAssembly.EntryPoint '' If the base type of the host assembly inherits from the '' "ServiceBase" class, it must be a windows service. We store '' the result ready for the next caller of this method. _isService = (entryPoint.ReflectedType.BaseType.FullName = "System.ServiceProcess.ServiceBase") End If '' Return the cached result. Return CBool(_isService) End Get End Property Private Shared _isService As Nullable(Of Boolean) = Nothing #End Region End Class

Actualmente estoy escribiendo un pequeño código de arranque para un servicio que se puede ejecutar en la consola. Básicamente se reduce a llamar al método OnStart () en lugar de usar el ServiceBase para iniciar y detener el servicio (porque no ejecuta la aplicación si no está instalada como un servicio y hace que la depuración sea una pesadilla).

En este momento estoy usando Debugger.IsAttached para determinar si debo usar ServiceBase.Run o [service] .OnStart, pero sé que no es la mejor idea porque algunas veces los usuarios finales quieren ejecutar el servicio en una consola (para ver la salida etc. en tiempo real).

¿Alguna idea sobre cómo podría determinar si el controlador de servicio de Windows comenzó a ''mí'', o si el usuario comenzó ''yo'' en la consola? Aparentemente Environment.IsUserInteractive no es la respuesta. Pensé en usar argumentos de línea de comando, pero parece "sucio".

Siempre pude ver una declaración try-catch en ServiceBase.Run, pero parece sucio. Editar: prueba de captura no funciona.

Tengo una solución: ponerlo aquí para todos los otros apiladores interesados:

public void Run() { if (Debugger.IsAttached || Environment.GetCommandLineArgs().Contains<string>("-console")) { RunAllServices(); } else { try { string temp = Console.Title; ServiceBase.Run((ServiceBase[])ComponentsToRun); } catch { RunAllServices(); } } } // void Run private void RunAllServices() { foreach (ConsoleService component in ComponentsToRun) { component.Start(); } WaitForCTRLC(); foreach (ConsoleService component in ComponentsToRun) { component.Stop(); } }

EDITAR: Hubo otra pregunta en StackOverflow donde el tipo tenía problemas con Environment.CurrentDirectory siendo "C: / Windows / System32" parece que esa podría ser la respuesta. Voy a probar hoy.


Aquí hay una traducción de la respuesta de chksr a .NET y evitar el error que no reconoce los servicios interactivos:

using System.Security.Principal; var wi = WindowsIdentity.GetCurrent(); var wp = new WindowsPrincipal(wi); var serviceSid = new SecurityIdentifier(WellKnownSidType.ServiceSid, null); var localSystemSid = new SecurityIdentifier(WellKnownSidType.LocalSystemSid, null); var interactiveSid = new SecurityIdentifier(WellKnownSidType.InteractiveSid, null); // maybe check LocalServiceSid, and NetworkServiceSid also bool isServiceRunningAsUser = wp.IsInRole(serviceSid); bool isSystem = wp.IsInRole(localSystemSid); bool isInteractive = wp.IsInRole(interactiveSid); bool isAnyService = isServiceRunningAsUser || isSystem || !isInteractive;


Bueno, hay un código muy antiguo (unos 20 años más o menos, no de mí, pero se encuentra en la web salvaje, salvaje, y en C no C #) que debería darte una idea de cómo hacer el trabajo:

enum enEnvironmentType { ENVTYPE_UNKNOWN, ENVTYPE_STANDARD, ENVTYPE_SERVICE_WITH_INTERACTION, ENVTYPE_SERVICE_WITHOUT_INTERACTION, ENVTYPE_IIS_ASP, }; enEnvironmentType GetEnvironmentType(void) { HANDLE hProcessToken = NULL; DWORD groupLength = 300; PTOKEN_GROUPS groupInfo = NULL; SID_IDENTIFIER_AUTHORITY siaNt = SECURITY_NT_AUTHORITY; PSID pInteractiveSid = NULL; PSID pServiceSid = NULL; DWORD dwRet = NO_ERROR; DWORD ndx; BOOL m_isInteractive = FALSE; BOOL m_isService = FALSE; // open the token if (!::OpenProcessToken(::GetCurrentProcess(),TOKEN_QUERY,&hProcessToken)) { dwRet = ::GetLastError(); goto closedown; } // allocate a buffer of default size groupInfo = (PTOKEN_GROUPS)::LocalAlloc(0, groupLength); if (groupInfo == NULL) { dwRet = ::GetLastError(); goto closedown; } // try to get the info if (!::GetTokenInformation(hProcessToken, TokenGroups, groupInfo, groupLength, &groupLength)) { // if buffer was too small, allocate to proper size, otherwise error if (::GetLastError() != ERROR_INSUFFICIENT_BUFFER) { dwRet = ::GetLastError(); goto closedown; } ::LocalFree(groupInfo); groupInfo = (PTOKEN_GROUPS)::LocalAlloc(0, groupLength); if (groupInfo == NULL) { dwRet = ::GetLastError(); goto closedown; } if (!GetTokenInformation(hProcessToken, TokenGroups, groupInfo, groupLength, &groupLength)) { dwRet = ::GetLastError(); goto closedown; } } // // We now know the groups associated with this token. We want // to look to see if the interactive group is active in the // token, and if so, we know that this is an interactive process. // // We also look for the "service" SID, and if it''s present, // we know we''re a service. // // The service SID will be present iff the service is running in a // user account (and was invoked by the service controller). // // create comparison sids if (!AllocateAndInitializeSid(&siaNt, 1, SECURITY_INTERACTIVE_RID, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid)) { dwRet = ::GetLastError(); goto closedown; } if (!AllocateAndInitializeSid(&siaNt, 1, SECURITY_SERVICE_RID, 0, 0, 0, 0, 0, 0, 0, &pServiceSid)) { dwRet = ::GetLastError(); goto closedown; } // try to match sids for (ndx = 0; ndx < groupInfo->GroupCount ; ndx += 1) { SID_AND_ATTRIBUTES sanda = groupInfo->Groups[ndx]; PSID pSid = sanda.Sid; // // Check to see if the group we''re looking at is one of // the two groups we''re interested in. // if (::EqualSid(pSid, pInteractiveSid)) { // // This process has the Interactive SID in its // token. This means that the process is running as // a console process // m_isInteractive = TRUE; m_isService = FALSE; break; } else if (::EqualSid(pSid, pServiceSid)) { // // This process has the Service SID in its // token. This means that the process is running as // a service running in a user account ( not local system ). // m_isService = TRUE; m_isInteractive = FALSE; break; } } if ( !( m_isService || m_isInteractive ) ) { // // Neither Interactive or Service was present in the current // users token, This implies that the process is running as // a service, most likely running as LocalSystem. // m_isService = TRUE; } closedown: if ( pServiceSid ) ::FreeSid( pServiceSid ); if ( pInteractiveSid ) ::FreeSid( pInteractiveSid ); if ( groupInfo ) ::LocalFree( groupInfo ); if ( hProcessToken ) ::CloseHandle( hProcessToken ); if (dwRet == NO_ERROR) { if (m_isService) return(m_isInteractive ? ENVTYPE_SERVICE_WITH_INTERACTION : ENVTYPE_SERVICE_WITHOUT_INTERACTION); return(ENVTYPE_STANDARD); } else return(ENVTYPE_UNKNOWN); }


Este hilo es muy viejo, pero pensé que arrojaría mi solución por ahí. Simplemente, para manejar este tipo de situación, construí un "arnés de servicio" que se usa tanto en la consola como en los casos de servicio de Windows. Como se mencionó anteriormente, la mayor parte de la lógica está contenida en una biblioteca separada, pero esto es más para probar y "enlazar".

El código adjunto de ninguna manera representa la mejor manera posible de resolver esto, solo mi propio enfoque. Aquí, la aplicación de consola llama al arnés de servicio cuando se encuentra en "modo consola" y por la lógica de "inicio de servicio" de la misma aplicación cuando se ejecuta como un servicio. Al hacerlo de esta manera, ahora puede llamar

ServiceHost.Instance.RunningAsAService (Boolean)

desde cualquier lugar de su código para verificar si la aplicación se está ejecutando como un servicio o simplemente como una consola.

Aquí está el código:

public class ServiceHost { private static Logger log = LogManager.GetLogger(typeof(ServiceHost).Name); private static ServiceHost mInstance = null; private static object mSyncRoot = new object(); #region Singleton and Static Properties public static ServiceHost Instance { get { if (mInstance == null) { lock (mSyncRoot) { if (mInstance == null) { mInstance = new ServiceHost(); } } } return (mInstance); } } public static Logger Log { get { return log; } } public static void Close() { lock (mSyncRoot) { if (mInstance.mEngine != null) mInstance.mEngine.Dispose(); } } #endregion private ReconciliationEngine mEngine; private ServiceBase windowsServiceHost; private UnhandledExceptionEventHandler threadExceptionHanlder = new UnhandledExceptionEventHandler(ThreadExceptionHandler); public bool HostHealthy { get; private set; } public bool RunningAsService {get; private set;} private ServiceHost() { HostHealthy = false; RunningAsService = false; AppDomain.CurrentDomain.UnhandledException += threadExceptionHandler; try { mEngine = new ReconciliationEngine(); HostHealthy = true; } catch (Exception ex) { log.FatalException("Could not initialize components.", ex); } } public void StartService() { if (!HostHealthy) throw new ApplicationException("Did not initialize components."); try { mEngine.Start(); } catch (Exception ex) { log.FatalException("Could not start service components.", ex); HostHealthy = false; } } public void StartService(ServiceBase serviceHost) { if (!HostHealthy) throw new ApplicationException("Did not initialize components."); if (serviceHost == null) throw new ArgumentNullException("serviceHost"); windowsServiceHost = serviceHost; RunningAsService = true; try { mEngine.Start(); } catch (Exception ex) { log.FatalException("Could not start service components.", ex); HostHealthy = false; } } public void RestartService() { if (!HostHealthy) throw new ApplicationException("Did not initialize components."); try { log.Info("Stopping service components..."); mEngine.Stop(); mEngine.Dispose(); log.Info("Starting service components..."); mEngine = new ReconciliationEngine(); mEngine.Start(); } catch (Exception ex) { log.FatalException("Could not restart components.", ex); HostHealthy = false; } } public void StopService() { try { if (mEngine != null) mEngine.Stop(); } catch (Exception ex) { log.FatalException("Error stopping components.", ex); HostHealthy = false; } finally { if (windowsServiceHost != null) windowsServiceHost.Stop(); if (RunningAsService) { AppDomain.CurrentDomain.UnhandledException -= threadExceptionHanlder; } } } private void HandleExceptionBasedOnExecution(object ex) { if (RunningAsService) { windowsServiceHost.Stop(); } else { throw (Exception)ex; } } protected static void ThreadExceptionHandler(object sender, UnhandledExceptionEventArgs e) { log.FatalException("Unexpected error occurred. System is shutting down.", (Exception)e.ExceptionObject); ServiceHost.Instance.HandleExceptionBasedOnExecution((Exception)e.ExceptionObject); } }

Todo lo que necesita hacer aquí es reemplazar esa referencia de ReconcilationEngine aspecto ominoso con cualquier método que aumente su lógica. Luego, en su aplicación, use los métodos ServiceHost.Instance.Start() y ServiceHost.Instance.Stop() , ya sea que esté ejecutándose en modo consola o como servicio.


Esto es un poco de autoencadenamiento, pero tengo una pequeña aplicación que cargará sus tipos de servicio en su aplicación a través de la reflexión y los ejecutará de esa manera. Incluí el código fuente, por lo que podría cambiarlo ligeramente para mostrar la salida estándar.

No se necesitan cambios de código para usar esta solución. También tengo un tipo de solución Debugger.IsAttached que es lo suficientemente genérica para ser utilizada con cualquier servicio. El enlace está en este artículo: .NET Windows Service Runner


Jonathan, no es exactamente una respuesta a tu pregunta, pero acabo de escribir un servicio de Windows y también noté la dificultad de depurar y probar.

Lo resolvió simplemente escribiendo todo el código de procesamiento real en un conjunto de biblioteca de clases separado, que luego fue referenciado por el ejecutable del servicio de Windows, así como una aplicación de consola y un arnés de prueba.

Además de la lógica básica del temporizador, todos los procesos más complejos ocurrieron en el ensamblaje común y pudieron ser probados / ejecutados bajo demanda de manera increíblemente fácil.


La única forma que he encontrado para lograr esto, es verificar si una consola está conectada al proceso en primer lugar, accediendo a cualquier propiedad del objeto de la Consola (por ejemplo, Título) dentro de un bloque try / catch.

Si el SCM inicia el servicio, no hay consola, y al acceder a la propiedad lanzará un System.IO.IOError.

Sin embargo, dado que esto se parece demasiado a depender de un detalle específico de la implementación (¿qué sucede si el SCM en algunas plataformas o algún día decide proporcionar una consola a los procesos que inicia?), Siempre uso un interruptor de línea de comando (-console ) en aplicaciones de producción ...


Lo que funciona para mí:

  • La clase que realiza el trabajo de servicio real se ejecuta en un hilo separado.
  • Este hilo se inicia desde el método OnStart () y se detiene desde OnStop ().
  • La decisión entre el servicio y el modo de consola depende de Environment.UserInteractive

Código de muestra:

class MyService : ServiceBase { private static void Main() { if (Environment.UserInteractive) { startWorkerThread(); Console.WriteLine ("====== Press ENTER to stop threads ======"); Console.ReadLine(); stopWorkerThread() ; Console.WriteLine ("====== Press ENTER to quit ======"); Console.ReadLine(); } else { Run (this) ; } } protected override void OnStart(string[] args) { startWorkerThread(); } protected override void OnStop() { stopWorkerThread() ; } }


Modifiqué el ProjectInstaller para anexar el parámetro / servicio del argumento de la línea de comandos cuando se está instalando como servicio:

static class Program { static void Main(string[] args) { if (Array.Exists(args, delegate(string arg) { return arg == "/install"; })) { System.Configuration.Install.TransactedInstaller ti = null; ti = new System.Configuration.Install.TransactedInstaller(); ti.Installers.Add(new ProjectInstaller()); ti.Context = new System.Configuration.Install.InstallContext("", null); string path = System.Reflection.Assembly.GetExecutingAssembly().Location; ti.Context.Parameters["assemblypath"] = path; ti.Install(new System.Collections.Hashtable()); return; } if (Array.Exists(args, delegate(string arg) { return arg == "/uninstall"; })) { System.Configuration.Install.TransactedInstaller ti = null; ti = new System.Configuration.Install.TransactedInstaller(); ti.Installers.Add(new ProjectInstaller()); ti.Context = new System.Configuration.Install.InstallContext("", null); string path = System.Reflection.Assembly.GetExecutingAssembly().Location; ti.Context.Parameters["assemblypath"] = path; ti.Uninstall(null); return; } if (Array.Exists(args, delegate(string arg) { return arg == "/service"; })) { ServiceBase[] ServicesToRun; ServicesToRun = new ServiceBase[] { new MyService() }; ServiceBase.Run(ServicesToRun); } else { Console.ReadKey(); } } }

El ProjectInstaller.cs se modifica para anular OnBeforeInstall () y OnBeforeUninstall ()

[RunInstaller(true)] public partial class ProjectInstaller : Installer { public ProjectInstaller() { InitializeComponent(); } protected virtual string AppendPathParameter(string path, string parameter) { if (path.Length > 0 && path[0] != ''"'') { path = "/"" + path + "/""; } path += " " + parameter; return path; } protected override void OnBeforeInstall(System.Collections.IDictionary savedState) { Context.Parameters["assemblypath"] = AppendPathParameter(Context.Parameters["assemblypath"], "/service"); base.OnBeforeInstall(savedState); } protected override void OnBeforeUninstall(System.Collections.IDictionary savedState) { Context.Parameters["assemblypath"] = AppendPathParameter(Context.Parameters["assemblypath"], "/service"); base.OnBeforeUninstall(savedState); } }


Normalmente marque el servicio de Windows como una aplicación de consola que toma un parámetro de línea de comando de "-console" para ejecutarse como una consola, de lo contrario se ejecuta como un servicio. Para depurarlo, simplemente establezca los parámetros de la línea de comando en las opciones del proyecto en "-console" ¡y listo!

Esto hace que la depuración sea fácil y sencilla, y significa que la aplicación funciona como un servicio por defecto, que es lo que desearás.


Otra solución ... por lo que puede ejecutarse como WinForm o como servicio de Windows

var backend = new Backend(); if (Environment.UserInteractive) { backend.OnStart(); Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Fronend(backend)); backend.OnStop(); } else { var ServicesToRun = new ServiceBase[] {backend}; ServiceBase.Run(ServicesToRun); }


Tal vez comprobar si el padre del proceso es C: / Windows / system32 / services.exe.