I work as a service

I am currently writing a small bootstrap code for a service that can be started in the console. In essence, it comes down to calling the OnStart () method instead of using ServiceBase to start and stop the service (because it does not start the application if it is not installed as a service and makes debugging a nightmare).

I am currently using Debugger.IsAttached to determine if I should use ServiceBase.Run or [service] .OnStart, but I know this is not a good idea because some end users want to start the service in the console (to see the output etc. in real time).

Any ideas on how I can determine if the Windows service controller “me” is running, or the user started “me” in the console? Obviously, Environment.IsUserInteractive is not the answer. I was thinking about using the args command line, but it seems dirty.

I could always see the try-catch expression in ServiceBase.Run, but it seems dirty. Edit: The catch attempt does not work.

I have a solution: put it here for all other interested stackers:

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

EDIT: There was another question in StackOverflow where the guy was having problems with Environment.CurrentDirectory being "C: \ Windows \ System32" looks like this might be the answer. I will test today.

+44
c # windows-services
Oct. 14 '08 at 6:19 06:19
source share
12 answers

Like Ash, I write all the actual processing code in a separate assembly of the class library, which is then referenced by the Windows service executable, as well as the console application.

However, there are times when it is useful to know if the class library is running in the context of a service executable or console application. I do this to reflect the base class of the hosting application. (Sorry for VB, but I suggest that c # -ified can quite easily be the following):

 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 
+15
Oct 20 '08 at 16:01
source share

Another workaround .. therefore it can work as WinForm or as a Windows service

 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); } 
+27
Jul 02 '10 at
source share

I usually mark my Windows service as a console application that accepts the "-console" command line option to start using the console, otherwise it starts as a service. For debugging, you simply set the command line parameters in the project options to "-console", and you're done!

This makes debugging enjoyable and simple, and means that the application functions as the default service, which is what you need.

+20
Oct. 14 '08 at 6:28
source share

What works for me:

  • The class that performs the actual operation of the service starts in a separate thread.
  • This thread starts from the OnStart () method and stops with OnStop ().
  • The decision between service and console mode depends on Environment.UserInteractive

Code example:

 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() ; } } 
+14
Oct. 16 '08 at 10:47
source share

Jonathan, not quite the answer to your question, but I just finished writing the Windows service, and also noted difficulties with debugging and testing.

I solved it by simply writing all the actual processing code in a separate assembly of the class library, which was then referenced by the Windows service executable, as well as the console application and test harness.

In addition to the basic timer logic, more and more complex processing took place in the general assembly and could be tested / started on demand incredibly easily.

+9
Oct. 14 '08 at 6:34
source share

I modified ProjectInstaller to add a parameter / service to the command line argument when it is installed as a service:

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

ProjectInstaller.cs is then modified to override the OnBeforeInstall () and OnBeforeUninstall () functions.

 [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); } } 
+9
Jan 21 '10 at 17:44
source share

This topic is really old, but I thought I would drop my solution there. Simple enough to cope with this situation, I built a "service harness", which is used in both console and Windows applications. As above, most of the logic is contained in a separate library, but this is more for testing and “linking”.

The enclosed code is by no means the “best possible” way to solve this problem, just my own approach. Here, the service harness is called by the console application in the “console mode” mode and the “start the service” logic of the same application when it works as a service. By doing it this way, you can now call

ServiceHost.Instance.RunningAsAService (Boolean)

from anywhere in your code to check if the application is running as a service or just as a console.

Here is the 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); } } 

All you have to do is replace this sinister ReconcilationEngine search link with any method that extends your logic. Then in your application, use the ServiceHost.Instance.Start() and ServiceHost.Instance.Stop() methods if you are working in console mode or as a service.

+4
Apr 04 2018-12-12T00:
source share

Perhaps check if the parent of the process is C: \ Windows \ system32 \ services.exe.

+3
Nov 04 '08 at 7:31
source share

The only way I found for this is to check whether the console is connected to the process in the first place by accessing any Console object (for example, Title) inside the try / catch block.

If the service is started by SCM, there is no console, and access to the property will raise System.IO.IOError.

However, since this is a bit like I rely on implementation-specific details (what if SCM on some platforms or someday decides to provide a console to run processes?), I always use the command line switch (-console) in production applications ...

+2
Oct. 14 '08 at 6:37
source share

Here's a translation of chksr's response to .NET and avoiding an error that interactive services do not recognize:

 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; 
+1
Jan 14 '15 at 2:54
source share

This is a little self-starting, but I have a small application that will load your types of services in your application through reflection and execute them this way. I am including the source code, so you can modify it a bit to display standard output.

No code changes needed to use this solution. I also have a Debugger.IsAttached solution that is common enough to use with any service. The link is in this article: .NET Windows Service Runner

0
Oct. 14 '08 at 6:39
source share

Well, the code is very old (about 20 years or so, but not from me, but in the wild, in the wild, and in C not C #), which should give you an idea of ​​how to do this work:

 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 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); } 
-one
Sep 12 '14 at 11:43 on
source share



All Articles