Einfachere Möglichkeit zum Debuggen eines Windows-Dienstes


325

Gibt es eine einfachere Möglichkeit, den Code zu durchlaufen, als den Dienst über den Windows Service Control Manager zu starten und dann den Debugger an den Thread anzuhängen? Es ist etwas umständlich und ich frage mich, ob es einen einfacheren Ansatz gibt.


Ich habe dieses User Voice-Ticket erstellt. Erwägen
David

Antworten:


271

Wenn ich den Dienst schnell debuggen möchte, schaue ich einfach dort rein Debugger.Break(). Wenn diese Linie erreicht ist, werde ich wieder zu VS zurückkehren. Vergessen Sie nicht, diese Zeile zu entfernen, wenn Sie fertig sind.

UPDATE: Alternativ zu #if DEBUGPragmas können Sie auch Conditional("DEBUG_SERVICE")Attribute verwenden.

[Conditional("DEBUG_SERVICE")]
private static void DebugMode()
{
    Debugger.Break();
}

OnStartRufen Sie auf Ihrem einfach diese Methode auf:

public override void OnStart()
{
     DebugMode();
     /* ... do the rest */
}

Dort wird der Code nur während Debug-Builds aktiviert. Wenn Sie gerade dabei sind, kann es hilfreich sein, eine separate Build-Konfiguration für das Service-Debugging zu erstellen.


45
Oder Sie können Debugger.Launch () verwenden. Sie müssen eine using-Anweisung für den Namespace The Systems.Diagnostics einfügen.
Omar Kooheji

1
Ihr Blog-Beitrag hat einwandfrei funktioniert und meinen Tag gerettet :) Allerdings hat der Debugger.Break () bei mir nicht funktioniert. Es scheint, dass .Net die DebugMode-Funktion aus Optimierungsgründen überspringt.
Bizhan

3
Debugger.Launch () funktioniert bei mir, wenn Debugger.Break () dies nicht tut. (Prozess wird mit Code 255 beendet.)
Oliver Bock

Wie bringt ihr das zum Laufen? Nichts passiert. Ich habe Break () und Launch () ausprobiert.
4thSpace

13
@ 4thSpace: 1. Erstellen Sie ein Installationsprogramm für Ihren Dienst, damit Sie Ihren Dienst installieren können. 2. Fügen Sie die Zeile Debugger.Launch () hinzu. am Anfang Ihres Main (). 3. Erstellen Sie Ihren Code im Debug-Modus. 4. Überschreiben Sie die installierten DLLs mit den Debug-DLLs. 5. Starten Sie den Dienst über das Fenster "Windows-Dienste". Nun wird ein Popup angezeigt, in dem Sie aufgefordert werden, eine Verbindung zu einem Debugger herzustellen. Dieser Weg hat bei mir funktioniert. Hoffentlich auch für dich.
ffonz

210

Ich denke auch, dass eine separate "Version" für die normale Ausführung und als Service der richtige Weg ist, aber ist es wirklich erforderlich, einen separaten Befehlszeilenschalter für diesen Zweck zuzuweisen?

Könnten Sie nicht einfach tun:

public static int Main(string[] args)
{
  if (!Environment.UserInteractive)
  {
    // Startup as service.
  }
  else
  {
    // Startup as application
  }
}

Das hätte den "Vorteil", dass Sie Ihre App einfach per Doppelklick starten können (OK, wenn Sie das wirklich brauchen) und dass Sie einfach F5in Visual Studio klicken können (ohne die Projekteinstellungen ändern zu müssen, um diese /consoleOption einzuschließen).

Technisch gesehen wird Environment.UserInteractivegeprüft, ob das WSF_VISIBLEFlag für die aktuelle Fensterstation gesetzt ist. Gibt es jedoch einen anderen Grund, aus dem es zurückkehren würde false, abgesehen davon, dass es als (nicht interaktiver) Dienst ausgeführt wird?


Großartig! Ich habe früher eine "if #debug" -Methode verwendet, um als Anwendung beim Debuggen zu starten, andernfalls als Dienst. Dies führt dazu, dass die App nicht als Dienst ausgeführt werden kann, wenn Sie sie debuggen möchten. Ihre Lösung löst dies jedoch und lässt sie in allen vier Kombinationen von Dienst / App und Release / Debug ausführen.
Jonas

29
Wenn Sie nicht möchten, dass das Programm ausgeführt wird, wenn Sie darauf doppelklicken (Benutzer werden möglicherweise verwirrt und führen mehrere Instanzen usw. aus), können Sie System.Diagnostics.Debugger.IsAttachedstattdessen verwenden Environment.UserInteractive.
Blorgbeard ist

5
Aber gibt es noch einen anderen Grund, aus dem false zurückgegeben wird, abgesehen davon, dass es als (nicht interaktiver) Dienst ausgeführt wird? Ich kann mir eines vorstellen: Eine geplante Aufgabe, die nicht an eine Konsole angeschlossen ist.
Hogan

7
Ich benutze Befehlszeilenparameter für diesen Fall. --install, um den Dienst zu installieren, --uninstall, um den Dienst zu deinstallieren, und --interactive, um den Dienst als Anwendung auszuführen. Ich füge --interactive zu den Projektoptionen hinzu (Debugging> Befehlsargumente). So kann ich leicht von VS debuggen. Durch Doppelklicken wird keine unerwünschte laufende Instanz erstellt, da --interactive erforderlich ist. nur meine 2 Cent.
Emir Akaydın

@ EmirAkaydın Ja, eigentlich habe ich auch Befehlszeilenparameter als "Backup". Eigentlich wollte ich jedoch eine "interaktive" Instanz beim Doppelklicken und nicht und eine Fehlermeldung darüber, dass ein Dienst nicht auf diese Weise gestartet werden kann. Unterschiedliche Ziele, denke ich ;-)
Christian.K

122

Als ich vor einigen Wochen ein neues Serviceprojekt eingerichtet habe, habe ich diesen Beitrag gefunden. Obwohl es viele großartige Vorschläge gibt, habe ich immer noch nicht die gewünschte Lösung gefunden: Die Möglichkeit, die Serviceklassen OnStartund OnStop-methoden aufzurufen , ohne die Serviceklassen zu ändern.

Die Lösung, die ich gefunden habe, verwendet den Environment.Interactiveausgewählten Laufmodus, wie in anderen Antworten auf diesen Beitrag vorgeschlagen.

static void Main()
{
    ServiceBase[] servicesToRun;
    servicesToRun = new ServiceBase[] 
    {
        new MyService()
    };
    if (Environment.UserInteractive)
    {
        RunInteractive(servicesToRun);
    }
    else
    {
        ServiceBase.Run(servicesToRun);
    }
}

Der RunInteractiveHelfer verwendet Reflection, um die geschützten OnStartund OnStopMethoden aufzurufen :

static void RunInteractive(ServiceBase[] servicesToRun)
{
    Console.WriteLine("Services running in interactive mode.");
    Console.WriteLine();

    MethodInfo onStartMethod = typeof(ServiceBase).GetMethod("OnStart", 
        BindingFlags.Instance | BindingFlags.NonPublic);
    foreach (ServiceBase service in servicesToRun)
    {
        Console.Write("Starting {0}...", service.ServiceName);
        onStartMethod.Invoke(service, new object[] { new string[] { } });
        Console.Write("Started");
    }

    Console.WriteLine();
    Console.WriteLine();
    Console.WriteLine(
        "Press any key to stop the services and end the process...");
    Console.ReadKey();
    Console.WriteLine();

    MethodInfo onStopMethod = typeof(ServiceBase).GetMethod("OnStop", 
        BindingFlags.Instance | BindingFlags.NonPublic);
    foreach (ServiceBase service in servicesToRun)
    {
        Console.Write("Stopping {0}...", service.ServiceName);
        onStopMethod.Invoke(service, null);
        Console.WriteLine("Stopped");
    }

    Console.WriteLine("All services stopped.");
    // Keep the console alive for a second to allow the user to see the message.
    Thread.Sleep(1000);
}

Dies ist der gesamte erforderliche Code, aber ich habe auch eine exemplarische Vorgehensweise mit Erklärungen geschrieben.


Dies ist eine gute Erweiterungsmethode für ServiceBase []. Ich habe mehrere Dienste in meiner Lösung. Anstatt eine gemeinsame Basisklasse für Program.cs zu haben, rufe ich einfach servicesToRun.RunInteractive (args) auf. Schöne Lösung @Anders!
David Keaveny

3
Tolle Lösung. Ich habe eine einfache Erweiterung für Servicebase [] als David schlug vor , die Dienstleistungen in nur eine Zeile Code laufen zu lassen: pastebin.com/F0fhhG2R
Funbit

4
+1 Ein ehemaliger Kollege von mir hat eine Basisklasse "EasyRunService" erstellt (die ServiceProcess erbt), die fast dasselbe tut, jedoch ohne Reflexion (da OnStart jetzt in der Basisklasse ist). Das Debuggen eines Windows-Dienstes ist ein Kinderspiel.
Sondergard

3
@ Chazt3n Stellen Sie sicher, dass Ihr Projektausgabetyp auf "Konsolenanwendung" eingestellt ist. Bei der Dienstinstallation spielt es keine Rolle, welcher Ausgabetyp ausgewählt ist, das Verhalten ist dasselbe.
Funbit

2
Immer noch eine großartige Lösung! Das einzige, was ich hinzufügen möchte (wie in der Abbildung gezeigt walk through), ist sicherzustellen, dass Sie in die Projekteigenschaften gehen und den Ausgabetyp in ändern, Console Applicationbevor Sie versuchen, zu kompilieren und auszuführen. Finden Sie es bei Project Properties -> Application -> Output type -> Console Application. Damit dies für mich richtig funktioniert, musste ich die Anwendung mit dem startBefehl ausführen . Bsp.: C:\"my app name.exe" -serviceWürde bei mir nicht funktionieren. Stattdessen benutzte ichC:\start /wait "" "my app name.exe" -service
Arvo Bowen

47

Manchmal ist es wichtig zu analysieren, was während des Starts des Dienstes vor sich geht. Das Anhängen an den Prozess hilft hier nicht weiter, da Sie nicht schnell genug sind, um den Debugger anzuhängen, während der Dienst gestartet wird.

Die kurze Antwort lautet: Ich verwende dazu die folgenden 4 Codezeilen :

#if DEBUG
    base.RequestAdditionalTime(600000); // 600*1000ms = 10 minutes timeout
    Debugger.Launch(); // launch and attach debugger
#endif

Diese werden OnStartwie folgt in die Dienstmethode eingefügt :

protected override void OnStart(string[] args)
{
    #if DEBUG
       base.RequestAdditionalTime(600000); // 10 minutes timeout for startup
       Debugger.Launch(); // launch and attach debugger
    #endif
    MyInitOnstart(); // my individual initialization code for the service
    // allow the base class to perform any work it needs to do
    base.OnStart(args);
}

Für diejenigen, die es noch nicht getan haben, habe ich unten detaillierte Hinweise beigefügt , da Sie leicht stecken bleiben können. Die folgenden Hinweise beziehen sich auf Windows 7x64 und Visual Studio 2010 Team Edition , sollten jedoch auch für andere Umgebungen gültig sein.


Wichtig: Stellen Sie den Dienst im "manuellen" ModusInstallUtil bereit (verwenden Sie entweder das Dienstprogramm an der VS-Eingabeaufforderung oder führen Sie ein von Ihnen vorbereitetes Dienstinstallationsprojekt aus). Öffnen Sie Visual Studio, bevor Sie den Dienst starten, und laden Sie die Lösung mit dem Quellcode des Dienstes. Richten Sie zusätzliche Haltepunkte nach Bedarf in Visual Studio ein. Starten Sie den Dienst dann über die Dienststeuerung.

Aufgrund des Debugger.LaunchCodes wird das Dialogfeld "In Servicename.exe ist eine nicht behandelte Microsoft .NET Framework-Ausnahme aufgetreten ." erscheinen. Klicken Sie wie im Screenshot gezeigt:Elevate Yes, debug Servicename.exe
FrameworkException

Insbesondere in Windows 7 UAC werden Sie anschließend möglicherweise aufgefordert, Administratoranmeldeinformationen einzugeben. Geben Sie sie ein und fahren Sie fort mit Yes:

UACPrompt

Danach wird das bekannte Just-In-Time-Debugger-Fenster von Visual Studio angezeigt. Sie werden gefragt, ob Sie mit dem ausgewählten Debugger debuggen möchten. Bevor Sie auf klicken Yes, wählen Sie aus , dass Sie keine neue Instanz öffnen möchten (2. Option). Eine neue Instanz wäre hier nicht hilfreich, da der Quellcode nicht angezeigt wird. Sie wählen stattdessen die zuvor geöffnete Visual Studio-Instanz aus: VSDebuggerPrompt

Nachdem Sie geklickt haben Yes, zeigt Visual Studio nach einer Weile den gelben Pfeil rechts in der Zeile an, in der sich die Debugger.LaunchAnweisung befindet, und Sie können Ihren Code debuggen (Methode MyInitOnStart, die Ihre Initialisierung enthält). VSDebuggerBreakpoint

Durch Drücken von wird F5die Ausführung sofort fortgesetzt, bis der nächste von Ihnen vorbereitete Haltepunkt erreicht ist.

Tipp: Um den Dienst am Laufen zu halten, wählen Sie Debug -> Alle trennen . Auf diese Weise können Sie einen Client ausführen, der mit dem Dienst kommuniziert, nachdem dieser ordnungsgemäß gestartet wurde und Sie das Debuggen des Startcodes abgeschlossen haben. Wenn Sie Shift+F5 drücken (Debugging beenden), wird der Dienst beendet. Stattdessen sollten Sie das Service Control Panel verwenden , um es zu stoppen.

Beachten Sie das

  • Wenn Sie ein Release erstellen , wird der Debug-Code automatisch entfernt und der Dienst wird normal ausgeführt.

  • Ich verwende Debugger.Launch(), der einen Debugger startet und anfügt . Ich habe auch getestet Debugger.Break(), was nicht funktioniert hat , da beim Start des Dienstes noch kein Debugger angeschlossen ist (was den "Fehler 1067: Der Prozess wurde unerwartet beendet" verursacht ).

  • RequestAdditionalTimesetzt eine längere Timeout für die Inbetriebnahme des Service (es ist nicht selbst zu verzögern den Code, sondern wird sofort mit der weiterhin Debugger.LaunchAnweisung). Andernfalls ist das Standardzeitlimit zum Starten des Dienstes zu kurz und das Starten des Dienstes schlägt fehl, wenn Sie nicht base.Onstart(args)schnell genug vom Debugger aus anrufen . Bei einer Zeitüberschreitung von 10 Minuten wird praktisch vermieden, dass die Meldung " Der Dienst hat nicht geantwortet ..." unmittelbar nach dem Start des Debuggers angezeigt wird.

  • Sobald Sie sich daran gewöhnt haben, ist diese Methode sehr einfach, da Sie lediglich 4 Zeilen zu einem vorhandenen Service-Code hinzufügen müssen, damit Sie schnell die Kontrolle und das Debuggen erlangen können.


1
Wissen Sie aus Neugier, ob es eine Zeitüberschreitung für die Benutzerinteraktion mit der Benutzeraufforderung Debugger.Launch () gibt?
Shiv

1
base.RequestAdditionalTime(600000)Verhindert wie beschrieben, dass die Dienststeuerung den Dienst für 10 Minuten beendet, wenn sie nicht base.OnStart(args)innerhalb dieser Zeitspanne anruft. Abgesehen davon erinnere ich mich, dass die Benutzerkontensteuerung auch abgebrochen wird, wenn Sie die Administratoranmeldeinformationen nach einer Weile nicht mehr eingeben (ich weiß nicht, wie viele Sekunden genau, aber ich denke, Sie müssen sie innerhalb einer Minute eingeben, sonst bricht die Benutzerkontensteuerung ab). , wodurch die Debug-Sitzung beendet wird.
Matt

2
Ich fand, dass dies die beste Methode zum Debuggen von CustomCommand-Nachrichten ist. +1.
Justin

40

Normalerweise kapsle ich die Logik des Dienstes in eine separate Klasse und starte diese von einer 'Runner'-Klasse. Diese Runner-Klasse kann der eigentliche Dienst oder nur eine Konsolenanwendung sein. Ihre Lösung hat also (mindestens) 3 Projekte:

/ConsoleRunner
   /....
/ServiceRunner
   /....
/ApplicationLogic
   /....

1
Früher habe ich diesen Ansatz auch verwendet, aber ich denke, eine Kombination aus diesem und der obigen Antwort ist ein Vergnügen.
RobS

27

Dieses YouTube-Video von Fabio Scopel erklärt, wie man einen Windows-Dienst ganz gut debuggt ... die eigentliche Methode beginnt im Video um 4:45 Uhr ...

Hier ist der Code, der im Video erklärt wird ... Fügen Sie in Ihrer Program.cs-Datei das Material für den Debug-Abschnitt hinzu ...

namespace YourNamespace
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        static void Main()
        {
#if DEBUG
            Service1 myService = new Service1();
            myService.OnDebug();
            System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite);
#else
            ServiceBase[] ServicesToRun;
            ServicesToRun = new ServiceBase[]
            {
                new Service1()
            };
            ServiceBase.Run(ServicesToRun);
#endif

        }
    }
}

Fügen Sie in Ihrer Service1.cs-Datei die OnDebug () -Methode hinzu ...

    public Service1()
    {
        InitializeComponent();
    }

    public void OnDebug()
    {
        OnStart(null);
    }

    protected override void OnStart(string[] args)
    {
        // your code to do something
    }

    protected override void OnStop()
    {
    }

Wie es funktioniert

Grundsätzlich müssen Sie ein erstellen public void OnDebug(), das das aufruft, OnStart(string[] args)da es geschützt und außerhalb nicht zugänglich ist. Das void Main()Programm wird mit #ifPräprozessor mit hinzugefügt #DEBUG.

Visual Studio definiert, DEBUGob das Projekt im Debug-Modus kompiliert wird. Dadurch kann der Debug-Abschnitt (unten) ausgeführt werden, wenn die Bedingung erfüllt ist

Service1 myService = new Service1();
myService.OnDebug();
System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite);

Und es wird genau wie eine Konsolenanwendung ausgeführt. Sobald alles in Ordnung ist, können Sie den Modus ändern Releaseund der reguläre elseAbschnitt löst die Logik aus


Ich habe nach dieser Antwort gesucht, keine Ahnung, warum sie so niedrig eingestuft wurde. Erklärte den Code, um anderen zu helfen oder möglicherweise mehr Kommentare;)
Vinod Srivastav

14

AKTUALISIEREN

Dieser Ansatz ist bei weitem der einfachste:

http://www.codeproject.com/KB/dotnet/DebugWinServices.aspx

Ich lasse meine ursprüngliche Antwort unten für die Nachwelt.


Meine Dienste haben in der Regel eine Klasse, die einen Timer kapselt, da der Dienst in regelmäßigen Abständen prüfen soll, ob noch etwas zu tun ist.

Wir aktualisieren die Klasse neu und rufen StartEventLoop () während des Starts des Dienstes auf. (Diese Klasse kann auch problemlos von einer Konsolen-App aus verwendet werden.)

Der nette Nebeneffekt dieses Entwurfs ist, dass die Argumente, mit denen Sie den Timer einrichten, verwendet werden können, um eine Verzögerung zu haben, bevor der Dienst tatsächlich funktioniert, sodass Sie Zeit haben, einen Debugger manuell anzuhängen.

ps Wie hängt man den Debugger manuell an einen laufenden Prozess an ...?

using System;
using System.Threading;
using System.Configuration;    

public class ServiceEventHandler
{
    Timer _timer;
    public ServiceEventHandler()
    {
        // get configuration etc.
        _timer = new Timer(
            new TimerCallback(EventTimerCallback)
            , null
            , Timeout.Infinite
            , Timeout.Infinite);
    }

    private void EventTimerCallback(object state)
    {
        // do something
    }

    public void StartEventLoop()
    {
        // wait a minute, then run every 30 minutes
        _timer.Change(TimeSpan.Parse("00:01:00"), TimeSpan.Parse("00:30:00");
    }
}

Außerdem habe ich Folgendes getan (bereits in früheren Antworten erwähnt, jedoch mit den Flags des bedingten Compilers [#if], um zu verhindern, dass es in einem Release-Build ausgelöst wird).

Ich habe damit aufgehört, weil wir manchmal vergessen haben, Release einzubauen und eine Debugger-Pause in einer App einzulegen, die auf einer Client-Demo ausgeführt wird (peinlich!).

#if DEBUG
if (!System.Diagnostics.Debugger.IsAttached)
{
    System.Diagnostics.Debugger.Break();
}
#endif

Was passiert, wenn // do something die Fertigstellung länger als 30 Minuten dauert?
Vinod Srivastav

13

static void Main()
{
#if DEBUG
                // Run as interactive exe in debug mode to allow easy
                // debugging.

                var service = new MyService();
                service.OnStart(null);

                // Sleep the main thread indefinitely while the service code
                // runs in .OnStart

                Thread.Sleep(Timeout.Infinite);
#else
                // Run normally as service in release mode.

                ServiceBase[] ServicesToRun;
                ServicesToRun = new ServiceBase[]{ new MyService() };
                ServiceBase.Run(ServicesToRun);
#endif
}

[Entschuldigung für keine Erklärung mit den Code-Markdown-Problemen] Sollte in Debug-Builds normal von MS Visual Studio (F5) ausgeführt werden. Läuft weiterhin als normaler Dienst in Release-Builds.
Thomas Bratt

Kombinieren Sie dies mit der obigen Lösung von Christian K., um die Eigenschaft "Environment.UserInteractive" zu verwenden, und die Lösung ist wirklich sauber und einfach.
Ben Robbins

OnStartist protectedund Sie können die Zugriffsebene nicht ändern :(
Eduard Luca

10

Sie können den Dienst auch über die Eingabeaufforderung (sc.exe) starten.

Persönlich würde ich den Code in der Debugging-Phase als eigenständiges Programm ausführen, und wenn die meisten Fehler behoben sind, wechseln Sie zur Ausführung als Dienst.


10

Früher hatte ich einen Befehlszeilenschalter, der das Programm entweder als Dienst oder als reguläre Anwendung startete. Dann würde ich in meiner IDE den Schalter so einstellen, dass ich meinen Code durchgehen kann.

Bei einigen Sprachen können Sie tatsächlich erkennen, ob es in einer IDE ausgeführt wird, und diesen Wechsel automatisch durchführen.

Welche Sprache benutzt du?


9

Verwenden Sie die TopShelf- Bibliothek.

Erstellen Sie eine Konsolenanwendung und konfigurieren Sie das Setup in Ihrem Main

class Program
    {
        static void Main(string[] args)
        {
            HostFactory.Run(x =>
            {

                // setup service start and stop.
                x.Service<Controller>(s =>
                {
                    s.ConstructUsing(name => new Controller());
                    s.WhenStarted(controller => controller.Start());
                    s.WhenStopped(controller => controller.Stop());
                });

                // setup recovery here
                x.EnableServiceRecovery(rc =>
                {
                    rc.RestartService(delayInMinutes: 0);
                    rc.SetResetPeriod(days: 0);
                });

                x.RunAsLocalSystem();
            });
        }
}

public class Controller
    {
        public void Start()
        {

        }

        public void Stop()
        {

        }
    }

Um Ihren Dienst zu debuggen, drücken Sie einfach F5 im Visual Studio.

Geben Sie zum Installieren des Dienstes cmd "console.exe install" ein.

Anschließend können Sie den Dienst im Windows Service Manager starten und beenden.


Ihre Lizenz war verwirrend zu verstehen
l --''''''--------- ‚‘ ‚‘ ‚‘ ‚‘ ‚‘ ‚‘

Sie verwenden die Apache-Lizenz afaik. Topshelf ist der einfachste Weg, Windows-Dienste zu entwickeln und zu debuggen. Super einfach zu bedienen. Als Konsolenanwendung entwickeln. Als Dienst mit einem Befehlszeilenschalter installieren. Sehr empfehlenswert.
beraubt

TopShelf hat mir jede Menge Zeit gespart. Thx
L_7337

8

Ich denke, es hängt davon ab, welches Betriebssystem Sie verwenden. Vista ist aufgrund der Trennung zwischen den Sitzungen viel schwieriger mit Diensten zu verbinden.

Die zwei Optionen, die ich in der Vergangenheit verwendet habe, sind:

  • Verwenden Sie GFlags (in den Debugging-Tools für Windows), um einen permanenten Debugger für einen Prozess einzurichten. Dies ist im Registrierungsschlüssel "Image File Execution Options" vorhanden und unglaublich nützlich. Ich denke, Sie müssen die Serviceeinstellungen anpassen, um "Interact with Desktop" zu aktivieren. Ich benutze dies für alle Arten von Debugging, nicht nur für Dienste.
  • Die andere Möglichkeit besteht darin, den Code ein wenig zu trennen, damit der Serviceteil mit einem normalen App-Start austauschbar ist. Auf diese Weise können Sie ein einfaches Befehlszeilenflag verwenden und als Prozess (anstelle eines Dienstes) starten, was das Debuggen erheblich vereinfacht.

Hoffe das hilft.


+1 für GFlags. Dies ist besonders nützlich, wenn Sie den Quellcode nicht ändern können (oder wenn Sie ihn nicht haben).
Chris Gillum

6

Ich möchte in der Lage sein, jeden Aspekt meines Dienstes zu debuggen, einschließlich aller Initialisierungen in OnStart (), während ich ihn dennoch mit vollem Dienstverhalten im Rahmen des SCM ausführe ... kein "Konsolen" - oder "App" -Modus.

Dazu erstelle ich im selben Projekt einen zweiten Dienst, der zum Debuggen verwendet wird. Wenn der Debug-Dienst wie gewohnt gestartet wird (dh im MMC-Plugin für Dienste), wird der Diensthostprozess erstellt. Auf diese Weise können Sie den Debugger anhängen, obwohl Sie Ihren eigentlichen Dienst noch nicht gestartet haben. Starten Sie nach dem Anhängen des Debuggers an den Prozess Ihren eigentlichen Dienst, und Sie können überall im Dienstlebenszyklus darauf zugreifen, einschließlich OnStart ().

Da nur ein sehr geringer Codeeinbruch erforderlich ist, kann der Debug-Service problemlos in Ihr Service-Setup-Projekt aufgenommen und aus Ihrer Produktionsversion entfernt werden, indem eine einzelne Codezeile auskommentiert und ein einzelnes Projektinstallationsprogramm gelöscht wird.

Einzelheiten:

1) Angenommen, Sie implementieren MyService, erstellen Sie auch MyServiceDebug. Fügen Sie beide wie folgt zum ServiceBaseArray hinzu Program.cs:

    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    static void Main()
    {
        ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[] 
        { 
            new MyService(),
            new MyServiceDebug()
        };
        ServiceBase.Run(ServicesToRun);
    }

2) Fügen Sie den realen Dienst UND den Debug-Dienst zum Projektinstallationsprogramm für das Dienstprojekt hinzu:

Geben Sie hier die Bildbeschreibung ein

Beide Dienste (Real und Debug) werden eingeschlossen, wenn Sie die Ausgabe des Dienstprojekts zum Setup-Projekt für den Dienst hinzufügen. Nach der Installation werden beide Dienste im MMC-Plugin service.msc angezeigt.

3) Starten Sie den Debug-Dienst in MMC.

4) Hängen Sie in Visual Studio den Debugger an den vom Debug-Dienst gestarteten Prozess an.

5) Starten Sie den echten Dienst und genießen Sie das Debuggen.


5

Wenn ich einen Dienst schreibe, füge ich die gesamte Dienstlogik in ein DLL-Projekt ein und erstelle zwei "Hosts", die diese DLL aufrufen. Einer ist ein Windows-Dienst und der andere ist eine Befehlszeilenanwendung.

Ich verwende die Befehlszeilenanwendung zum Debuggen und hänge den Debugger nur für Fehler an den realen Dienst an, die ich in der Befehlszeilenanwendung nicht reproduzieren kann.

Wenn Sie diesen Ansatz verwenden, denken Sie daran, dass Sie den gesamten Code testen müssen, während Sie in einem echten Dienst ausgeführt werden. Das Befehlszeilentool ist eine nette Debugging-Hilfe. Es ist eine andere Umgebung und verhält sich nicht genau wie ein echter Dienst.


4

Beim Entwickeln und Debuggen eines Windows-Dienstes führe ich ihn normalerweise als Konsolenanwendung aus, indem ich einen / console-Startparameter hinzufüge und dies überprüfe. Macht das Leben viel einfacher.

static void Main(string[] args) {
    if (Console.In != StreamReader.Null) {
        if (args.Length > 0 && args[0] == "/console") {
            // Start your service work.
        }
    }
}

Bis Sie dienstspezifische Probleme debuggen müssen.
Leppie

Richtig, dann müssen Sie den Debugger an den eigentlichen Serviceprozess anhängen. In den meisten Fällen treten jedoch Fehler auf, und die Entwicklung ist viel einfacher.
Maurice

4

Wie wäre es mit Debugger.Break () in der ersten Zeile?


2

Zum Debuggen von Windows-Diensten kombiniere ich GFlags und eine von regedit erstellte REG-Datei.

  1. Führen Sie GFlags aus und geben Sie den exe-Namen und vsjitdebugger an
  2. Führen Sie regedit aus und gehen Sie zu dem Ort, an dem GFlags seine Optionen festlegt
  3. Wählen Sie "Export Key" aus dem Datei-Menü
  4. Speichern Sie diese Datei irgendwo mit der Erweiterung .reg
  5. Immer wenn Sie den Dienst debuggen möchten: Doppelklicken Sie auf die REG-Datei
  6. Wenn Sie das Debuggen beenden möchten, doppelklicken Sie auf die zweite .reg-Datei

Oder speichern Sie die folgenden Snippets und ersetzen Sie servicename.exe durch den gewünschten ausführbaren Namen.


debugon.reg:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ Windows NT \ CurrentVersion \ Ausführungsoptionen für Image-Dateien \ servicename.exe]
"GlobalFlag" = "0x00000000"
"Debugger" = "vsjitdebugger.exe"

debugoff.reg:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ Windows NT \ CurrentVersion \ Ausführungsoptionen für Image-Dateien \ servicename.exe]
"GlobalFlag" = "0x00000000"

Funktioniert das noch unter Win 7 / Win 2008? Es ist der Ansatz von support.microsoft.com/kb/824344, aber er basiert auf interaktiven Diensten, und ich dachte, sie wurden getötet? Dies war immer meine bevorzugte Option (da in der Produktion möglicherweise Startprobleme auftreten, bei denen das Einfügen eines Debugger.Break () in den Code möglicherweise keine Option ist).
Piers7

1

Für die routinemäßige Kleinprogrammierung habe ich einen sehr einfachen Trick ausgeführt, um meinen Dienst einfach zu debuggen:

Beim Start des Dienstes suche ich nach einem Befehlszeilenparameter "/ debug". Wenn der Dienst mit diesem Parameter aufgerufen wird, starte ich nicht den üblichen Dienst, sondern starte alle Listener und zeige einfach die Meldung "Debug in Bearbeitung, zum Beenden OK drücken" an.

Wenn mein Dienst auf die übliche Weise gestartet wird, wird er als Dienst gestartet. Wenn er mit dem Befehlszeilenparameter / Debug gestartet wird, verhält er sich wie ein normales Programm.

In VS füge ich einfach als Debugging-Parameter hinzu / debugge und starte das Serviceprogramm direkt.

Auf diese Weise kann ich die meisten kleinen Probleme problemlos beheben. Natürlich müssen einige Dinge noch als Service debuggt werden, aber für 99% ist dies gut genug.



1

Ich verwende eine Variation der Antwort von JOP. Mithilfe von Befehlszeilenparametern können Sie den Debugging-Modus in der IDE mit Projekteigenschaften oder über den Windows-Dienstmanager festlegen.

protected override void OnStart(string[] args)
{
  if (args.Contains<string>("DEBUG_SERVICE"))
  {
    Debugger.Break();
  }
  ...
}


1

Platzieren Sie einfach Ihr Debugger-Mittagessen an einer beliebigen Stelle und hängen Sie Visualstudio beim Start an

#if DEBUG
    Debugger.Launch();
#endif

Außerdem müssen Sie VS als Administatrator starten und zulassen, dass ein Prozess automatisch von einem anderen Benutzer debuggt werden kann (wie hier erläutert ):

reg add "HKCR\AppID{E62A7A31-6025-408E-87F6-81AEB0DC9347}" /v AppIDFlags /t REG_DWORD /d 8 /f

1

Verwenden Sie das Windows Service Template C # -Projekt, um eine neue Service-App zu erstellen: https://github.com/HarpyWar/windows-service-template

Es gibt einen automatisch erkannten Konsolen- / Servicemodus, eine automatische Installation / Deinstallation Ihres Dienstes und einige der am häufigsten verwendeten Funktionen.


1

Hier ist die einfache Methode, mit der ich den Dienst ohne zusätzliche "Debug" -Methoden und mit integrierten VS-Unit-Tests getestet habe.

[TestMethod]
public void TestMyService()
{
    MyService fs = new MyService();

    var OnStart = fs.GetType().BaseType.GetMethod("OnStart", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);

    OnStart.Invoke(fs, new object[] { null });
}

// As an extension method
public static void Start(this ServiceBase service, List<string> parameters)
{
     string[] par = parameters == null ? null : parameters.ToArray();

     var OnStart = service.GetType().GetMethod("OnStart", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);

     OnStart.Invoke(service, new object[] { par });
}

1
static class Program
{
    static void Main()
    {
        #if DEBUG

        // TODO: Add code to start application here

        //    //If the mode is in debugging
        //    //create a new service instance
        Service1 myService = new Service1();

        //    //call the start method - this will start the Timer.
        myService.Start();

        //    //Set the Thread to sleep
        Thread.Sleep(300000);

        //    //Call the Stop method-this will stop the Timer.
        myService.Stop();

         #else
        ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[] 
        { 
            new Service1() 
        };

        ServiceBase.Run(ServicesToRun);
         #endif
    }
}

das ist einfacher. Ändern Sie einfach die Lösungskonfigurationseinstellung in Debug, führen Sie das Projekt / die Lösung aus und fügen Sie unterwegs Haltepunkte hinzu.
Bahamut

0

Sie haben zwei Möglichkeiten, um das Debuggen durchzuführen.

  1. Erstellen einer Protokolldatei: Ich persönlich bevorzuge eine separate Protokolldatei wie eine Textdatei, anstatt das Anwendungsprotokoll oder das Ereignisprotokoll zu verwenden. Dies kostet Sie jedoch viel Zeit, da es immer noch schwierig ist, herauszufinden, wo sich der genaue Fehlerort befindet
  2. Konvertieren Sie die Anwendung in eine Konsolenanwendung: Auf diese Weise erhalten Sie alle Debugging-Tools, die wir in VS verwenden können.

Bitte beziehen Sie sich auf DIESEN Blog-Beitrag, den ich für das Thema erstellt habe.


0

Einfach einfügen

Debugger.Break();

irgendwo in Ihrem Code.

Beispielsweise ,

internal static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        private static void Main()
        {
            Debugger.Break();
            ServiceBase[] ServicesToRun;
            ServicesToRun = new ServiceBase[]
            {
                new Service1()
            };
            ServiceBase.Run(ServicesToRun);
        }
    }

Es wird getroffen, Debugger.Break();wenn Sie Ihr Programm ausführen.


0

Am besten verwenden Sie den Namespace ' System.Diagnostics '.

Fügen Sie Ihren Code in den Block "if else" für den Debug-Modus und den Release-Modus ein, wie unten gezeigt, um in Visual Studio zwischen dem Debug- und dem Release-Modus zu wechseln.

#if DEBUG  // for debug mode
       **Debugger.Launch();**  //debugger will hit here
       foreach (var job in JobFactory.GetJobs())
            {
                //do something 
            }

#else    // for release mode
      **Debugger.Launch();**  //debugger will hit here
     // write code here to do something in Release mode.

#endif
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.