2015-05-18 13 views
6

Ho scritto un piccolo servizio che funge da server di rete locale. Per scrivere il servizio, ho seguito uno tutorial on MSDN, come scrivere un servizio utilizzando la classe ServiceBase.Servizio causa errore SCM "ha segnalato uno stato corrente non valido 0"

Ma quando mi registro e avvio il servizio, ricevo i messaggi di errore come mostrato di seguito. Ricevo esattamente due di questi messaggi di errore all'inizio e alla fine del servizio. (Esempio = nome del servizio).

Il servizio di esempio ha riportato uno stato corrente non valido 0.

Ecco un campione minimo del mio servizio con tutte le parti interessate. Il codice inizia con una definizione di enum e struct che è stato fornito nel tutorial MSDN:

public enum ServiceState 
{ 
    SERVICE_STOPPED = 0x00000001, 
    SERVICE_START_PENDING = 0x00000002, 
    SERVICE_STOP_PENDING = 0x00000003, 
    SERVICE_RUNNING = 0x00000004, 
    SERVICE_CONTINUE_PENDING = 0x00000005, 
    SERVICE_PAUSE_PENDING = 0x00000006, 
    SERVICE_PAUSED = 0x00000007, 
} 

[StructLayout(LayoutKind.Sequential)] 
public struct ServiceStatus 
{ 
    public long dwServiceType; 
    public ServiceState dwCurrentState; 
    public long dwControlsAccepted; 
    public long dwWin32ExitCode; 
    public long dwServiceSpecificExitCode; 
    public long dwCheckPoint; 
    public long dwWaitHint; 
}; 

Dopo questo, il codice di servizio che ho scritto, ridotto alle parti interessate:

namespace example 
{ 
    public partial class Service : ServiceBase 
    { 
     public Service() 
      : base() 
     { 
      this.ServiceName = "ExampleService"; 
      this.AutoLog = false; 
      this.CanStop = true; 
      this.CanShutdown = false; 
      this.CanPauseAndContinue = false; 
      this.CanHandlePowerEvent = false; 
      this.CanHandleSessionChangeEvent = false; 
     } 

     protected override void OnStart(string[] args) 
     { 
      try { 
       SetServiceState(ServiceState.SERVICE_START_PENDING, 100000); 
       // ... initialise and start... 
       SetServiceState(ServiceState.SERVICE_RUNNING); 
      } catch (System.Exception ex) { 
       SetServiceState(ServiceState.SERVICE_STOPPED); 
      } 
     } 

     protected override void OnStop() 
     { 
      SetServiceState(ServiceState.SERVICE_STOP_PENDING, 100000); 
      // ... stop service ... 
      SetServiceState(ServiceState.SERVICE_STOPPED); 
     } 

     private void SetServiceState(ServiceState state, int waitHint = 0) 
     { 
      ServiceStatus serviceStatus = new ServiceStatus(); 
      serviceStatus.dwCurrentState = state; 
      serviceStatus.dwWaitHint = waitHint; 
      SetServiceStatus(this.ServiceHandle, ref serviceStatus); 
     } 

     [DllImport("advapi32.dll", SetLastError=true)] 
     private static extern bool SetServiceStatus(IntPtr handle, ref ServiceStatus serviceStatus); 
    } 
} 

Il punto di ingresso principale è semplice come questo:

static void Main(string[] args) 
{ 
    ServiceBase.Run(new Service()); 
} 

come si può vedere, il numero di messaggi di errore corrisponde al numero di SetServiceState chiamate. Se aggiungo altre chiamate di questo tipo, il numero di messaggi di errore aumenta di conseguenza.

Quindi presumo, l'intero problema è da qualche parte nel modo in cui chiamo l'API SetServiceStatus, ma al momento non riesco a vedere il problema.

Riesci a individuare il problema?

In ogni caso, questo è il modo corretto di creare un servizio utilizzando C# e .NET?

risposta

14

Prima:

Normalmente non è necessario chiamare il metodo SetServiceState. Potrebbe essere necessario se il tuo servizio impiega molto tempo per inizializzarsi (dove puoi dire allo SCM che sei ancora vivo con SERVICE_START_PENDING e hai bisogno di qualche secondo in più). Normalmente, si avvia una discussione in OnStart e si torna al più presto.

Ho scritto molti servizi in C# negli ultimi anni e non ne ho mai avuto bisogno.

Ma - Ho trovato un bug nel codice di esempio MSDN (ho testato il codice personalmente).

Nella struttura ServiceStatus sostituire long con int o (unità migliore). Se usi uint, devi anche modificarlo nel metodo SetServiceState.

Vedere anche Should DWORD map to int or uint?.

Se siete interessati in una piccola introduzione come si può iniziare con un servizio di Windows in C# con una configurazione aggiuntiva, è possibile controllare il mio blog:

http://www.rsprog.de/samplewindowsservice/

[Aggiornamento 2018/01/20 ]

ho creato un nuovo articolo a tale proposito, che utilizza il metodo di auto-installazione che ha il vantaggio (almeno per me), che non ha bisogno l'estensione progetto di installazione di Visual Studio

http://www.rsprog.de/windowsserviceselfinstall/

+0

Grande, grazie, questo ha risolto il problema. Di solito sono consapevole di quanto sia pessima la documentazione MSDN, ma qui ho solo dato per scontato che questa definizione di struttura dell'esempio sia corretta. Hai anche ragione, gli aggiornamenti di stato manuale non sono realmente necessari. L'avvio del mio codice ha richiesto molto tempo nella prima versione, ma ho spostato tutto in un thread, perché il risultato di alcuni di questi lunghi processi non è rilevante per l'avvio. Ottimo post di blog, solo la formattazione della lista passo-passo mi dava quasi mal di testa ;-). – Flovdis

+0

Ottimo, ha funzionato per me. È impossibile contare quanti errori MSDN ha ... questo era uno tra i numeri infiniti ... – Fabiano

+0

Grazie mille, Rainer - Ho usato il codice di esempio di Microsoft per creare un servizio e centrare immediatamente questo problema. Ho anche aggiunto un commento alla loro pagina qui (https://technet.microsoft.com/en-us/library/cc756305(v=ws.10).aspx) che punta alla tua soluzione qui. – Rich

Problemi correlati