2015-05-18 12 views
6

Napisałem małą usługę, która działa jako lokalny serwer sieciowy. Aby napisać usługę, postępowałem zgodnie z tutorial on MSDN, jak napisać usługę przy użyciu klasy ServiceBase.Usługa powoduje błąd SCM "zgłosiło niepoprawny stan bieżący 0"

Po zarejestrowaniu się i uruchomieniu usługi wyświetlane są komunikaty o błędach, jak pokazano poniżej. Dostaję dokładnie dwa z tych komunikatów o błędach na początku i na przystanku serwisowym. (Przykład = nazwa usługi).

Przykład ten serwis poinformował nieprawidłowy stan obecny 0.

Tutaj minimalną próbkę moich usług ze wszystkimi odpowiednimi częściami. Kod rozpoczyna się od definicji enum i struct, który został podany w poradniku 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; 
}; 

Po tym kodzie serwisowym napisałem, sprowadza się do odpowiednich części:

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

głównego punktu wejścia jest proste, jak to:

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

jak widać, liczba komunikatów o błędach dopasowuje liczbę SetServiceState połączeń. Jeśli dodaję dodatkowe takie połączenia, liczba komunikatów o błędach odpowiednio wzrośnie.

Zakładam więc, że cały problem jest gdzieś w drodze, którą nazywam API SetServiceStatus, ale obecnie nie widzę problemu.

Czy możesz zauważyć problem?

Czy to jest poprawny sposób na zbudowanie usługi przy użyciu C# i .NET?

Odpowiedz

14

pierwsze:

To nie jest zwykle konieczne, aby wywołać metodę SetServiceState. Może być potrzebny, jeśli twoja usługa naprawdę potrzebuje dużo czasu na zainicjowanie (gdzie możesz powiedzieć SCM, że nadal żyjesz z SERVICE_START_PENDING i potrzebujesz jeszcze kilku sekund). Zwykle rozpoczynasz wątek w OnStart i jak najszybciej powracasz.

Napisałem wiele usług w C# w ostatnich latach i nigdy tego nie potrzebowałem.

Ale znalazłem błąd w kodzie przykładowym MSDN (sam testowałem swój kod).

W strukturze ServiceStatus należy zamienić na long z int lub (lepiej jednostką). Jeśli używasz uint, musisz to zmienić w metodzie SetServiceState.

Zobacz także Should DWORD map to int or uint?.

Jeśli jesteś zainteresowany w małym wprowadzeniem w jaki sposób można zacząć usługi Windows w C# z dodatkowej konfiguracji można sprawdzić mój wpis w blogu:

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

[Aktualizacja 2018-01-20 ]

stworzyłem nowy artykuł o tym, która wykorzystuje metodę self-instalacyjnego, który ma tę zaletę (przynajmniej dla mnie), że nie ma potrzeby konfiguracji rozszerzenia projektu Visual Studio

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

+0

Świetnie, dziękuję, to rozwiązało problem. Zazwyczaj zdaję sobie sprawę z tego, jak zła jest dokumentacja MSDN, ale tutaj po prostu założyłem, że definicja struktury z przykładu jest prawidłowa. Masz również rację, ręczne aktualizacje statusu nie są naprawdę konieczne. Uruchomienie mojego kodu zajęło dużo czasu w pierwszej wersji, ale przeniosłem wszystko w wątku, ponieważ wynik niektórych z tych długich procesów nie ma znaczenia dla uruchomienia. Świetny wpis na blogu, tylko formatowanie listy krok po kroku sprawiło, że prawie poczułam ból głowy ;-). – Flovdis

+0

Świetnie, zadziałało to dla mnie. Nie da się policzyć, ile błędów ma MSDN ... to była jedna z nieskończonych liczb ... – Fabiano

+0

Wielkie dzięki, Rainer - użyłem przykładowego kodu Microsoftu do stworzenia usługi i od razu trafiłem w ten problem. Dodałem również komentarz do ich strony tutaj (https://technet.microsoft.com/en-us/library/cc756305(v=ws.10).aspx), wskazując tutaj swoje rozwiązanie. – Rich

Powiązane problemy