54 Stimmen

Bin ich als Dienst tätig?

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 zu verwenden, um den Dienst zu starten und zu stoppen (weil die Anwendung nicht ausgeführt wird, wenn sie nicht als Dienst installiert ist und die Fehlersuche zu einem Albtraum wird).

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

Haben Sie eine Idee, wie ich feststellen kann, ob der Windows-Dienstcontroller "mich" gestartet hat oder ob der Benutzer "mich" in der Konsole gestartet hat? Anscheinend Umgebung.IsUserInteractive ist nicht die Antwort. Ich dachte an die Verwendung von Befehlszeilen-Args, aber das scheint "schmutzig".

Ich könnte immer sehen, über eine Try-Catch-Anweisung um ServiceBase.Run, aber das scheint schmutzig. Bearbeiten: Try-Catch funktioniert nicht.

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

    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 gab eine andere Frage auf StackOverflow, wo der Kerl Probleme mit dem Environment.CurrentDirectory hatte, das " C:\Windows\System32 "Sieht so aus, als könnte das die Antwort sein. Ich werde das heute testen.

0 Stimmen

Vielen Dank, dass Sie Ihre Lösung hinzugefügt haben, das sollte eine nützliche Referenz sein.

2 Stimmen

Nicht, dass IsUserInteractive no für Konsolenanwendungen false zurückgeben, wie es in dem Link, den Sie oben angegeben haben, angegeben wurde - zumindest nicht generell. Ich benutze es für diesen Zweck und hatte nie irgendwelche Probleme damit.

0 Stimmen

0voto

Anderson Imes Punkte 25252

Dies ist ein bisschen ein Selbst-Stecker, aber ich habe eine kleine App, die Ihre Service-Typen in Ihrer App über Reflexion laden und führen sie auf diese Weise. Ich füge den Quellcode bei, so dass 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 eine Lösung vom Typ Debugger.IsAttached, die generisch genug ist, um mit jedem Dienst verwendet zu werden. Der Link befindet sich in diesem Artikel: .NET Windows-Dienst-Läufer

0 Stimmen

Ich habe tatsächlich eine Basisklasse für sie geschrieben, die als Start()-Methode hat, auf diese Weise muss ich nicht auf Reflexion zurückgreifen. Danke für den Tipp aber.

0 Stimmen

Dies ist eine eigenständige Möglichkeit, jeden Dienst außerhalb der Windows-Dienstumgebung auszuführen, ohne den Code zu ändern. Doppelklicken Sie einfach auf den Runner, wählen Sie die .exe oder .dll Ihres Dienstes und klicken Sie auf ok. Wenn Sie den Runner für die Befehlszeile ausführen, sehen Sie Standard-IO.

0voto

chksr Punkte 164

Nun, es gibt einige sehr alte Code (etwa 20 Jahre oder so, nicht von mir, aber in der wilden, wilden Web gefunden, und in C nicht C #), die Ihnen eine Idee, wie man die Arbeit zu tun geben sollte:

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

3 Stimmen

Das sieht nicht nach c# aus.

3 Stimmen

Aber es könnte doch in C# übersetzt werden, oder nicht?

5 Stimmen

Nicht so einfach c#-ifiziert

0voto

Serge Ageyev Punkte 427

Ich bin wohl etwas spät dran, aber der interessante Unterschied, wenn ich als Dienst ausgeführt werde, ist, dass der aktuelle Ordner beim Start auf das Systemverzeichnis zeigt ( C:\windows\system32 standardmäßig). Es ist sehr unwahrscheinlich, dass eine Benutzeranwendung in einer realen Situation aus dem Systemordner gestartet wird.

Also, ich verwende folgenden Trick (c#):

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

    return (false);
}

Für die künftige Verlängerung wird zusätzlich geprüft, ob System.Environment.UserInteractive == false (aber ich weiß nicht, wie es mit den Einstellungen des Dienstes "Interaktion zwischen Dienst und Desktop zulassen" zusammenhängt).

Sie können die Fenstersitzung auch überprüfen, indem Sie System.Diagnostics.Process.GetCurrentProcess().SessionId == 0 (Ich weiß nicht, wie es mit den Einstellungen des Dienstes "Interaktion zwischen Dienst und Desktop zulassen" zusammenhängt).

Wenn Sie portablen Code schreiben (z.B. mit .NetCore), können Sie auch die Environment.OSVersion.Platform um sicherzustellen, dass Sie zuerst auf Windows sind.

0 Stimmen

Das aktuelle Verzeichnis kann in einer Vielzahl von Fällen system32 sein, z. B. wenn die Anwendung als Administrator oder mit erweiterten Rechten ausgeführt wird, wenn sie von einem Skript aus gestartet wird oder wenn manuell dorthin kopiert wird. Dies ist KEINE Möglichkeit zu sagen, dass Sie als Dienst ausgeführt werden.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X