2017-10-13 15 views
1

私は簡単なサービスを実行します。 私はそれを起動し、SCMを使用して停止できます。条件が成立したときにサービスを停止する必要もあります。Delphiサービス自体が停止しません

質問1:SCMを使用するとサービスが停止します。私は "サービスを停止"をクリックし、サービスはほぼ即座に停止します。しかし、私は、exeが約10秒間停止する前に、Windowsのタスクリストに留まることに気づいた。それは正常な行動ですか?

質問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は呼び出されず、 "ServiceExecute"はSvcスレッドが終了すると終了しません。さらに、サービスが終了しても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; 

UPDATE 2:The explication for the delay of 30 seconds for the service to terminate seems to be here

+0

、彼らが使用されることはありません、それは変数が設定されることはありません停止し、各procの宣言に、なぜこれらのconsts。もし私がそれをしたら驚くべきことを見つけるでしょう –

+0

あなたはsname定数について話していますか?ちょうどデバッグの目的のために。ログにproc名を取得する唯一の方法。コードはここに掲載するために簡略化されました。私は定数を削除しませんでした。それで全部です。停止した変数は設定されていますが、読み込まれません。それは私が推測するSCMで使用されています。 – user2244705

+0

私はあなたも質問からそれらを削除する必要がありますと思う。ちなみに関数の名前がコールスタックウィンドウに表示されるので、私はこれを考える必要はありません。 –

答えて

2

スレッドが自身を終了させたい場合は、SCMはサービスがコンセプト実証コードに示すように順番にスレッドを終了しますこれを停止する必要があることを知らせる呼び出すことができます以下。この作業を行うために、匿名メソッドをThreadコンストラクタに渡して、サービス自体に依存しないようにします(スレッドコードはサービス外でもテストできます)。 サービスを開始して何もしないと、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. 

ワーカースレッド:

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

ありがとう@whorsdaddy、私は私のオフィスではない、水曜日まで、私はできるだけ早くそれを試してみませんか? – user2244705

+0

ありがとう、魅力のように動作する多くのwhorsdaddy。 – user2244705

関連する問題