Wie debugge ich Windows-Dienste in Visual Studio?

84

Ist es möglich, die Windows-Dienste in Visual Studio zu debuggen?

Ich habe Code wie verwendet

System.Diagnostics.Debugger.Break();

aber es gibt einige Codefehler wie:

Ich habe zwei Ereignisfehler erhalten: eventID 4096 VsJITDebugger und "Der Dienst hat nicht rechtzeitig auf die Start- oder Steuerungsanforderung reagiert."

PawanS
quelle

Antworten:

123

Verwenden Sie den folgenden Code in der Servicemethode OnStart:

System.Diagnostics.Debugger.Launch();

Wählen Sie die Option Visual Studio aus der Popup-Nachricht.

Hinweis: Um es nur im Debug-Modus #if DEBUGzu verwenden, kann eine Compiler-Direktive wie folgt verwendet werden. Dies verhindert ein versehentliches oder Debuggen im Release-Modus auf einem Produktionsserver.

#if DEBUG
    System.Diagnostics.Debugger.Launch();
#endif
Chirag
quelle
9
Denken Sie daran, VS als Administrator auszuführen. Dann wird es auf der Liste verfügbar sein.
Michael
1
Kann jemand klarstellen, was mit der Popup-Nachricht gemeint ist? Wann / wie erscheint das?
Mike
@Mike, installiere und starte den Dienst, er wird angezeigt.
Harshit Shrivastava
@Mike ist ein Windows-Dialogfeld, das auf dem interaktiven (angemeldeten) Desktop angezeigt wird und Sie fragt, ob Sie eine App zum Debuggen des Prozesses auswählen möchten. Wenn Sie VS auswählen, wird der Debugger gestartet und an den Prozess
Chris Johnson,
62

Sie können dies auch versuchen.

  1. Erstellen Sie Ihren Windows-Dienst und installieren und starten Sie…. Das heißt, Windows-Dienste müssen auf Ihrem System ausgeführt werden.
  2. Gehen Sie während der Ausführung Ihres Dienstes zum Menü Debug und klicken Sie auf Prozess anhängen (oder Prozess in altem Visual Studio).
  3. Suchen Sie Ihren laufenden Dienst und stellen Sie sicher, dass der Prozess "Von allen Benutzern anzeigen" und " Prozesse in allen Sitzungen anzeigen" ausgewählt ist. Wenn nicht, wählen Sie ihn aus.

Geben Sie hier die Bildbeschreibung ein

  1. Klicken Sie auf die Schaltfläche Anhängen
  2. Klicken Sie auf OK
  3. Klicken Sie auf Schließen
  4. Setzen Sie einen Haltepunkt an Ihrem gewünschten Ort und warten Sie auf die Ausführung. Es wird automatisch debuggt, wenn Ihr Code diesen Punkt erreicht.
  5. Denken Sie daran, setzen Sie Ihren Haltepunkt an eine erreichbare Stelle , wenn er onStart () ist, und stoppen Sie den Dienst und starten Sie ihn erneut

(Nach langem googeln fand ich dies unter "Debuggen der Windows-Dienste in Visual Studio".)

PawanS
quelle
2
Kann diese Option in VS2013 Update 5 nicht sehen. :(
Sandeep Talabathula
1
Aber Sie müssen Ihre Vs-2017 als Administrator
ausführen
1
Ich habe es versucht. Es funktionierte mit einem Haltepunkt in onStop, aber nicht in onStart, denn wenn der Dienst beendet wird, wird der Debugger nicht mehr verbunden
KansaiRobot
22

Sie sollten den gesamten Code trennen , die wird tun Sachen aus dem Service - Projekt in ein separates Projekt, und dann eine Testanwendung, dass Sie normalerweise laufen und debuggen können.

Das Serviceprojekt wäre nur die Shell, die zur Implementierung des Serviceteils benötigt wird.

Lasse V. Karlsen
quelle
OOW !! Strg + C dann Strg + V, ich meine zu neuem Projekt. Ja, das mache nur ich. Ist es nicht möglich, einen Prozess an das Debugging oder eine andere Option anzuhängen, anstatt ein separates Projekt zu erstellen?
PawanS
1
Natürlich ist es möglich, aber es ist viel einfacher, einen Windows-Dienst zu entwickeln, wenn Sie den Serviceteil während der Entwicklung entfernen.
Lasse V. Karlsen
hmmm ... das ist ein guter Weg, aber es verdoppelt einfach die Arbeit. Ich dachte, es würde einen anderen Weg geben.
PawanS
9
Ich sehe nicht ein, wie es die Arbeit "verdoppeln" würde. Sicher, es wird ein wenig Overhead beim Erstellen des zusätzlichen Projekts und beim Aufteilen des Codes im Service auf ein drittes Projekt bedeuten, aber ansonsten würden Sie keine Kopie des Codes erstellen, sondern ihn verschieben. und verweisen Sie auf dieses Projekt.
Lasse V. Karlsen
3
^ + 1. Es verdoppelt die Arbeit zur Verwaltung des Dienstes, was in Bezug auf die Entwicklungszeit so gut wie null ist, und Sie tun dies nur einmal. Das Debuggen eines Dienstes ist SCHMERZLICH - starten Sie ihn lieber als Befehlszeile. Suchen Sie in Google nach vordefinierten Wrapper-Klassen, die dies ermöglichen (sie verwenden Reflection, um das Starten / Stoppen der Serviceklasse zu simulieren). Eine Stunde Arbeit, jede Menge Ersparnisse, Nettoverlust: negativ - Sie gewinnen Zeit.
TomTom
14

Entweder das, wie von Lasse V. Karlsen vorgeschlagen, oder Sie richten eine Schleife in Ihrem Dienst ein, die darauf wartet, dass ein Debugger angehängt wird. Das einfachste ist

while (!Debugger.IsAttached)
{
    Thread.Sleep(1000);
}

... continue with code

Auf diese Weise können Sie den Dienst starten und in Visual Studio "An Prozess anhängen ..." auswählen und an Ihren Dienst anhängen, um dann die normale Ausführung fortzusetzen.

Pauli Østerø
quelle
Wo soll ich den obigen Code einfügen ... und beim Anhängen
erhalte
3
Wir haben auch Code wie diesen verwendet if (Environment.UserInteractive) { InteractiveRun(args); } else { Service instance = new Service(); ServiceBase[] servicesToRun = new ServiceBase[] { instance }; ServiceBase.Run(servicesToRun); }
Kirill Kovalenko
Dieser Code sollte so früh wie möglich sein, bevor Code ausgeführt werden kann, den Sie debuggen möchten.
Pauli Østerø
@ Pawan: In Start/ OnStart()ich denke
abatishchev
@ Kirill: Verwenden Sie Tildes, um Code in Kommentaren hervorzuheben, zBfoo(bar)
abatishchev
7

Angesichts ServiceBase.OnStartder protectedSichtbarkeit bin ich den Reflexionsweg gegangen, um das Debuggen zu erreichen.

private static void Main(string[] args)
{
    var serviceBases = new ServiceBase[] {new Service() /* ... */ };

#if DEBUG
    if (Environment.UserInteractive)
    {
        const BindingFlags bindingFlags =
            BindingFlags.Instance | BindingFlags.NonPublic;

        foreach (var serviceBase in serviceBases)
        {
            var serviceType = serviceBase.GetType();
            var methodInfo = serviceType.GetMethod("OnStart", bindingFlags);

            new Thread(service => methodInfo.Invoke(service, new object[] {args})).Start(serviceBase);
        }

        return;
    }
#endif

    ServiceBase.Run(serviceBases);
}

Beachten Sie, dass dies Threadstandardmäßig ein Vordergrund-Thread ist. returnWenn Mainder Faux-Service-Thread ausgeführt wird, wird der Vorgang nicht beendet.

ta.speot.is
quelle
Da OnStart schnell zurückkehren soll, sollten Sie dies nicht in einem anderen Thread tun müssen. Wenn der Dienst jedoch keinen anderen Thread startet, wird Ihr Prozess sofort beendet.
Matt Connolly
@MattConnolly Bei letzterem: Falls erforderlich, ändere ich den obigen Code, um einen Vordergrund-Thread zu starten, der für immer schläft (vor der normalen Verarbeitung).
ta.speot.is
Dies sollte eine echte Antwort sein. Funktioniert wunderbar!
Lentyai
4

In einem Microsoft-Artikel wird erläutert, wie Sie hier einen Windows-Dienst debuggen und welchen Teil jeder übersehen kann, wenn er ihn durch Anhängen an einen Prozess debuggt.

Unten ist mein Arbeitscode. Ich habe den von Microsoft vorgeschlagenen Ansatz verfolgt.

Fügen Sie diesen Code hinzu zu program.cs:

static void Main(string[] args)
{
    // 'If' block will execute when launched through Visual Studio
    if (Environment.UserInteractive)
    {
        ServiceMonitor serviceRequest = new ServiceMonitor();
        serviceRequest.TestOnStartAndOnStop(args);
    }
    else // This block will execute when code is compiled as a Windows application
    {
        ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[]
        {
            new ServiceMonitor()
        };
        ServiceBase.Run(ServicesToRun);
    }
}

Fügen Sie diesen Code der ServiceMonitor-Klasse hinzu.

internal void TestOnStartAndOnStop(string[] args)
{
    this.OnStart(args);
    Console.ReadLine();
    this.OnStop();
}

Gehen Sie nun zu Projekteigenschaften , wählen Sie die Registerkarte "Anwendung" und wählen Sie beim Debuggen den Ausgabetyp als "Konsolenanwendung" oder nach dem Debuggen "Windows-Anwendung" aus, kompilieren Sie Ihren Dienst neu und installieren Sie ihn.

Geben Sie hier die Bildbeschreibung ein

Kumar Chandraketu
quelle
Gibt es eine Möglichkeit, die Ausgabe in der Debug- und Windows-Anwendung in der Version auf Konsolenanwendung festzulegen?
Kofifus
3

Sie können eine Konsolenanwendung erstellen. Ich benutze diese mainFunktion:

    static void Main(string[] args)
    {
        ImportFileService ws = new ImportFileService();
        ws.OnStart(args);
        while (true)
        {
            ConsoleKeyInfo key = System.Console.ReadKey();
            if (key.Key == ConsoleKey.Escape)
                break;
        }
        ws.OnStop();
    }

Meine ImportFileServiceKlasse ist genau die gleiche wie in der Anwendung meines Windows-Dienstes, mit Ausnahme von heiritant ( ServiceBase).

Kerrubin
quelle
ist es auf dem gleichen Projekt oder ich ein anderes Projekt für diese Konsolen-App
PawanS
Es sind 2 verschiedene Projekte mit ähnlichen Klassen. In meinem Fall handelt es sich um einen einfachen Dienst, bei dem nur die ImportFileService-Klasse doppelt vorhanden ist. Wenn ich dev / testen möchte, verwende ich consoleapp und kopiere / füge es ein. Wie Lasse V. Karlsen sagte, es ist ein Debug-Programm, die gesamte Logik (Geschäft) ist auf einem dritten Projekt.
Kerrubin
Ist OnStart nicht geschützt?
Joe Phillips
Ja ist. Deshalb habe ich "außer dem Erben ( ServiceBase)" gesagt . Ich finde es einfacher, in einer Konsolen-App zu debuggen, aber ich verstehe, wenn das nicht alle überzeugt.
Kerrubin
2

Sie können auch System.Diagnostics.Debugger.Launch () ausprobieren. Methode . Es hilft dabei, den Debugger-Zeiger auf den angegebenen Speicherort zu bringen, und Sie können dann Ihren Code debuggen.

Vor diesem Schritt installieren Sie bitte Ihre service.exe über die Befehlszeile der Visual Studio-Eingabeaufforderung - installutil projectservice.exe

Starten Sie dann Ihren Dienst über die Systemsteuerung -> Verwaltung -> Computerverwaltung -> Dienst und Anwendung -> Dienste -> Ihr Dienstname

Abhishek Srivastava
quelle
2

Ich habe diesen Code gerade zu meiner Serviceklasse hinzugefügt, damit ich OnStart indirekt aufrufen kann, ähnlich wie bei OnStop.

    public void MyOnStart(string[] args)
    {
        OnStart(args);
    }
RichardHowells
quelle
2

Ich verwende den /ConsoleParameter im Visual Studio-Projekt DebugStartoptionenBefehlszeilenargumente :

public static class Program
{
    [STAThread]
    public static void Main(string[] args)
    {
         var runMode = args.Contains(@"/Console")
             ? WindowsService.RunMode.Console
             : WindowsService.RunMode.WindowsService;
         new WinodwsService().Run(runMode);
    }
}


public class WindowsService : ServiceBase
{
    public enum RunMode
    {
        Console,
        WindowsService
    }

    public void Run(RunMode runMode)
    {
        if (runMode.Equals(RunMode.Console))
        {
            this.StartService();
            Console.WriteLine("Press <ENTER> to stop service...");
            Console.ReadLine();

            this.StopService();
            Console.WriteLine("Press <ENTER> to exit.");
            Console.ReadLine();
        }
        else if (runMode.Equals(RunMode.WindowsService))
        {
            ServiceBase.Run(new[] { this });
        }
    }

    protected override void OnStart(string[] args)
    {
        StartService(args);
    }

    protected override void OnStop()
    {
        StopService();
    }

    /// <summary>
    /// Logic to Start Service
    /// Public accessibility for running as a console application in Visual Studio debugging experience
    /// </summary>
    public virtual void StartService(params string[] args){ ... }

    /// <summary>
    /// Logic to Stop Service
    /// Public accessibility for running as a console application in Visual Studio debugging experience
    /// </summary>
    public virtual void StopService() {....}
}
Sean M.
quelle
2

Ich habe diese Frage gefunden, aber ich denke, eine klare und einfache Antwort fehlt.

Ich möchte meinen Debugger nicht an einen Prozess anhängen, möchte aber dennoch den Dienst OnStartund die OnStopMethoden aufrufen können. Ich möchte auch, dass es als Konsolenanwendung ausgeführt wird, damit ich Informationen von NLog auf einer Konsole protokollieren kann .

Ich habe diese brillanten Anleitungen gefunden, die dies tun:

Ändern Sie zunächst die Projekte Output typein Console Application.

Geben Sie hier die Bildbeschreibung ein

Ändern Sie Ihre Program.cs, um so auszusehen:

static class Program
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    static void Main()
    {
        // Startup as service.
        ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[]
        {
            new Service1()
        };

        if (Environment.UserInteractive)
        {
            RunInteractive(ServicesToRun);
        }
        else
        {
            ServiceBase.Run(ServicesToRun);
        }
    }
}

Fügen Sie dann die folgende Methode hinzu, damit Dienste im interaktiven Modus ausgeführt werden können.

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);
}
Ogglas
quelle
Großartiger Code! Einfach, effektiv. +1. Aber genauso einfach habe ich dies zu einer Forms-App gemacht. Ich hasse Konsolen-Apps wirklich. Sie können auch einfach eine Formularschaltfläche für jedes Serviceereignis implementieren.
Roland
2

Ich verwende ein großartiges Nuget-Paket namens ServiceProcess.Helpers.

Und ich zitiere...

Es unterstützt das Debuggen von Windows-Diensten, indem es eine Benutzeroberfläche zum Abspielen / Stoppen / Anhalten erstellt, wenn ein Debugger angeschlossen ist, ermöglicht jedoch auch die Installation und Ausführung des Dienstes in der Windows Server-Umgebung.

All dies mit einer Codezeile.

http://windowsservicehelper.codeplex.com/

Nach der Installation und Verkabelung müssen Sie lediglich Ihr Windows-Dienstprojekt als Startprojekt festlegen und auf Ihrem Debugger auf Start klicken.

Christophe Chang
quelle
Danke für das Teilen! Es war bei weitem die einfachste Lösung!
Wellington Zanelli
1

Wenn Sie zu Beginn eines Windows-Dienstvorgangs versuchen, etwas zu debuggen, funktioniert das "Anhängen" an den laufenden Prozess leider nicht. Ich habe versucht, Debugger.Break () in der OnStart-Prozedur zu verwenden, aber mit einer 64-Bit-kompilierten Visual Studio 2010-Anwendung gibt der Befehl break nur einen Fehler wie den folgenden aus:

System error 1067 has occurred.

Zu diesem Zeitpunkt müssen Sie in Ihrer Registrierung die Option "Image File Execution" für Ihre ausführbare Datei einrichten. Die Einrichtung dauert fünf Minuten und funktioniert sehr gut. Hier ist ein Microsoft-Artikel mit folgenden Details:

Gewusst wie: Starten Sie den Debugger automatisch

Brian
quelle
1

Probieren Sie die eigene Post-Build-Ereignisbefehlszeile von Visual Studio aus .

Versuchen Sie dies im Post-Build hinzuzufügen:

@echo off
sc query "ServiceName" > nul
if errorlevel 1060 goto install
goto stop

:delete
echo delete
sc delete "ServiceName" > nul
echo %errorlevel%
goto install

:install
echo install
sc create "ServiceName" displayname= "Service Display Name" binpath= "$(TargetPath)" start= auto > nul
echo %errorlevel%
goto start

:start
echo start
sc start "ServiceName" > nul
echo %errorlevel%
goto end

:stop
echo stop
sc stop "ServiceName" > nul
echo %errorlevel%
goto delete

:end

Wenn der Build-Fehler mit einer Meldung wie Error 1 The command "@echo off sc query "ServiceName" > nulso weiter, Ctrl+ Cdann Ctrl+V die Fehlermeldung in Notepad und schauen Sie sich den letzten Satz der Nachricht an.

Es könnte heißen exited with code x. Suchen Sie hier nach dem Code in einem häufigen Fehler und sehen Sie, wie Sie ihn beheben können.

1072 -- Marked for deletion  Close all applications that maybe using the service including services.msc and Windows event log.
1058 -- Can't be started because disabled or has no enabled associated devices  just delete it.
1060 -- Doesn't exist  just delete it.
1062 -- Has not been started  just delete it.
1053 -- Didn't respond to start or control  see event log (if logged to event log). It may be the service itself throwing an exception.
1056 -- Service is already running  stop the service, and then delete.

Mehr zu Fehlercodes hier .

Und wenn der Build-Fehler mit Meldung wie dieser,

Error    11    Could not copy "obj\x86\Debug\ServiceName.exe" to "bin\Debug\ServiceName.exe". Exceeded retry count of 10. Failed.    ServiceName
Error    12    Unable to copy file "obj\x86\Debug\ServiceName.exe" to "bin\Debug\ServiceName.exe". The process cannot access the file 'bin\Debug\ServiceName.exe' because it is being used by another process.    ServiceName

Öffnen Sie cmd und versuchen Sie dann, es zuerst mit zu töten taskkill /fi "services eq ServiceName" /f

Wenn alles F5in Ordnung ist, sollte es ausreichen, um es zu debuggen.

asakura89
quelle
0

Führen Sie in der OnStartMethode die folgenden Schritte aus.

protected override void OnStart(string[] args)
{
    try
    {
        RequestAdditionalTime(600000);
        System.Diagnostics.Debugger.Launch(); // Put breakpoint here.

        .... Your code
    }
    catch (Exception ex)
    {
        .... Your exception code
    }
}

Führen Sie dann eine Eingabeaufforderung als Administrator aus und geben Sie Folgendes ein:

c:\> sc create test-xyzService binPath= <ProjectPath>\bin\debug\service.exe type= own start= demand

In der obigen Zeile wird test-xyzService in der Serviceliste erstellt.

Um den Dienst zu starten, werden Sie aufgefordert, eine Verbindung zum Debüt in Visual Studio herzustellen oder nicht.

c:\> sc start text-xyzService

So beenden Sie den Dienst:

c:\> sc stop test-xyzService

So löschen oder deinstallieren Sie:

c:\> sc delete text-xyzService
user3942119
quelle
0

Debuggen Sie einen Windows-Dienst über http (getestet mit VS 2015 Update 3 und .Net FW 4.6).

Zunächst müssen Sie in Ihrer VS-Lösung ein Konsolenprojekt erstellen (Hinzufügen -> Neues Projekt -> Konsolenanwendung).

Erstellen Sie im neuen Projekt eine Klasse "ConsoleHost" mit diesem Code:

class ConsoleHost : IDisposable
{
    public static Uri BaseAddress = new Uri(http://localhost:8161/MyService/mex);
    private ServiceHost host;

    public void Start(Uri baseAddress)
    {
        if (host != null) return;

        host = new ServiceHost(typeof(MyService), baseAddress ?? BaseAddress);

        //binding
        var binding = new BasicHttpBinding()
        {
            Name = "MyService",
            MessageEncoding = WSMessageEncoding.Text,
            TextEncoding = Encoding.UTF8,
            MaxBufferPoolSize = 2147483647,
            MaxBufferSize = 2147483647,
            MaxReceivedMessageSize = 2147483647
        };

        host.Description.Endpoints.Clear();
        host.AddServiceEndpoint(typeof(IMyService), binding, baseAddress ?? BaseAddress);

        // Enable metadata publishing.
        var smb = new ServiceMetadataBehavior
        {
            HttpGetEnabled = true,
            MetadataExporter = { PolicyVersion = PolicyVersion.Policy15 },
        };

        host.Description.Behaviors.Add(smb);

        var defaultBehaviour = host.Description.Behaviors.OfType<ServiceDebugBehavior>().FirstOrDefault();
        if (defaultBehaviour != null)
        {
            defaultBehaviour.IncludeExceptionDetailInFaults = true;
        }

        host.Open();
    }

    public void Stop()
    {
        if (host == null)
            return;

        host.Close();
        host = null;
    }

    public void Dispose()
    {
        this.Stop();
    }
}

Und dies ist der Code für die Program.cs-Klasse:

public static class Program
{
    [STAThread]
    public static void Main(string[] args)
    {
        var baseAddress = new Uri(http://localhost:8161/MyService);
        var host = new ConsoleHost();
        host.Start(null);
        Console.WriteLine("The service is ready at {0}", baseAddress);
        Console.WriteLine("Press <Enter> to stop the service.");
        Console.ReadLine();
        host.Stop();
    }
}

Konfigurationen wie Verbindungszeichenfolgen sollten in die Datei App.config des Konsolenprojekts kopiert werden.

Um die Konsole zu öffnen, klicken Sie mit der rechten Maustaste auf Konsolenprojekt und klicken Sie auf Debuggen -> Neue Instanz starten.

mggSoft
quelle
0

Fügen Sie Ihrer Serviceklasse einfach einen Konstruktor hinzu (falls Sie ihn noch nicht haben). Unten können Sie ein Beispiel für Visual Basic .net finden.

Public Sub New()
   OnStart(Nothing) 
End Sub

Klicken Sie anschließend mit der rechten Maustaste auf das Projekt und wählen Sie " Debuggen -> Neue Instanz starten ".

Pedro Martín
quelle