Modo più semplice per eseguire il debug di un servizio Windows


325

Esiste un modo più semplice per scorrere il codice che avviare il servizio tramite Gestione controllo servizi di Windows e quindi collegare il debugger al thread? È un po 'ingombrante e mi chiedo se esiste un approccio più diretto.


Ho creato questo biglietto vocale utente. Prendi in considerazione la possibilità di votare per questo: visualstudio.uservoice.com/forums/121579-visual-studio-ide/…
David

Risposte:


271

Se voglio eseguire il debug rapido del servizio, inserisco semplicemente una Debugger.Break(). Quando viene raggiunta quella linea, mi riporterà a VS. Non dimenticare di rimuovere quella linea quando hai finito.

AGGIORNAMENTO: in alternativa ai #if DEBUGpragmi, puoi anche usare l' Conditional("DEBUG_SERVICE")attributo.

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

Sul tuo OnStart, basta chiamare questo metodo:

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

Lì, il codice sarà abilitato solo durante le build di debug. Mentre ci sei, potrebbe essere utile creare una Configurazione build separata per il debug del servizio.


45
Oppure potresti usare Debugger.Launch () dovrai includere un'istruzione using per lo spazio dei nomi The Systems.Diagnostics.
Omar Kooheji,

1
Il tuo post sul blog ha funzionato bene e mi ha salvato la giornata :) tuttavia Debugger.Break () non ha funzionato per me. sembra che .Net salti la funzione DebugMode per alcuni motivi legati all'ottimizzazione.
Bizhan,

3
Debugger.Launch () funziona per me quando Debugger.Break () no. (Il processo termina con il codice 255.)
Oliver Bock,

Come state facendo questo ragazzi? Non succede niente. Ho provato Break () e Launch ().
4thSpace

13
@ 4thSpace: 1. crea un programma di installazione per il tuo servizio, in modo da poter installare il tuo servizio. 2. Aggiungi la linea Debugger.Launch (); all'inizio del tuo Main (). 3. Costruisci il tuo codice in modalità Debug. 4. Sovrascrivi le dll installate con le debug-dll. 5. Avviare il servizio dal pannello Servizi di Windows. Ora appare un popup che ti chiede di collegarti a un debugger. In questo modo ha funzionato per me. Spero anche per te.
ffonz,

210

Penso anche che avere una "versione" separata per la normale esecuzione e come servizio sia la strada da percorrere, ma è davvero necessario dedicare un interruttore della riga di comando separato a tale scopo?

Non potresti semplicemente fare:

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

Ciò avrebbe il "vantaggio", che puoi semplicemente avviare la tua app tramite doppio clic (OK, se ne hai davvero bisogno) e che puoi semplicemente accedere F5a Visual Studio (senza la necessità di modificare le impostazioni del progetto per includere /consolequell'opzione).

Tecnicamente, Environment.UserInteractivecontrolla se WSF_VISIBLEFlag è impostato per la stazione corrente della finestra, ma c'è qualche altro motivo per cui dovrebbe tornare false, oltre ad essere gestito come un servizio (non interattivo)?


Grande! In precedenza ho usato un metodo "if #debug" per avviare come applicazione il debug, altrimenti un servizio. Questo porta all'app che non può essere eseguito come servizio se si desidera eseguire il debug, ma la soluzione lo risolve e lascia che sia eseguibile in tutte e quattro le combinazioni di servizio / app e rilascio / debug.
Jonas

29
Se non si desidera eseguire il programma quando viene fatto doppio clic (gli utenti potrebbero essere confusi ed eseguire diverse istanze, ecc.), È possibile utilizzare System.Diagnostics.Debugger.IsAttachedinvece di Environment.UserInteractive.
Blorgbeard esce

5
ma c'è qualche altro motivo per cui potrebbe restituire false, oltre a essere eseguito come servizio (non interattivo)? Mi viene in mente uno: un'attività pianificata che non si collega a una console.
Hogan,

7
io uso i parametri della riga di comando per questo caso. --install per installare il servizio, --uninstall per disinstallare il servizio e --interactive per eseguire il servizio come applicazione. aggiungo --interattivo alle opzioni del progetto (Debug> Argomenti comando). Quindi posso facilmente eseguire il debug da VS. il doppio clic non creerà un'istanza in esecuzione indesiderata poiché --interactive è richiesto. solo i miei 2 centesimi.
Emir Akaydın,

@ EmirAkaydın Sì, in realtà ho anche i parametri della riga di comando come "backup". Tuttavia, in realtà volevo avere un'istanza "interattiva" quando si fa doppio clic e non un messaggio di errore sul fatto che un servizio non può essere avviato in questo modo. Gol diversi, suppongo ;-)
Christian.K,

123

Quando ho creato un nuovo progetto di assistenza qualche settimana fa, ho trovato questo post. Sebbene ci siano molti ottimi suggerimenti, non ho ancora trovato la soluzione che volevo: la possibilità di chiamare le classi di servizio OnStarte i OnStopmetodi senza alcuna modifica alle classi di servizio.

La soluzione che ho trovato utilizza la Environment.Interactivemodalità di esecuzione select, come suggerito da altre risposte a questo post.

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

L' RunInteractivehelper utilizza la reflection per chiamare il metodo protetto OnStarte OnStop:

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

Questo è tutto il codice richiesto, ma ho anche scritto una procedura dettagliata con spiegazioni.


Questo rende un buon metodo di estensione per ServiceBase []. Ho più servizi nella mia soluzione, quindi piuttosto che avere una classe base comune per Program.cs, chiamo servicesToRun.RunInteractive (args). Bella soluzione @Anders!
David Keaveny,

3
Ottima soluzione davvero. Ho creato una semplice estensione per ServiceBase [] come suggerito da David, che consente di eseguire i servizi in una sola riga di codice: pastebin.com/F0fhhG2R
Funbit

4
+1 Un mio ex collega ha creato una classe base "EasyRunService" (che eredita ServiceProcess) che fa praticamente la stessa cosa, ma senza la necessità di riflessione (perché OnStart è ora nella classe base). Rende davvero il debug di un servizio Windows un gioco da ragazzi.
Sondergard,

3
@ Chazt3n Assicurarsi che il tipo di output del progetto sia impostato su "Applicazione console". Per quanto riguarda l'installazione del servizio, non importa quale tipo di output sia selezionato, il comportamento è lo stesso.
Funbit

2
Comunque un'ottima soluzione! L'unica cosa che aggiungerei (come mostrato in walk through) è assicurarmi di andare nelle proprietà del progetto e cambiare il tipo di output Console Applicationprima di provare a compilare ed eseguire. Trovalo a Project Properties -> Application -> Output type -> Console Application. Inoltre, affinché questo funzioni correttamente per me, ho finito per dover eseguire l'applicazione usando il startcomando. Es: C:\"my app name.exe" -servicenon funzionerebbe per me. Invece ho usatoC:\start /wait "" "my app name.exe" -service
Arvo Bowen l'

47

A volte è importante analizzare cosa succede durante l'avvio del servizio. Il collegamento al processo non aiuta in questo caso, poiché non si è abbastanza veloci da collegare il debugger durante l'avvio del servizio.

La risposta breve è, sto usando le seguenti 4 righe di codice per fare questo:

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

Questi sono inseriti nel OnStartmetodo del servizio come segue:

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

Per coloro che non l'hanno mai fatto prima, ho incluso suggerimenti dettagliati di seguito , perché puoi facilmente rimanere bloccato. I seguenti suggerimenti si riferiscono a Windows 7x64 e Visual Studio 2010 Team Edition , ma dovrebbero essere validi anche per altri ambienti.


Importante: distribuire il servizio in modalità "manuale" (utilizzando l' InstallUtilutilità dal prompt dei comandi VS o eseguire un progetto di installazione del servizio che è stato preparato). Aprire Visual Studio prima di avviare il servizio e caricare la soluzione contenente il codice sorgente del servizio - impostare punti di interruzione aggiuntivi come richiesto in Visual Studio - quindi avviare il servizio tramite il Pannello di controllo del servizio.

A causa del Debugger.Launchcodice, ciò causerà una finestra di dialogo "Si è verificata un'eccezione di Microsoft .NET Framework non gestita in Servicename.exe ." apparire. Clicca come mostrato nello screenshot:Elevate Yes, debug Servicename.exe
FrameworkException

Successivamente, soprattutto in UAC di Windows 7, è possibile che venga richiesto di immettere le credenziali di amministratore. Inseriscili e procedi con Yes:

UACPrompt

Successivamente, viene visualizzata la nota finestra del debugger just-in-time di Visual Studio . Ti chiede se desideri eseguire il debug utilizzando il debugger eliminato. Prima di fare clic Yes, selezionare che non si desidera aprire una nuova istanza (seconda opzione): una nuova istanza non sarebbe utile qui, poiché il codice sorgente non verrebbe visualizzato. Quindi selezioni invece l'istanza di Visual Studio che hai aperto in precedenza: VSDebuggerPrompt

Dopo aver fatto clic Yes, dopo un po 'Visual Studio mostrerà la freccia gialla nella riga in cui si trova l' Debugger.Launchistruzione e sarà possibile eseguire il debug del codice (metodo MyInitOnStart, che contiene l'inizializzazione). VSDebuggerBreakpoint

La pressione F5continua l'esecuzione immediatamente, fino al raggiungimento del punto di interruzione successivo che è stato preparato.

Suggerimento: per mantenere attivo il servizio, selezionare Debug -> Scollega tutto . Ciò consente di eseguire un client che comunica con il servizio dopo che è stato avviato correttamente e al termine del debug del codice di avvio. Se si preme Shift+F5 (interrompe il debug), questo terminerà il servizio. Invece di farlo, dovresti usare il Pannello di controllo del servizio per fermarlo.

Si noti che

  • Se si crea una versione, il codice di debug viene automaticamente rimosso e il servizio viene eseguito normalmente.

  • Sto usando Debugger.Launch(), che avvia e allega un debugger . Ho anche testato Debugger.Break(), che non ha funzionato , perché non è ancora collegato alcun debugger all'avvio del servizio (causando "Errore 1067: il processo è terminato in modo imprevisto" ).

  • RequestAdditionalTimeimposta un lungo timeout per l'avvio del servizionon ritardare il codice stesso, ma immediatamente continuare con la Debugger.Launchdichiarazione). In caso contrario, il timeout predefinito per l'avvio del servizio è troppo breve e l'avvio del servizio non riesce se non si chiama base.Onstart(args)abbastanza rapidamente dal debugger. In pratica, un timeout di 10 minuti evita che venga visualizzato il messaggio " il servizio non ha risposto ..." immediatamente dopo l'avvio del debugger.

  • Una volta che ci si abitua, questo metodo è molto semplice perché richiede solo di aggiungere 4 righe a un codice di servizio esistente, consentendo di ottenere rapidamente controllo e debug.


1
Per curiosità, sai se c'è un timeout per l'interazione dell'utente con il prompt utente Debugger.Launch ()?
Shiv,

1
Come descritto, base.RequestAdditionalTime(600000)impedirà al controllo del servizio di terminare il servizio per 10 minuti se non chiama base.OnStart(args)entro tale periodo di tempo). A parte questo, ricordo che UAC si interromperà anche se non inserisci le credenziali di amministratore dopo un po '(non so quanti secondi esattamente, ma penso che devi inserirlo entro un minuto, altrimenti UAC si interrompe) , che terminerà la sessione di debug.
Matt,

2
Ho trovato che questo è il metodo migliore per il debug dei messaggi CustomCommand. +1.
Giustino,

40

Quello che faccio di solito è incapsulare la logica del servizio in una classe separata e iniziare da una classe "runner". Questa classe di corridori può essere il servizio effettivo o solo un'applicazione console. Quindi la tua soluzione ha (almeno) 3 progetti:

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

1
Usavo anche questo approccio, ma penso che una combinazione di questo e la risposta sopra funzionino a meraviglia.
RobS

27

Questo video di YouTube di Fabio Scopel spiega come eseguire correttamente il debug di un servizio Windows ... il metodo effettivo per farlo inizia alle 4:45 nel video ...

Ecco il codice spiegato nel video ... nel tuo file Program.cs, aggiungi gli elementi per la sezione Debug ...

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

        }
    }
}

Nel tuo file Service1.cs, aggiungi il metodo OnDebug () ...

    public Service1()
    {
        InitializeComponent();
    }

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

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

    protected override void OnStop()
    {
    }

Come funziona

Fondamentalmente devi creare un public void OnDebug()che chiama il OnStart(string[] args)come è protetto e non accessibile all'esterno. Il void Main()programma viene aggiunto con il #ifpreprocessore con#DEBUG .

Visual Studio definisce DEBUGse il progetto è compilato in modalità Debug. Ciò consentirà l'esecuzione della sezione di debug (sotto) quando la condizione è vera

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

E funzionerà proprio come un'applicazione console, una volta che le cose vanno bene puoi cambiare la modalità Releasee la elsesezione normale attiverà la logica


Stavo cercando questa risposta, non so perché sia ​​stata classificata così in basso. Spiegato il codice per aiutare gli altri o forse altri commenti;)
Vinod Srivastav,

14

AGGIORNARE

Questo approccio è di gran lunga il più semplice:

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

Lascio la mia risposta originale qui sotto per i posteri.


I miei servizi tendono ad avere una classe che incapsula un Timer poiché voglio che il servizio controlli a intervalli regolari se c'è qualche lavoro da fare.

Rinnoviamo la classe e chiamiamo StartEventLoop () durante l'avvio del servizio. (Questa classe potrebbe essere facilmente utilizzata anche da un'app console.)

Il piacevole effetto collaterale di questo design è che gli argomenti con cui si imposta il timer possono essere utilizzati per avere un ritardo prima che il servizio inizi effettivamente a funzionare, in modo da avere il tempo di collegare manualmente un debugger.

ps Come collegare manualmente il debugger a un processo in esecuzione ...?

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

Inoltre, ero solito fare quanto segue (già menzionato nelle risposte precedenti ma con i flag del compilatore condizionale [#if] per evitare che sparasse in una build di rilascio).

Ho smesso di farlo in questo modo perché a volte ci saremmo dimenticati di compilare in Release e avere un'interruzione del debugger in un'app in esecuzione su una demo client (imbarazzante!).

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

Cosa succede quando il completamento // do somethingrichiede più di 30 minuti?
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
}

[Mi dispiace per nessuna spiegazione con il codice - problemi di markdown] Dovrebbe funzionare normalmente da MS Visual Studio (F5) nelle build di debug. Funziona ancora come un normale servizio nelle build di rilascio.
Thomas Bratt,

Combina questo con la soluzione sopra di Christian K. per usare la proprietà "Environment.UserInteractive" e la soluzione è davvero semplice e pulita.
Ben Robbins,

OnStartè protectede non è possibile modificare il livello di accesso :(
Eduard Luca

10

È inoltre possibile avviare il servizio tramite il prompt dei comandi (sc.exe).

Personalmente, eseguivo il codice come programma autonomo durante la fase di debug e, quando la maggior parte dei bug viene risolta, passa all'esecuzione come servizio.


10

Quello che ero solito fare era avere un commutatore da riga di comando che avrebbe avviato il programma come servizio o come normale applicazione. Quindi, nel mio IDE imposterei l'interruttore in modo da poter scorrere il mio codice.

Con alcune lingue puoi effettivamente rilevare se è in esecuzione in un IDE ed eseguire questa opzione automaticamente.

Che lingua stai usando?


9

Usa la libreria TopShelf .

Crea un'applicazione console quindi configura la configurazione nel tuo 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()
        {

        }
    }

Per eseguire il debug del tuo servizio, premi F5 in Visual Studio.

Per installare il servizio, digitare cmd "console.exe install"

È quindi possibile avviare e interrompere il servizio nel gestore servizi di Windows.


La loro licenza era troppo confusa per capire
l --''''''--------- '' '' '' '' '' ''

Usano Apache License afaik. Topshelf è il modo più semplice che ho usato per sviluppare ed eseguire il debug di servizi Windows. Super facile da usare. Sviluppa come un'applicazione console. Installa come servizio con un'opzione da riga di comando. Altamente raccomandato.
ruba il

TopShelf mi ha fatto risparmiare un sacco di tempo. Thx
L_7337

8

Penso che dipenda dal sistema operativo in uso, Vista è molto più difficile da collegare ai servizi, a causa della separazione tra le sessioni.

Le due opzioni che ho usato in passato sono:

  • Utilizzare GFlags (negli Strumenti di debug per Windows) per configurare un debugger permanente per un processo. Questo esiste nella chiave di registro "Opzioni di esecuzione dei file di immagine" ed è incredibilmente utile. Penso che dovrai modificare le impostazioni del servizio per abilitare "Interagisci con desktop". Lo uso per tutti i tipi di debug, non solo per i servizi.
  • L'altra opzione è quella di separare un po 'il codice, in modo che la parte di servizio sia intercambiabile con un normale avvio dell'app. In questo modo, è possibile utilizzare un semplice flag della riga di comando e avviarlo come processo (anziché come servizio), il che semplifica notevolmente il debug.

Spero che questo ti aiuti.


+1 per GFlags. Ciò è particolarmente utile se non è possibile modificare il codice sorgente (o se non lo si possiede).
Chris Gillum,

6

Mi piace essere in grado di eseguire il debug di ogni aspetto del mio servizio, compresa qualsiasi inizializzazione in OnStart (), mentre lo sto ancora eseguendo con un comportamento di servizio completo nel quadro di SCM ... nessuna modalità "console" o "app".

Lo faccio creando un secondo servizio, nello stesso progetto, da utilizzare per il debug. Il servizio di debug, quando avviato come di consueto (ovvero nel plug-in MMC dei servizi), crea il processo host del servizio. Questo ti dà un processo per collegare il debugger anche se non hai ancora iniziato il tuo vero servizio. Dopo aver collegato il debugger al processo, avvia il tuo vero servizio e puoi interromperlo ovunque nel ciclo di vita del servizio, incluso OnStart ().

Poiché richiede un'intrusione di codice molto minima, il servizio di debug può essere facilmente incluso nel progetto di installazione del servizio e può essere facilmente rimosso dalla versione di produzione commentando una singola riga di codice ed eliminando un singolo programma di installazione del progetto.

Dettagli:

1) Supponendo che stai implementando MyService, crea anche MyServiceDebug. Aggiungi entrambi ServiceBaseall'array in questo Program.csmodo:

    /// <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) Aggiungere il servizio reale E il servizio di debug al programma di installazione del progetto per il progetto di servizio:

inserisci qui la descrizione dell'immagine

Entrambi i servizi (reali e di debug) vengono inclusi quando si aggiunge l'output del progetto di servizio al progetto di installazione per il servizio. Dopo l'installazione, entrambi i servizi verranno visualizzati nel plug-in MMC service.msc.

3) Avviare il servizio di debug in MMC.

4) In Visual Studio, collegare il debugger al processo avviato dal servizio di debug.

5) Avvia il servizio reale e goditi il ​​debug.


5

Quando scrivo un servizio inserisco tutta la logica di servizio in un progetto DLL e creo due "host" che chiamano in questa DLL, uno è un servizio Windows e l'altro è un'applicazione da riga di comando.

Uso l'applicazione a riga di comando per il debug e allego il debugger al servizio reale solo per i bug che non riesco a riprodurre nell'applicazione a riga di comando.

Se usi questo approccio, ricorda solo che devi testare tutto il codice durante l'esecuzione in un servizio reale, mentre lo strumento da riga di comando è un valido aiuto per il debug, è un ambiente diverso e non si comporta esattamente come un vero servizio.


4

Durante lo sviluppo e il debug di un servizio Windows in genere lo eseguo come un'applicazione console aggiungendo un parametro di avvio / console e verificandolo. Semplifica la vita.

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

Finché non devi eseguire il debug di problemi specifici del servizio.
leppie,

È vero, quindi è necessario collegare il debugger al processo di servizio effettivo. Ma nella maggior parte dei casi i bug appariranno in entrambi i modi e lo sviluppo è molto più semplice.
Maurice,

4

Che ne dici di Debugger.Break () nella prima riga?


2

Per eseguire il debug dei servizi Windows, combino GFlags e un file .reg creato da regedit.

  1. Esegui GFlags, specificando il nome exe e vsjitdebugger
  2. Esegui regedit e vai alla posizione in cui GFlags imposta le sue opzioni
  3. Scegli "Esporta chiave" dal menu file
  4. Salvare quel file da qualche parte con l'estensione .reg
  5. Ogni volta che desideri eseguire il debug del servizio: fai doppio clic sul file .reg
  6. Se si desidera interrompere il debug, fare doppio clic sul secondo file .reg

Oppure salva i seguenti frammenti e sostituisci servicename.exe con il nome eseguibile desiderato.


debugon.reg:

Editor del registro di Windows Versione 5.00

[HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ Windows NT \ CurrentVersion \ Opzioni di esecuzione file immagine \ servicename.exe]
"GlobalFlag" = "0x00000000"
"Debugger" = "vsjitdebugger.exe"

debugoff.reg:

Editor del registro di Windows Versione 5.00

[HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ Windows NT \ CurrentVersion \ Opzioni di esecuzione file immagine \ servicename.exe]
"GlobalFlag" = "0x00000000"

Funziona ancora su Win 7 / Win 2008? È l'approccio di support.microsoft.com/kb/824344 ma si basa su servizi interattivi e ho pensato che fossero stati uccisi? Era sempre la mia opzione preferita (poiché i problemi di avvio potrebbero sorgere in produzione, dove l'inserimento di un Debugger.Break () nel codice potrebbe non essere un'opzione).
piers7,

1

Per la programmazione ordinaria di piccole cose ho fatto un trucco molto semplice per eseguire facilmente il debug del mio servizio:

All'avvio del servizio, cerco un parametro della riga di comando "/ debug". Se il servizio viene chiamato con questo parametro, non eseguo il consueto avvio del servizio, ma invece avvio tutti i listener e visualizzo solo una finestra di messaggio "Debug in progress, premi ok per terminare".

Quindi, se il mio servizio viene avviato come di consueto, verrà avviato come servizio, se viene avviato con il parametro della riga di comando / debug funzionerà come un normale programma.

In VS aggiungerò / debug come parametro di debug e avvierò direttamente il programma di servizio.

In questo modo posso facilmente eseguire il debug per la maggior parte dei piccoli problemi. Naturalmente, alcune cose dovranno ancora essere sottoposte a debug come servizio, ma per il 99% è abbastanza buono.



1

Uso una variazione sulla risposta di JOP. Utilizzando i parametri della riga di comando è possibile impostare la modalità di debug nell'IDE con le proprietà del progetto o tramite il service manager di Windows.

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


1

Metti il ​​tuo pranzo debugger ovunque e collega Visualstudio all'avvio

#if DEBUG
    Debugger.Launch();
#endif

Inoltre, devi avviare VS come Administatrator e devi consentire che un processo possa essere automaticamente sottoposto a debug da un utente diverso (come spiegato qui ):

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


1

Ecco il semplice metodo che ho usato per testare il servizio, senza ulteriori metodi di "Debug" e con Test VS Unit integrati.

[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
    }
}

questo è più facile. basta cambiare l'impostazione di configurazione della soluzione in debug, eseguire il progetto / soluzione, aggiungere punti di interruzione mentre si procede.
Bahamut,

0

Hai due opzioni per eseguire il debug.

  1. creare un file di registro: Personalmente preferisco un file di registro separato come il file di testo piuttosto che utilizzare il registro dell'applicazione o il registro eventi, ma questo ti costerà molto per conto del tempo, perché è ancora difficile capire dove si trova la posizione esatta dell'errore
  2. Converti l'applicazione in console: questo ti consentirà tutti gli strumenti di debug che possiamo usare in VS.

Si prega di fare riferimento a QUESTO post sul blog che ho creato per l'argomento.


0

Basta incollare

Debugger.Break();

ovunque nel tuo codice.

Per esempio ,

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

Colpirà Debugger.Break();quando esegui il tuo programma.


0

L'opzione migliore è utilizzare lo spazio dei nomi " System.Diagnostics ".

Racchiudere il codice in caso contrario bloccare per la modalità di debug e la modalità di rilascio come mostrato di seguito per passare dalla modalità di debug a quella di rilascio in 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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.