Frage

Ich bin derzeit ein wenig Bootstrap-Code für einen Dienst zu schreiben, die in der Konsole ausgeführt werden können. Es läuft darauf hinaus im Wesentlichen bis auf die OnStart () -Methode aufrufen, anstatt die Servicebase der Verwendung zu starten und beenden Sie den Dienst (weil es die Anwendung nicht ausgeführt werden, wenn es nicht als Dienst installiert ist, und macht das Debuggen ein Alptraum).

Im Moment ist Debugger.IsAttached verwende, um zu bestimmen, ob ich ServiceBase.Run oder [Service] .OnStart verwenden sollte, aber ich weiß, das ist nicht die beste Idee, weil einige Male Benutzer den Dienst in einer Konsole ausführen möge Ende (die Ausgabe usw. in Echtzeit zu sehen).

Alle Ideen, wie ich, wenn der Windows-Dienst-Controller gestartet bestimmen könnte ‚ich‘, oder wenn der Benutzer gestartet ‚me‘ in der Konsole? Apparantly Environment.IsUserInteractive ist nicht die Antwort. Ich dachte über Kommandozeilen args verwenden, aber das scheint ‚schmutzig‘.

Ich kann immer sehen, über eine Try-catch-Anweisung um ServiceBase.Run, aber das scheint schmutzig. Edit:. Versuchen Sie fangen nicht funktioniert

Ich habe eine Lösung: Putting es hier für alle anderen interessierten Staplern bis:

    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: Es war eine weitere Frage auf Stackoverflow, wo der Mann hatte Probleme mit der Environment.CurrentDirectory ist „C: \ Windows \ System32“ sieht aus wie, dass die Antwort sein kann. Ich werde heute testen.

War es hilfreich?

Lösung

Wie Ash, ich schreibe all tatsächlichen Verarbeitungscode in einer separaten Klassenbibliothek Baugruppe, die dann durch die Windows-Dienst ausführbare Datei referenziert wurde, sowie eine Konsole app.

Allerdings gibt es Gelegenheiten, bei denen es sinnvoll ist, zu wissen, ob die Klassenbibliothek wird im Rahmen des Dienstes ausführbar oder der Konsole App ausgeführt wird. Die Art, wie ich dies tun, ist auf der Basisklasse des Hosting-App zu reflektieren. (Sorry für die VB, aber ich glaube, dass die folgend könnte c # sein -ified ziemlich leicht):

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

Andere Tipps

Eine andere Lösung .. so kann als WinForm oder als Windows-Dienst ausführen

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

ich in der Regel Flagge von Windows-Dienst als Konsolenanwendung, die eine Kommandozeilenparameter von „-console“ nimmt als Konsole zu laufen, sonst läuft es als Dienstleistung an. So debuggen Sie nur die Befehlszeile paramters in den Projektoptionen auf „console“ und du bist weg!

Das macht das Debuggen schön und einfach und bedeutet, dass die App-Funktionen als Dienst standardmäßig, das ist, was Sie wollen.

Was funktioniert für mich:

  • Die Klasse der eigentlichen Dienst Arbeit tut, ist in einem separaten Thread ausgeführt wird.
  • Dieser Thread innerhalb der OnStart () -Methode gestartet und gestoppt von OnStop ().
  • 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 Ihre Frage, aber ich habe gerade einen Windows-Dienst zu schreiben und verwies auch auf die Schwierigkeit mit Debuggen und Testen.

es wird gelöst, indem einfach all tatsächlichen Verarbeitungscode in einer separaten Klassenbibliothek Montag schreiben, das dann durch die Windows-Dienst ausführbare Datei referenziert wurde, sowie eine Konsolenanwendung und eine Testumgebung.

Neben grundlegender Timer Logik, die all komplexeren Verarbeitung geschah in der gemeinsamen Versammlung und konnte unglaublich leicht auf Anfrage getestet werden / laufen.

Ich habe das Project modifiziert, um die Befehlszeilenargument Parameter / Dienst anhängen, wenn es als Dienst installiert werden:

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 ist dann ein OnBeforeInstall () und OnBeforeUninstall ()

außer Kraft zu setzen modifizierte
[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, dass ich meine Lösung werfen würde da draußen. Ganz einfach, diese Art von Situation zu handhaben, baute ich einen „Service Baum“, die sowohl in der Konsole und Windows-Dienst Fällen verwendet wird. Wie oben, die meisten der Logik ist in einer separaten Bibliothek enthalten ist, aber das ist mehr für das Testen und „Verknüpfbarkeit“.

Mit dem beigefügten Code keineswegs stellt den „bestmöglichen“ Weg, dies zu lösen, nur meinen eigenen Ansatz. Hier wird der Service Baum wird von der Konsolenanwendung, wenn in „Konsolenmodus“ und durch die gleiche Anwendung „Start Service“ Logik aufgerufen wird, wenn es als Dienst ausgeführt wird. Durch es auf diese Weise tun, können Sie jetzt anrufen

ServiceHost.Instance.RunningAsAService (Boolean)

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

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, dass die bedrohliche aussehende ReconcilationEngine Referenz ersetzen mit dem, was Methode, um Ihre Logik ist boostrapping. Dann in Ihrer Anwendung, verwenden Sie die ServiceHost.Instance.Start() und ServiceHost.Instance.Stop() Methoden, ob Sie im Konsolenmodus oder als Dienst ausgeführt werden.

Überprüfen Vielleicht, wenn der Prozess Elternteil ist. C: \ Windows \ system32 \ services.exe

Die einzige Möglichkeit, die ich gefunden habe, dies zu erreichen, ist zu überprüfen, ob eine Konsole mit dem Prozess an erster Stelle angebracht ist, mit einer Konsole Objekteigenschaft Zugriff (zB Titel) innerhalb eines try / catch-Blockes.

Wenn der Dienst durch den SCM gestartet wird, gibt es keine Konsole, und die Eigenschaft, den Zugriff auf eine System.IO.IOError werfen.

Da dies jedoch ein bisschen zu viel fühlt sich wie auf einer Implementierung spezifische Detail verlassen (was, wenn der SCM auf einigen Plattformen oder entscheidet einen Tag eine Konsole auf die Prozesse zu schaffen, es beginnt?), Habe ich immer eine Befehlszeilenoption verwenden (console) in Produktion apps ...

Hier ist eine Übersetzung von chksr Antwort auf .NET und die Fehler zu vermeiden, die interaktiven Dienste verkennen:

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 über Reflexion wird laden und sie auf diese Weise auszuführen. Ich schließe den Quellcode, so könnte man es leicht Standardausgabe anzuzeigen ändern.

Es wird keine Änderungen am Code erforderlich, um diese Lösung zu verwenden. Ich habe eine Debugger.IsAttached Art der Lösung als auch, dass generische genug ist, um mit einem Dienst genutzt werden. Link ist in diesem Artikel: .NET Windows Service Runner

Nun gibt es einig sehr alten Code (ca. 20 Jahre oder so, nicht von mir, sondern in der freien Natur, Wildbahn und nicht in C C # gefunden), dass Sie sollte eine Idee geben, wie die Arbeit zu tun:

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);
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top