[ACCEPTED]-Am I Running as a Service-windows-services

Accepted answer
Score: 26

Another workaround.. so can run as WinForm 1 or as 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);
}
Score: 20

I usually flag my Windows service as a console 8 application which takes a command line parameter 7 of "-console" to run using a console, otherwise 6 it runs as a service. To debug you just 5 set the command line parameters in the project 4 options to "-console" and you're off!

This 3 makes debugging nice and easy and means 2 that the app functions as a service by default, which 1 is what you'll want.

Score: 16

What works for me:

  • The class doing the actual service work is running in a separate thread.
  • This thread is started from within the OnStart() method, and stopped from OnStop().
  • The decision between service and console mode depends on Environment.UserInteractive

Sample code:

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

0

Score: 16

Like Ash, I write all actual processing 10 code in a separate class library assembly, which 9 was then referenced by the windows service 8 executable, as well as a console app.

However, there 7 are occasions when it is useful to know 6 if the class library is running in the context 5 of the service executable or the console 4 app. The way I do this is to reflect on 3 the base class of the hosting app. (Sorry 2 for the VB, but I imagine that the following 1 could be c#-ified fairly easily):

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
Score: 10

Jonathan, not exactly an answer to your 11 question, but I've just finished writing 10 a windows service and also noted the difficulty 9 with debugging and testing.

Solved it by 8 simply writing all actual processing code 7 in a separate class library assembly, which 6 was then referenced by the windows service 5 executable, as well as a console app and 4 a test harness.

Apart from basic timer logic, all 3 more complex processing happened in the 2 common assembly and could be tested/run 1 on demand incredibly easily.

Score: 10

I have modified the ProjectInstaller to 5 append the command-line argument parameter 4 /service, when it is being installed as 3 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();
        }
    }
}

The ProjectInstaller.cs is then 2 modified to override a OnBeforeInstall() and 1 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);
    }
}
Score: 4

This thread is really old, but I thought 22 I would throw my solution out there. Quite 21 simply, to handle this type of situation, I 20 built a "service harness" that is used in 19 both the console and Windows service cases. As 18 above, most of the logic is contained in 17 a separate library, but this is more for 16 testing and "linkability".

The attached code 15 by no means represents the "best possible" way 14 to solve this, just my own approach. Here, the 13 service harness is called by the console 12 app when in "console mode" and by the same 11 application's "start service" logic when 10 it is running as a service. By doing it 9 this way, you can now call

ServiceHost.Instance.RunningAsAService (Boolean)

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

Here 6 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 need to do here is 5 replace that ominous looking ReconcilationEngine reference 4 with whatever method is boostrapping your 3 logic. Then in your application, use the 2 ServiceHost.Instance.Start() and ServiceHost.Instance.Stop() methods whether you are running in 1 console mode or as a service.

Score: 3

Maybe checking if the process parent is 1 C:\Windows\system32\services.exe.

Score: 2

The only way I've found to achieve this, is 12 to check if a console is attached to the 11 process in the first place, by accessing 10 any Console object property (e.g. Title) inside 9 a try/catch block.

If the service is started 8 by the SCM, there is no console, and accessing 7 the property will throw a System.IO.IOError.

However, since 6 this feels a bit too much like relying on 5 an implementation-specific detail (what 4 if the SCM on some platforms or someday 3 decides to provide a console to the processes 2 it starts?), I always use a command line 1 switch (-console) in production apps...

Score: 1

Here is a translation of chksr's answer 2 to .NET, and avoiding the bug that fails 1 to recognize interactive services:

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;
Score: 0

This is a bit of a self-plug, but I've got 9 a little app that will load up your service 8 types in your app via reflection and execute 7 them that way. I include the source code, so 6 you could change it slightly to display 5 standard output.

No code changes needed to 4 use this solution. I have a Debugger.IsAttached 3 type of solution as well that is generic 2 enough to be used with any service. Link 1 is in this article: .NET Windows Service Runner

Score: 0

Well there's some very old code (about 20 3 years or so, not from me but found in the 2 wild, wild web, and in C not C#) that should 1 give you an idea how to do the job:

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);
}
Score: 0

Seems I am bit late to the party, but interesting 15 difference when run as a service is that 14 at start current folder points to system 13 directory (C:\windows\system32 by default). Its hardly unlikely 12 user app will start from the system folder 11 in any real life situation.

So, I use following 10 trick (c#):

protected static bool IsRunAsService()
{
    string CurDir = Directory.GetCurrentDirectory();
    if (CurDir.Equals(Environment.SystemDirectory, StringComparison.CurrentCultureIgnoreCase))
    { 
         return true; 
    }

    return (false);
}

For future extension, additional 9 check make be done for System.Environment.UserInteractive == false (but I do not know 8 how it correlates with 'Allow service to 7 interact with desktop' service settings).

You 6 may also check window session by System.Diagnostics.Process.GetCurrentProcess().SessionId == 0 (I do 5 not know how it correlates with 'Allow service 4 to interact with desktop' service settings 3 as well).

If you write portable code (say, with 2 .NetCore) you may also check Environment.OSVersion.Platform to ensure 1 that you are on windows first.

More Related questions