Bin ich als ein Dienst ausgeführt

Ich schreibe gerade einen kleinen Bootstrap-Code für einen Dienst, der in der Konsole ausgeführt werden kann. Es läuft im Wesentlichen darauf hinaus, die OnStart () – Methode aufzurufen, anstatt die ServiceBase zum Starten und Stoppen des Dienstes zu verwenden (weil die Anwendung nicht ausgeführt wird, wenn sie nicht als Dienst installiert ist und das Debuggen zu einem Albtraum macht).

Im Moment benutze ich Debugger.IsAttached, um zu bestimmen, ob ich ServiceBase.Run oder [service] .OnStart verwenden soll, aber ich weiß, dass das nicht die beste Idee ist, weil Endbenutzer manchmal den Dienst in einer Konsole ausführen wollen (um zu sehen) der Ausgang usw. in Echtzeit).

Irgendwelche Ideen, wie ich feststellen könnte, ob der Windows-Dienst-Controller “mich” gestartet hat oder ob der Benutzer “mich” in der Konsole gestartet hat? Apparently Environment.IsUserInteractive ist nicht die Antwort. Ich dachte daran, Befehlszeilenargumente zu verwenden, aber das scheint “schmutzig” zu sein.

Ich konnte immer eine Try-Catch-statement um ServiceBase.Run sehen, aber das scheint dreckig zu sein. Edit: Versuch zu fangen funktioniert nicht.

Ich habe eine Lösung: Ich stelle es für alle anderen interessierten Stapler hier auf:

public void Run() { if (Debugger.IsAttached || Environment.GetCommandLineArgs().Contains("-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(); } } 

BEARBEITEN: Es gab eine andere Frage auf StackOverflow, wo der Typ Probleme mit dem Environment.CurrentDirectory hatte, das “C: \ Windows \ System32” ist, sieht so aus, könnte die Antwort sein. Ich werde heute testen.

Solutions Collecting From Web of "Bin ich als ein Dienst ausgeführt"

Wie Ash schreibe ich den gesamten tatsächlichen Verarbeitungscode in eine separate classnbibliotheks-Assembly, auf die dann von der ausführbaren Windows-Dienst-Datei verwiesen wird, sowie auf eine Konsolen-App.

Es gibt jedoch Situationen, in denen es nützlich ist zu wissen, ob die classnbibliothek im Kontext der ausführbaren Datei des Dienstes oder der Konsolenanwendung ausgeführt wird. Die Art, wie ich das tue, ist, über die Basisklasse der Hosting-App nachzudenken. (Entschuldigung für die VB, aber ich stelle mir vor, dass das Folgende relativ einfach zu übersetzen ist):

 Public Class ExecutionContext '''  ''' Gets a value indicating whether the application is a windows service. '''  '''  ''' true if this instance is service; otherwise, false. '''  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 

Eine andere Problemumgehung .. kann so als WinForm oder als Windows-Dienst ausgeführt werden

 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); } 

Normalerweise kennzeichne ich den Windows-Dienst als eine Konsolenanwendung, die den Befehlszeilenparameter “-console” als Konsole verwendet, andernfalls wird sie als Dienst ausgeführt. Um zu debuggen, setze einfach die Befehlszeilenparameter in den Projektoptionen auf “-console” und du bist dran!

Dies macht das Debuggen sehr einfach und bedeutet, dass die App standardmäßig als Dienst fungiert, was Sie wünschen.

Was für mich funktioniert:

  • Die class, die die eigentliche Servicearbeit erledigt, läuft in einem separaten Thread.
  • Dieser Thread wird innerhalb der OnStart () -Methode gestartet und von OnStop () gestoppt.
  • Die Entscheidung zwischen Service- und Konsolenmodus hängt von Environment.UserInteractive

Beispielcode:

 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() ; } } 

Jonathan, nicht gerade eine Antwort auf deine Frage, aber ich habe gerade einen Windows-Dienst fertig geschrieben und auch die Schwierigkeiten beim Debuggen und Testen festgestellt.

Es wurde getriggers, indem einfach der gesamte Verarbeitungscode in eine separate classnbibliotheks-Assembly geschrieben wurde, auf die dann die ausführbare Datei des Windows-Diensts sowie eine Konsolenanwendung und eine Testumgebung verwiesen wurden.

Abgesehen von der grundlegenden Timer-Logik, erfolgte die komplexere Verarbeitung in der allgemeinen Assembly und konnte auf einfache Weise getestet / ausgeführt werden.

Ich habe den ProjectInstaller so geändert, dass er den Befehlszeilenargumentparameter / service anfügt, wenn er als Dienst installiert wird:

 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(); } } } 

Die ProjectInstaller.cs wird dann geändert, um OnBeforeInstall () und OnBeforeUninstall () zu überschreiben

 [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); } } 

Dieser Thread ist wirklich alt, aber ich dachte, ich würde meine Lösung dort hincasting. Um diese Art von Situation zu meistern, habe ich einfach einen “Service-Kabelbaum” erstellt, der sowohl in den Konsolen- als auch in den Windows-Service-Fällen verwendet wird. Wie oben ist der größte Teil der Logik in einer separaten Bibliothek enthalten, aber das ist mehr für das Testen und die “Verknüpfbarkeit”.

Der beigefügte Code stellt keineswegs den “bestmöglichen” Weg dar, dies zu lösen, nur meine eigene Herangehensweise. Hier wird der Service-Kabelbaum von der Konsolen-App aufgerufen, wenn er sich im “Konsolen-Modus” befindet, und von der “Start Service” -Logik derselben Anwendung, wenn er als Dienst ausgeführt wird. Auf diese Weise können Sie jetzt anrufen

ServiceHost.Instance.RunningAsAService (Boolean)

von überall in Ihrem Code, um zu überprüfen, ob die Anwendung als Dienst oder einfach als Konsole ausgeführt wird.

Hier ist der Code:

 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); } } 

Alles, was Sie hier tun müssen, ist, diese ominöse ReconcilationEngine Referenz durch irgendeine Methode zu ersetzen, die Ihre Logik verbessert. Verwenden Sie dann in Ihrer Anwendung die Methoden ServiceHost.Instance.Start() und ServiceHost.Instance.Stop() , unabhängig davon, ob Sie im Konsolenmodus oder als Dienst ausgeführt werden.

Vielleicht überprüfen, ob der process Eltern C: \ Windows \ system32 \ services.exe ist.

Der einzige Weg, den ich gefunden habe, um dies zu erreichen, besteht darin, zu prüfen, ob eine Konsole an den process angeschlossen ist, indem ich auf eine Console-Objekteigenschaft (zB Titel) innerhalb eines try / catch-Blocks zugreife.

Wenn der Dienst vom SCM gestartet wird, gibt es keine Konsole, und beim Zugriff auf die Eigenschaft wird ein System.IO.IOError ausgetriggers.

Da dies jedoch ein bisschen zu sehr ist, als würde man sich auf ein implementierungsspezifisches Detail verlassen (was ist, wenn das SCM auf einigen Plattformen oder eines Tages beschließt, eine Konsole für die processe bereitzustellen, die es startet), verwende ich immer einen Befehlszeilenschalter (-console) ) in Produktions-Apps …

Hier ist eine Übersetzung von chksrs Antwort auf .NET und die Vermeidung des Fehlers, der interaktive Dienste nicht erkennt:

 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; 

Dies ist ein bisschen ein Selbst-Stecker, aber ich habe eine kleine App, die Ihre Service-Typen in Ihrer App durch reflection laden und sie auf diese Weise ausführen wird. Ich schließe den Quellcode ein, sodass Sie ihn leicht ändern können, um die Standardausgabe anzuzeigen.

Für die Verwendung dieser Lösung sind keine Codeänderungen erforderlich. Ich habe auch einen Debugger.IsAttached-Typ der Lösung, der generisch genug ist, um mit jedem Dienst verwendet zu werden. Link ist in diesem Artikel: .NET Windows Service Runner

Nun, es gibt einen sehr alten Code (ungefähr 20 Jahre oder so, nicht von mir, sondern im wilden, wilden Web und in C nicht C #), der dir eine Idee geben sollte, wie man den Job macht:

 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); }