私は簡単なサービスを実行します。 私はそれを起動し、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
、彼らが使用されることはありません、それは変数が設定されることはありません停止し、各procの宣言に、なぜこれらのconsts。もし私がそれをしたら驚くべきことを見つけるでしょう –
あなたはsname定数について話していますか?ちょうどデバッグの目的のために。ログにproc名を取得する唯一の方法。コードはここに掲載するために簡略化されました。私は定数を削除しませんでした。それで全部です。停止した変数は設定されていますが、読み込まれません。それは私が推測するSCMで使用されています。 – user2244705
私はあなたも質問からそれらを削除する必要がありますと思う。ちなみに関数の名前がコールスタックウィンドウに表示されるので、私はこれを考える必要はありません。 –