2017-10-13 3 views
1

간단한 서비스를 실행합니다. SCM을 사용하여 시작할 수 있습니다. 나는 또한 조건이 사실이 될 때 스스로 멈추도록 서비스를 필요로한다.Delphi 서비스 자체가 중지되지 않습니다.

질문 1 : SCM을 사용할 때 서비스가 중지됩니다. 나는 "서비스 중지"를 클릭하고 거의 즉시 중단 할 서비스를 제공합니다. 그러나 나는 exe가 멈추기 전에 대략 10 초 동안 창 업무 명부에서 체재 한 ㄴ다는 것을주의했다. 그것은 정상적인 행동입니까?

질문 2 : 아래 코드 예제에서 변수를 증가시킴으로써 서비스가 중단되어야하는 조건을 시뮬레이트했습니다. 이 경우 서비스가 중지되지 않습니다. Windows 작업 관리자에서 작업을 중지해야합니다.

나는 여러 가지를 시도했지만 성공하지 못했습니다.

SCM을 사용하여 서비스를 중지하면 ServiceStop이 스레드 Kill 메서드를 호출하므로 스레드가 중지되고 서비스가 부드럽게 중지 될 수 있습니다.

서비스 자체를 중단하려는 경우 상태는 스레드 자체에서 테스트됩니다. 쓰레드는 멈추지 만 서비스는 멈춘다. 그래서 DoShutDown을 호출하여 서비스 중단을 알려야한다고 생각합니다. 하지만 멈추지 않습니다. DoShutDown 호출의 유무에 관계없이 서비스는 계속 유지됩니다.

내가 뭘 잘못하고 있니?

unit TestSvc; 

interface 

uses 
System.SyncObjs 
,SysUtils 
,Windows 
,SvcMgr 
,Classes 
; 


Type 
    TSvcTh = class(TThread) 
    private 
    FEvent : TEvent; 
    FInterval : Cardinal; 
    vi_dbg : byte; 
    protected 
    procedure Execute; override; 
    procedure DoTimer; 
    public 
    procedure Kill; 
    Constructor Create(); 
    Destructor Destroy; override; 
end; 

type 
    TMyService = class(TService) 
    procedure ServiceCreate(Sender: TObject); 
    procedure ServiceStart(Sender: TService; var Started: Boolean); 
    procedure ServiceShutdown(Sender: TService); 
    procedure ServiceStop(Sender: TService; var Stopped: Boolean); 
    private 
    SelfStop : Boolean; 
    Svc : TSvcTh; 
    public 
    function GetServiceController: TServiceController; override; 
    end; 
    var MyService: TMyService; 




implementation 

procedure ServiceController(CtrlCode: DWord); stdcall; 
const sname='ServiceController'; 
begin 
    MyService.Controller(CtrlCode); 
end; 
function TMyService.GetServiceController: TServiceController; 
const sname='TMyService.GetServiceController'; 
begin 
    Result := ServiceController; 
end; 
procedure TMyService.ServiceCreate(Sender: TObject); 
const sname='TMyService.ServiceCreate'; 
begin 
    try 
    Name := SvcName; 
    except 
    on e: exception do begin 
    end; 
    end; 
end; 


procedure TMyService.ServiceShutdown(Sender: TService); 
const sname='TMyService.ServiceShutdown'; 
var Stopped : boolean; 
begin 
    ServiceStop(Self, Stopped); 
end; 

procedure TMyService.ServiceStart(Sender: TService; var Started: Boolean); 
const sname='TMyService.ServiceStart'; 
begin 
    SelfStop := false; 
    Started := false; 
    try 
     Dbg(sname + ' ******* STARTING THREAD'); 
     Svc := TSvcTh.Create; 
     Dbg(sname + '******* THREAD STARTED'); 
     Started := true; 
    except 
     on e : exception do begin 
     Dbg(sname + '============== EXCEPTION =============>' + e.Message); 
     end; 
    end; 
end; 

procedure TMyService.ServiceStop(Sender: TService; var Stopped: Boolean); 
const sname='TMyService.ServiceStop'; 
begin 
    try 

    Stopped := True; 

    if not SelfStop then begin 
     Dbg(sname + '*** Stop using service controller'); 
     Svc.Kill; 
     Svc.WaitFor; 
     Svc.Free; 
     Svc := nil; 
    end 
    else begin 
     dbg(sname + ' *** Stop by the service itself ') ; 
    end; 

    except 
    on E : Exception do 
    begin 
     dbg(sname + ' Exception ! ' + e.Message); 
    end; 
    end; 
    Dbg(sname + '*** END'); 
end; 

procedure TSvcTh.DoTimer; 
const sname = 'TSvcTh.DoTimer'; 
begin 
    try 
    inc(vi_dbg); 
    Dbg(sname + '******* DoTimer'); 
    except 
    on e : exception do begin 
     Dbg(sname +' ============== EXCEPTION =============>' + e.Message); 
    end; 
    end; 
end; 

procedure TSvcTh.Execute; 
const sname = 'TSvcTh.Execute'; 
begin 
    while not Terminated do begin 
     try 
     case FEvent.WaitFor(FInterval) of 
     wrSignaled : begin // Triggered when we stop the service using service controller 
      Terminate; 
     end; 
     wrTimeout : begin 
      if not Servicemni.SelfStop then begin 
      DoTimer; 
      if vi_dbg > 5 then begin 
       MyService.SelfStop := true; // Testing auto stop 
       terminate; 
      end; 
      end; 
     end; 
     end; 
     except 
     on e : exception do begin 
      Dbg(sname + ' ============== EXCEPTION =============>' + e.Message); 
     end; 
     end; 
    end; 


    if MyService.SelfStop then begin 
    MyService.DoShutdown; 
    end; 

    Dbg(sname + ' ARRET ... ' + StrLog(MyService.Terminated)); 
    if MyService.SelfStop then begin 
    MyService.ReportStatus; 
    end; 


end; 

Constructor TSvcTh.Create(); 
const sname = 'TSvcTh.Create'; 
begin 
    FEvent := TEvent.Create(nil, False, False, ''); 
    FInterval := heartbeat; 
    vi_dbg := 0; 
    inherited Create(False); 
end; 
destructor TSvcTh.Destroy; 
const sname = 'TSvcTh.Destroy'; 
begin 
    try 
    if assigned(FEvent) then begin 
     FreeAndNil(FEvent); 
    end; 
    except 
    on e:exception do begin 
     Dbg(sname + '==========================> EXCEPTION : '+ e.Message); 
    end; 
    end; 
    inherited; 
end; 

procedure TSvcTh.Kill; 
const sname = 'TSvcTh.Kill'; 
begin 
    try 
    FEvent.SetEvent; 
    except 
    on e:exception do begin 
     dbg(sname + ' ==========================> EXCEPTION : '+ e.Message); 
    end; 
    end; 
end; 



end. 

UPDATE :

나는 ServiceExecute 방법을 추가하고 (그것을 종료없이) true로 바로 설정 SelfStop에 SVC 스레드를 수정하는 경우, 서비스가 종료됩니다. 그러나 그것은 매우 우아하게 보이지 않습니다. 왜 필요한지 알 수 없습니다. 사실, 서비스는 "ServiceExecute"스레드를 만드는 것으로 보입니다. 그러나이 메소드를 작성하지 않으면 ProcessRequest가 호출되지 않고 Svc 스레드가 종료 될 때 "ServiceExecute"가 종료되지 않습니다. 또한 서비스 종료 후 Windows 작업 관리자 (sysinternals의 프로세스 탐색기)에서 프로세스가 약 30 초 동안 유지됩니다.

procedure TSvcTh.Execute; 
const sname = 'TSvcTh.Execute'; 
begin 
    while not Terminated do begin 
     try 
     case FEvent.WaitFor(FInterval) of 
     wrSignaled : begin // Triggered when we stop the service using service controller 
      Terminate; 
     end; 
     wrTimeout : begin 
      DoTimer; 
      if vi_dbg > 5 then begin 
      MyService.SelfStop := true; // Testing auto stop 
      end; 
     end; 
     end; 
     except 
     on e : exception do begin 
      Dbg(sname + ' ============== EXCEPTION =============>' + e.Message); 
     end; 
     end; 
    end; 
end; 

procedure TMyService.ServiceExecute(Sender: TService); 
    begin 
    while not terminated do begin 
     ServiceThread.ProcessRequests(false); 
     if SelfStop then begin 
     ServiceThread.terminate; 
     Svc.Terminate; 
     Svc.WaitFor; 
     Svc.Free; 
     Svc := nil; 
     end; 
     sleep(1000);  
    end; 

업데이트 2 : The explication for the delay of 30 seconds for the service to terminate seems to be here

+0

. 내가 그랬다면 놀랍습니다. –

+0

sname 상수에 대해 이야기하고 있습니까? 그냥 디버깅 목적으로. 로그에서 proc 이름을 얻는 유일한 방법. 코드는 여기 게시하기 위해 단순화되었습니다. 나는 상수를 제거하지 않았다. 그게 다야. 중지 된 변수는 설정되지 않고 읽혀지지 않습니다. SCM에서 사용하는 것 같습니다. – user2244705

+0

나는 당신도 질문에서 그것들을 제거해야한다고 생각합니다.그런데 함수의 이름이 호출 스택 창에 표시되므로 생각하지 않아도됩니다. –

답변

2

스레드 자체를 종료하고자 할 경우, 그것은 SCM은 서비스가 개념 증명 코드에서와 같이 차례로 스레드를 종료 할 중지 할 필요가 있음을 알리는 호출 할 수 있습니다 이하. 이 작업을 수행하기 위해 익명 메소드를 스레드 생성자에 전달하여 서비스 자체에 종속되지 않게하고 스레드 코드를 서비스 외부에서 테스트 할 수 있습니다. 서비스를 시작한 후에 아무 것도하지 않으면 10 초 후에 서비스가 종료됩니다.

서비스 코드 :

unit Unit1; 

interface 

uses 
    Unit2, 
    WinApi.WinSvc, 
    Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.SvcMgr, Vcl.Dialogs; 

type 
    TService1 = class(TService) 
    procedure ServiceStart(Sender: TService; var Started: Boolean); 
    procedure ServiceStop(Sender: TService; var Stopped: Boolean); 
    private 
    { Private declarations } 
    MyThread : TMyThread; 
    Eventlog : TEventLogger; 
    public 
    function GetServiceController: TServiceController; override; 
    { Public declarations } 
    end; 

var 
    Service1: TService1; 

implementation 

{$R *.dfm} 

procedure ServiceController(CtrlCode: DWord); stdcall; 
begin 
    Service1.Controller(CtrlCode); 
end; 

function TService1.GetServiceController: TServiceController; 
begin 
    Result := ServiceController; 
end; 

procedure TService1.ServiceStart(Sender: TService; var Started: Boolean); 
begin 
EventLog := TEventLogger.Create('Service1'); 
// call our thread and inject code for premature service shutdown 
MyThread := TMyThread.Create(procedure begin Service1.Controller(SERVICE_CONTROL_STOP) end); 
MyThread.Start; 
EventLog.LogMessage('Started'); 
end; 

procedure TService1.ServiceStop(Sender: TService; var Stopped: Boolean); 
begin 
EventLog.LogMessage('Stop'); 
MyThread.Terminate; 
// Give some time to the thread to cleanup, then bailout 
WaitForSingleObject(MyThread.Handle, 5000); 
EventLog.LogMessage('Stopped'); 
EventLog.Free; 
Stopped := True; 
end; 

end. 

작업자 스레드 :

그는 사용되지 않습니다 각 시저 선언에 consts와 그 변수를 중지가 설정되지 않습니다 이유
unit Unit2; 

interface 

uses 
    SysUtils, 
    Vcl.SvcMgr, 
    Windows, 
    System.Classes; 

type 
    TSimpleProcedure = reference to procedure; 

    TMyThread = class(TThread) 
    private 
    { Private declarations } 
    ShutDownProc : TSimpleProcedure; 
    EventLog  : TEventLogger; 
    protected 
    procedure Execute; override; 
    public 
    constructor Create(AShutDownProc: TSimpleProcedure); 
    destructor Destroy; override; 
    end; 

implementation 

{ MyThread } 
constructor TMyThread.Create(AShutDownProc: TSimpleProcedure); 
begin 
inherited Create(True); 
ShutDownProc := AShutDownProc; 
end; 

procedure TMyThread.Execute; 

var 
    Count : Integer; 
    Running : Boolean; 

begin 
EventLog := TEventLogger.Create('MyThread'); 
EventLog.LogMessage('Thread Started'); 
Count := 0; 
Running := True; 
while not Terminated and Running do 
    begin 
    EventLog.LogMessage(Format('Count: %d', [Count])); 
    Running := Count <> 10; 
    Inc(Count); 
    if Running then 
    Sleep(1000); // do some work 
    end; 
// if thread wants to stop itself, call service thread shutdown and wait for termination 
if not Running and not Terminated then 
    begin 
    EventLog.LogMessage(Format('Thread Wants to Stop', [Count])); 
    ShutDownProc(); 
    end; 
EventLog.LogMessage(Format('Thread Await terminate', [Count])); 
// await termination 
while not Terminated do Sleep(10); 
EventLog.LogMessage(Format('Thread Terminated', [Count])); 
EventLog.Free; 
end; 

end. 
+0

감사합니다. @ 위스콘신 주일 전까지 제 사무실에 없었습니다. 나는 그렇게 빨리 시도 할 것입니다. – user2244705

+0

대단한 감사의 말을 전하며, 매력처럼 작동합니다. – user2244705