Łatwiejszy sposób na debugowanie usługi Windows

325

Czy istnieje prostszy sposób na przejście przez kod niż uruchomienie usługi za pomocą Menedżera kontroli usług systemu Windows, a następnie dołączenie debugera do wątku? To trochę kłopotliwe i zastanawiam się, czy istnieje bardziej proste podejście.

Matthias
źródło
Stworzyłem ten bilet User Voice. Rozważ głosowanie na to: visualstudio.uservoice.com/forums/121579-visual-studio-ide/...
David,

Odpowiedzi:

271

Jeśli chcę szybko debugować usługę, po prostu wpadam Debugger.Break()tam. Kiedy ta linia zostanie osiągnięta, przeniesie mnie z powrotem do VS. Nie zapomnij usunąć tej linii, gdy skończysz.

AKTUALIZACJA: Alternatywnie do #if DEBUGpragmatów możesz także użyć Conditional("DEBUG_SERVICE")atrybutu.

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

Na swoim komputerze OnStartpo prostu wywołaj tę metodę:

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

Tam kod będzie włączony tylko podczas kompilacji debugowania. Na razie przydatne może być utworzenie osobnej konfiguracji kompilacji do debugowania usługi.

jop
źródło
45
Lub możesz użyć Debugger.Launch (), musisz dołączyć instrukcję using dla przestrzeni nazw Systems.Diagnostics.
Omar Kooheji,
1
Twój post na blogu działał dobrze i uratował mi dzień :) jednak Debugger.Break () nie zadziałał dla mnie. wydaje się, że .Net pomija funkcję DebugMode z pewnych powodów związanych z optymalizacją.
Bizhan,
3
Debugger.Launch () działa dla mnie, gdy Debugger.Break () nie działa. (Proces kończy się kodem 255.)
Oliver Bock
Jak sobie radzicie? Nic się nie dzieje. Próbowałem Break () i Launch ().
4th Space
13
@ 4thSpace: 1. utwórz instalator usługi, aby móc zainstalować usługę. 2. Dodaj wiersz Debugger.Launch (); na początku twojej Main (). 3. Zbuduj swój kod w trybie debugowania. 4. Zastąp zainstalowane biblioteki DLL bibliotekami debugowania. 5. Uruchom usługę z panelu usług systemu Windows. Teraz pojawia się okienko z prośbą o dołączenie do debuggera. Ten sposób działał dla mnie. Mam nadzieję, że również dla ciebie.
ffonz
210

Myślę też, że posiadanie osobnej „wersji” do normalnego wykonywania i jako usługa jest właściwą drogą, ale czy naprawdę jest to konieczne, aby dedykować do tego celu oddzielny przełącznik wiersza poleceń?

Czy nie możesz po prostu zrobić:

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

Miałoby to tę zaletę, że można po prostu uruchomić aplikację za pomocą dwukrotnego kliknięcia (OK, jeśli naprawdę tego potrzebujesz) i że możesz po prostu nacisnąć F5Visual Studio (bez potrzeby modyfikowania ustawień projektu, aby uwzględnić tę /consoleopcję).

Z technicznego punktu widzenia Environment.UserInteractivesprawdza, czy WSF_VISIBLEflaga jest ustawiona dla bieżącej stacji okien, ale czy jest jakiś inny powód, dla którego wróciłaby false, poza uruchomieniem jej jako (nieinteraktywna) usługa?

Christian.K
źródło
Świetny! Wcześniej używałem metody „if #debug”, aby uruchamiać się jako aplikacja podczas debugowania, w przeciwnym razie usługa. Prowadzi to do tego, że aplikacja nie może być uruchamiana jako usługa, jeśli chcesz ją debugować, ale twoje rozwiązanie rozwiązuje ten problem i pozwala na uruchamianie we wszystkich czterech kombinacjach usługa / aplikacja i wydanie / debugowanie.
Jonas
29
Jeśli nie chcesz, aby program był uruchamiany po dwukrotnym kliknięciu (użytkownicy mogą się pomylić i uruchomić kilka instancji itp.), Możesz użyć System.Diagnostics.Debugger.IsAttachedzamiast tego Environment.UserInteractive.
Blorgbeard jest dostępny
5
ale czy jest jakiś inny powód, dla którego zwróciłby wartość false, poza uruchomieniem go jako (nieinteraktywna) usługa? Mogę wymyślić jedno: zaplanowane zadanie, które nie jest dołączane do konsoli.
Hogan
7
W tym przypadku używam parametrów wiersza polecenia. --install, aby zainstalować usługę, --uninstall, aby odinstalować usługę i --interactive, aby uruchomić usługę jako aplikację. dodałem --interactive do opcji projektu (Debugowanie> Argumenty poleceń). Więc mogę łatwo debugować z VS. podwójne kliknięcie nie utworzy niepożądanej działającej instancji, ponieważ wymagana jest opcja --interactive. tylko moje 2 centy.
Emir Akaydın
@ EmirAkaydın Tak, tak naprawdę mam parametry wiersza polecenia również jako „kopię zapasową”. Jednak tak naprawdę chciałem mieć „interaktywną” instancję podczas podwójnego kliknięcia, a nie i komunikat o błędzie dotyczący faktu, że usługi nie można uruchomić w ten sposób. Chyba różne cele ;-)
Christian.K
123

Kiedy kilka tygodni temu założyłem nowy projekt serwisowy, znalazłem ten post. Chociaż istnieje wiele świetnych sugestii, nadal nie znalazłem rozwiązania, które chciałem: możliwość wywoływania klas usług OnStarti OnStopmetod bez żadnych modyfikacji klas usług.

Rozwiązanie, które wymyśliłem, wykorzystuje Environment.Interactivewybrany tryb uruchamiania, jak sugerują inne odpowiedzi na ten post.

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

RunInteractivePomocnik wykorzystuje odbicia zadzwonić chronionych OnStarti OnStopmetod:

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

To jest cały wymagany kod, ale napisałem też przewodnik z objaśnieniami.

Anders Abel
źródło
To dobra metoda rozszerzenia dla ServiceBase []. Mam wiele usług w moim rozwiązaniu, więc zamiast wspólnej klasy bazowej dla Program.cs, po prostu wywołuję servicesToRun.RunInteractive (args). Ładne rozwiązanie @Anders!
David Keaveny
3
Rzeczywiście świetne rozwiązanie. Stworzyłem proste rozszerzenie ServiceBase [], jak sugerował David, które pozwala uruchamiać usługi w jednym wierszu kodu: pastebin.com/F0fhhG2R
Funbit
4
+1 Mój były kolega stworzył klasę bazową „EasyRunService” (która dziedziczy ServiceProcess), która robi prawie to samo, ale bez potrzeby refleksji (ponieważ OnStart jest teraz w klasie podstawowej). Naprawdę ułatwia debugowanie usługi Windows.
sondergard
3
@ Chazt3n Upewnij się, że typem wyjściowym projektu jest „Aplikacja konsoli”. Jeśli chodzi o instalację usługi, nie ma znaczenia, który typ wyjścia zostanie wybrany, zachowanie jest takie samo.
Funbit
2
Nadal świetne rozwiązanie! Jedyne, co chciałbym dodać (jak pokazano walk through), to upewnić się, że przejdziesz do właściwości projektu i zmienisz typ danych wyjściowych, Console Applicationzanim spróbujesz skompilować i uruchomić. Znajdź to na Project Properties -> Application -> Output type -> Console Application. Ponadto, aby działało to poprawnie dla mnie, musiałem uruchomić aplikację za pomocą startpolecenia. Np .: C:\"my app name.exe" -servicenie działałoby dla mnie. Zamiast tego użyłemC:\start /wait "" "my app name.exe" -service
Arvo Bowen,
47

Czasami ważna jest analiza tego, co dzieje się podczas uruchamiania usługi. Dołączanie do procesu nie pomaga tutaj, ponieważ nie jesteś wystarczająco szybki, aby dołączyć debugger podczas uruchamiania usługi.

Krótka odpowiedź brzmi: używam do tego następujących 4 linii kodu :

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

Są one wstawiane do OnStartmetody usługi w następujący sposób:

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

Dla tych, którzy wcześniej tego nie robili, poniżej zamieściłem szczegółowe wskazówki , ponieważ możesz łatwo utknąć. Poniższe wskazówki odnoszą się do systemu Windows 7x64 i Visual Studio 2010 Team Edition , ale powinny obowiązywać również w innych środowiskach.


Ważne: Wdróż usługę w trybie „ręcznym” (za pomocą InstallUtilnarzędzia z wiersza polecenia VS lub uruchom przygotowany projekt instalatora usługi). Otwórz program Visual Studio przed uruchomieniem usługi i załaduj rozwiązanie zawierające kod źródłowy usługi - skonfiguruj dodatkowe punkty przerwania zgodnie z wymaganiami w programie Visual Studio - a następnie uruchom usługę za pomocą panelu sterowania usługi.

Z powodu Debugger.Launchkodu spowoduje to wyświetlenie okna dialogowego „Wystąpił nieobsługiwany wyjątek Microsoft .NET Framework w pliku Servicename.exe ”. pojawić się. Kliknij, jak pokazano na zrzucie ekranu:Elevate Yes, debug Servicename.exe
FrameworkException

Następnie, szczególnie w UAC systemu Windows 7, może pojawić się monit o podanie poświadczeń administratora. Wpisz je i kontynuuj Yes:

UACPrompt

Następnie pojawi się dobrze znane okno debugera Visual Studio Just-In-Time . Pyta, czy chcesz debugować przy użyciu usuniętego debugera. Zanim klikniesz Yes, zaznaczyć, że nie chcą, aby otworzyć nową instancję (2 opcja) - nowa instancja nie będzie tu pomocny, ponieważ nie będzie wyświetlony kod źródłowy. Zamiast tego wybierasz wcześniej otwartą instancję programu Visual Studio: VSDebuggerPrompt

Po kliknięciu Yes, po chwili Visual Studio pojawi się żółta strzałka prawo w wierszu, w którym Debugger.Launchoświadczenie to i jesteś w stanie do debugowania kodu (metoda MyInitOnStart, która zawiera swój inicjalizacji). VSDebuggerBreakpoint

Naciśnięcie F5kontynuuje wykonywanie natychmiast, dopóki nie zostanie osiągnięty następny przygotowany punkt przerwania.

Wskazówka: Aby usługa działała, wybierz Debuguj -> Odłącz wszystko . Umożliwia to uruchomienie klienta komunikującego się z usługą po prawidłowym uruchomieniu i zakończeniu debugowania kodu startowego. Jeśli naciśniesz Shift+F5 (zatrzymaj debugowanie), spowoduje to zakończenie usługi. Zamiast tego należy użyć panelu sterowania usługami, aby go zatrzymać.

Zauważ, że

  • Jeśli budować wydania, a następnie kod debugowania jest automatycznie usuwany , a usługa działa normalnie.

  • Używam Debugger.Launch(), który uruchamia i dołącza debugger . Testowałem Debugger.Break()również, co nie zadziałało , ponieważ nie ma jeszcze podłączonego debugera podczas uruchamiania usługi (powodując „Błąd 1067: Proces został nieoczekiwanie zakończony” ).

  • RequestAdditionalTimeustawia dłuższy czas oczekiwania na uruchomienie usługi ( nie opóźnia samego kodu, ale natychmiast kontynuuje Debugger.Launchinstrukcję). W przeciwnym razie domyślny limit czasu uruchomienia usługi jest zbyt krótki, a uruchomienie usługi kończy się niepowodzeniem, jeśli nie zadzwonisz base.Onstart(args)wystarczająco szybko z debuggera. Praktycznie przekroczenie limitu czasu wynoszące 10 minut pozwala uniknąć wyświetlenia komunikatu „ usługa nie odpowiedziała ...” natychmiast po uruchomieniu debugera.

  • Gdy się przyzwyczaisz, ta metoda jest bardzo łatwa, ponieważ wymaga tylko dodania 4 linii do istniejącego kodu usługi, co pozwala szybko uzyskać kontrolę i debugować.

Matt
źródło
1
Z ciekawości wiesz, czy jest limit czasu na interakcję użytkownika z pytaniem użytkownika Debugger.Launch ()?
Shiv
1
Jak opisano, base.RequestAdditionalTime(600000)zapobiegnie zakończeniu usługi przez kontrolę usługi na 10 minut, jeśli nie zadzwoni base.OnStart(args)w tym czasie). Poza tym pamiętam, że UAC przerwie również, jeśli po pewnym czasie nie wprowadzisz poświadczeń administratora (nie wiem dokładnie, ile sekund, ale myślę, że musisz go wprowadzić w ciągu minuty, w przeciwnym razie UAC przerwie) , co zakończy sesję debugowania.
Matt
2
Uważam, że jest to najlepsza metoda debugowania wiadomości CustomCommand. +1.
Justin,
40

To, co zwykle robię, to umieszczenie logiki usługi w osobnej klasie i rozpoczęcie od klasy „biegacza”. Ta klasa runner może być faktyczną usługą lub tylko aplikacją konsoli. Twoje rozwiązanie ma (co najmniej) 3 projekty:

/ConsoleRunner
   /....
/ServiceRunner
   /....
/ApplicationLogic
   /....
Paul van Brenk
źródło
1
Kiedyś używałem tego podejścia, ale myślę, że połączenie tego z powyższą odpowiedzią działa.
RobS
27

Ten film Fabio Scopela z YouTube wyjaśnia, jak całkiem ładnie debugować usługę Windows ... rzeczywista metoda wykonania tego zaczyna się o 4:45 w filmie ...

Oto kod wyjaśniony w filmie ... w pliku Program.cs dodaj elementy do sekcji Debuguj ...

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

        }
    }
}

W pliku Service1.cs dodaj metodę OnDebug () ...

    public Service1()
    {
        InitializeComponent();
    }

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

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

    protected override void OnStop()
    {
    }

Jak to działa

Zasadniczo musisz utworzyć, public void OnDebug()który wywołuje, OnStart(string[] args)ponieważ jest chroniony i niedostępny na zewnątrz. void Main()Program dodaje się #ifpreprocesora z #DEBUG.

Program Visual Studio określa, DEBUGczy projekt jest kompilowany w trybie debugowania. Pozwoli to na uruchomienie sekcji debugowania (poniżej), gdy warunek jest spełniony

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

Będzie działał jak aplikacja konsoli, gdy wszystko pójdzie dobrze, możesz zmienić tryb, Releasea zwykła elsesekcja uruchomi logikę

Jason Miller
źródło
Szukałem tej odpowiedzi, nie wiem, dlaczego była tak nisko oceniana. Wyjaśnił kod, aby pomóc innym lub ewentualnie więcej komentarzy;)
Vinod Srivastav
14

AKTUALIZACJA

To podejście jest zdecydowanie najłatwiejsze:

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

Pierwszą odpowiedź zostawiam poniżej dla potomności.


Moje usługi zwykle mają klasę, która obudowuje Timer, ponieważ chcę, aby usługa regularnie sprawdzała, czy jest jakaś praca do wykonania.

Odnawiamy klasę i wywołujemy StartEventLoop () podczas uruchamiania usługi. (Tej klasy można łatwo użyć również z aplikacji konsoli).

Przyjemnym efektem ubocznym tego projektu jest to, że argumenty, przy pomocy których konfigurujesz Timer, mogą mieć opóźnienie, zanim usługa faktycznie zacznie działać, abyś miał czas na ręczne podłączenie debuggera.

ps Jak ręcznie podłączyć debugger do uruchomionego procesu ...?

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

Robiłem też następujące (już wspomniane w poprzednich odpowiedziach, ale z flagami kompilatora warunkowego [#if], aby uniknąć uruchamiania się w kompilacji wydania).

Przestałem to robić w ten sposób, ponieważ czasami zapominaliśmy o wbudowaniu w Release i mieliśmy przerwę w debugowaniu w aplikacji działającej na demie klienta (zawstydzające!).

#if DEBUG
if (!System.Diagnostics.Debugger.IsAttached)
{
    System.Diagnostics.Debugger.Break();
}
#endif
rohancragg
źródło
Co się stanie, gdy wykonanie // do somethingzajmie więcej niż 30 minut?
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
}
Thomas Bratt
źródło
[Przepraszamy za brak wyjaśnień dotyczących problemów z kodowaniem] Powinien działać normalnie z MS Visual Studio (F5) w kompilacjach debugowania. Nadal działa jako normalna usługa w kompilacjach wersji.
Thomas Bratt
Połącz to z powyższym rozwiązaniem autorstwa Christiana K., aby użyć właściwości „Environment.UserInteractive”, a rozwiązanie jest naprawdę czyste i proste.
Ben Robbins,
OnStartjest protectedi nie można modyfikować poziomu dostępu :(
Eduard Luca
10

Możesz także uruchomić usługę za pomocą wiersza polecenia (sc.exe).

Osobiście uruchomiłbym ten kod jako samodzielny program w fazie debugowania, a kiedy większość błędów zostanie usunięta, przejdź do uruchamiania jako usługa.

akauppi
źródło
10

Zwykłem mieć przełącznik linii poleceń, który uruchamiałby program albo jako usługę, albo jako zwykłą aplikację. Następnie w moim IDE ustawiłbym przełącznik, aby móc przejść przez mój kod.

W przypadku niektórych języków można faktycznie wykryć, czy działa on w środowisku IDE, i wykonać tę zmianę automatycznie.

Jakiego języka używasz?

RB.
źródło
9

Użyj biblioteki TopShelf .

Utwórz aplikację konsoli, a następnie skonfiguruj konfigurację w swoim 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()
        {

        }
    }

Aby debugować swoją usługę, po prostu naciśnij F5 w studiu wizualnym.

Aby zainstalować usługę, wpisz polecenie cmd „console.exe install”

Następnie można uruchomić i zatrzymać usługę w menedżerze usług systemu Windows.

Misterhex
źródło
Ich licencje były zbyt mylące, aby zrozumieć
ja - ... - ”-„ ”„
Używają afaik licencji Apache. Topshelf to najprostszy sposób, w jaki użyłem do opracowania i debugowania usług Windows. Bardzo łatwy w użyciu. Rozwijaj jako aplikacja konsolowa. Zainstaluj jako usługę z jednym przełącznikiem wiersza poleceń. Wysoce polecany.
okrada
TopShelf zaoszczędził mi mnóstwo czasu. Thx
L_7337
8

Myślę, że to zależy od używanego systemu operacyjnego. Vista jest znacznie trudniejsza do przyłączenia się do usług z powodu rozdzielenia sesji.

Dwie opcje, z których korzystałem w przeszłości to:

  • Użyj GFlags (w Narzędziach do debugowania dla systemu Windows), aby skonfigurować stały debugger dla procesu. Istnieje w kluczu rejestru „Opcje wykonywania pliku obrazu” i jest niezwykle przydatny. Myślę, że musisz dostosować ustawienia usługi, aby włączyć „Interakcja z komputerem”. Używam tego do wszystkich rodzajów debugowania, nie tylko usług.
  • Inną opcją jest nieco oddzielenie kodu, aby część serwisowa była wymienna z normalnym uruchomieniem aplikacji. W ten sposób możesz użyć prostej flagi wiersza poleceń i uruchomić jako proces (zamiast usługi), co znacznie ułatwia debugowanie.

Mam nadzieję że to pomoże.

RichS
źródło
+1 dla GFlags. Jest to szczególnie przydatne, jeśli nie możesz zmodyfikować kodu źródłowego (lub jeśli go nie masz).
Chris Gillum,
6

Lubię móc debugować każdy aspekt mojej usługi, w tym każdą inicjalizację w OnStart (), jednocześnie wykonując ją przy zachowaniu pełnej usługi w ramach SCM ... bez trybu „konsoli” lub „aplikacji”.

Robię to, tworząc drugą usługę w tym samym projekcie, która ma być używana do debugowania. Usługa debugowania, uruchomiona jak zwykle (tj. We wtyczce MMC usług), tworzy proces hosta usługi. Daje to proces dołączania debugera, nawet jeśli jeszcze nie uruchomiłeś swojej prawdziwej usługi. Po dołączeniu debugera do procesu uruchom swoją prawdziwą usługę i możesz włamać się do niej w dowolnym miejscu w cyklu życia usługi, w tym OnStart ().

Ponieważ wymaga bardzo minimalnego włamania do kodu, usługa debugowania może łatwo zostać włączona do projektu konfiguracji usługi i łatwo można ją usunąć z wersji produkcyjnej, komentując jeden wiersz kodu i usuwając instalator jednego projektu.

Detale:

1) Zakładając, że wdrażasz MyService, również stwórz MyServiceDebug. Dodaj oba do ServiceBasetablicy w następujący Program.cssposób:

    /// <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) Dodaj prawdziwą usługę ORAZ usługę debugowania do instalatora projektu dla projektu usługi:

wprowadź opis zdjęcia tutaj

Obie usługi (rzeczywista i debugowana) zostają uwzględnione po dodaniu danych wyjściowych projektu usługi do projektu instalacji dla usługi. Po instalacji obie usługi pojawią się we wtyczce MMC service.msc.

3) Uruchom usługę debugowania w MMC.

4) W Visual Studio dołącz debugger do procesu rozpoczętego przez usługę debugowania.

5) Uruchom prawdziwą usługę i ciesz się debugowaniem.

BitMask777
źródło
5

Kiedy piszę usługę, umieszczam całą logikę usługi w projekcie dll i tworzę dwa „hosty”, które wywołują tę bibliotekę dll, jeden to usługa Windows, a drugi to aplikacja wiersza poleceń.

Używam aplikacji z wiersza poleceń do debugowania i dołączam debugera do prawdziwej usługi tylko dla błędów, których nie mogę odtworzyć w aplikacji z wiersza poleceń.

Używam tego podejścia, pamiętaj tylko, że musisz przetestować cały kod podczas pracy w prawdziwej usłudze, podczas gdy narzędzie wiersza poleceń jest niezłą pomocą przy debugowaniu, to jest inne środowisko i nie zachowuje się dokładnie tak, jak prawdziwa usługa.

Nir
źródło
4

Podczas opracowywania i debugowania usługi Windows zazwyczaj uruchamiam ją jako aplikację konsolową, dodając parametr startowy / console i sprawdzając to. Ułatwia życie.

static void Main(string[] args) {
    if (Console.In != StreamReader.Null) {
        if (args.Length > 0 && args[0] == "/console") {
            // Start your service work.
        }
    }
}
Maurice
źródło
Do momentu debugowania określonych problemów związanych z usługą.
leppie
To prawda, że ​​musisz dołączyć debugger do rzeczywistego procesu obsługi. Ale w większości przypadków błędy pojawią się w obu kierunkach, a rozwój jest znacznie łatwiejszy.
Maurice
4

Co powiesz na Debugger.Break () w pierwszym wierszu?

leppie
źródło
2

Aby debugować usługi Windows, łączę GFlagi i plik .reg utworzony przez regedit.

  1. Uruchom GFlags, podając nazwę exe i vsjitdebugger
  2. Uruchom regedit i przejdź do lokalizacji, w której GFlags ustawia swoje opcje
  3. Wybierz „Eksportuj klucz” z menu pliku
  4. Zapisz ten plik gdzieś z rozszerzeniem .reg
  5. Za każdym razem, gdy chcesz debugować usługę: kliknij dwukrotnie plik .reg
  6. Jeśli chcesz zatrzymać debugowanie, kliknij dwukrotnie drugi plik .reg

Lub zapisz następujące fragmenty i zamień servicename.exe na żądaną nazwę pliku wykonywalnego.


debugon.reg:

Edytor rejestru systemu Windows w wersji 5.00

[HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ Windows NT \ CurrentVersion \ Image File Execution Options \ servicename.exe]
„GlobalFlag” = „0x00000000”
„Debugger” = „vsjitdebugger.exe”

debugoff.reg:

Edytor rejestru systemu Windows w wersji 5.00

[HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ Windows NT \ CurrentVersion \ Image File Execution Options \ servicename.exe]
„GlobalFlag” = „0x00000000”

źródło
Czy to nadal działa w Win 7 / Win 2008? To podejście z support.microsoft.com/kb/824344, ale opiera się na usługach interaktywnych, a myślałem, że zostali zabici? Zawsze była moją preferowaną opcją (ponieważ problemy z uruchamianiem mogą pojawiać się w środowisku produkcyjnym, gdzie wstawienie Debugger.Break () do kodu może nie być opcją).
mola7
1

W przypadku rutynowego programowania małych rzeczy zrobiłem bardzo prostą sztuczkę, aby łatwo debugować moją usługę:

Na początku usługi sprawdzam parametr wiersza polecenia „/ debug”. Jeśli usługa jest wywoływana z tym parametrem, nie wykonuję zwykłego uruchamiania usługi, ale zamiast tego uruchamiam wszystkie nasłuchiwania i po prostu wyświetlam komunikat „Trwa debugowanie, naciśnij OK, aby zakończyć”.

Więc jeśli moja usługa zostanie uruchomiona w zwykły sposób, uruchomi się jako usługa, jeśli zostanie uruchomiona z parametrem / debugowaniem w wierszu poleceń, będzie działać jak normalny program.

W VS dodam / debuguję jako parametr debugowania i bezpośrednio uruchomię program serwisowy.

W ten sposób mogę łatwo debugować większość małych problemów. Oczywiście niektóre rzeczy nadal będą wymagały debugowania jako usługi, ale dla 99% jest to wystarczająco dobre.

Sam
źródło
1
#if DEBUG
    System.Diagnostics.Debugger.Break();
#endif
Ervin Ter
źródło
1

Używam wariantu odpowiedzi JOP. Za pomocą parametrów wiersza poleceń można ustawić tryb debugowania w środowisku IDE z właściwościami projektu lub za pomocą menedżera usług systemu Windows.

protected override void OnStart(string[] args)
{
  if (args.Contains<string>("DEBUG_SERVICE"))
  {
    Debugger.Break();
  }
  ...
}
nh99
źródło
1

Wystarczy umieścić lunch debuggera w dowolnym miejscu i dołączyć program Visualstudio podczas uruchamiania

#if DEBUG
    Debugger.Launch();
#endif

Musisz także uruchomić VS jako administrator i zezwolić, aby proces mógł być automatycznie debugowany przez innego użytkownika (jak wyjaśniono tutaj ):

reg add "HKCR\AppID{E62A7A31-6025-408E-87F6-81AEB0DC9347}" /v AppIDFlags /t REG_DWORD /d 8 /f
wotanii
źródło
1

Użyj projektu szablonu usługi C # systemu Windows, aby utworzyć nową aplikację usługi https://github.com/HarpyWar/windows-service-template

Automatycznie wykrywany jest tryb konsoli / usługi, automatyczny instalator / deinstalator usługi oraz kilka najczęściej używanych funkcji.

HarpyWar
źródło
1

Oto prosta metoda, której użyłem do przetestowania usługi, bez żadnych dodatkowych metod „debugowania” i ze zintegrowanymi testami jednostek VS.

[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 });
}
MisterDr
źródło
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
    }
}
Mansoor
źródło
to jest łatwiejsze. wystarczy zmienić ustawienie konfiguracji rozwiązania, aby debugować, uruchomić projekt / rozwiązanie, dodawać punkty przerwania podczas pracy.
Bahamut
0

Dostępne są dwie opcje debugowania.

  1. utwórz plik dziennika: osobiście wolę osobny plik dziennika, taki jak plik tekstowy, zamiast dziennika aplikacji lub dziennika zdarzeń, ale to kosztuje dużo w imieniu czasu, ponieważ nadal trudno jest ustalić, gdzie jest dokładna lokalizacja błędu
  2. Konwertuj aplikację na aplikację konsolową: to umożliwi ci wszystkie narzędzia do debugowania, których możemy użyć w VS.

Proszę odnieść się to na blogu, który został utworzony na ten temat.

Sandaru
źródło
0

Po prostu wklej

Debugger.Break();

gdziekolwiek w twoim kodzie.

Na przykład ,

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

Uderzy Debugger.Break();po uruchomieniu programu.

Chutipong Roobklom
źródło
0

Najlepszą opcją jest użycie przestrzeni nazw „ System.Diagnostics ”.

Załącz kod, jeśli jeszcze zablokuj tryb debugowania i zwalniania, jak pokazano poniżej, aby przełączać się między trybem debugowania i zwalniania w Visual Studio,

#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
Amey P. Naik
źródło