SvcMgr.pas
上传用户:faith2010
上传日期:2016-01-09
资源大小:12650k
文件大小:28k
源码类别:

Delphi控件源码

开发平台:

Delphi

  1. {*******************************************************}
  2. {                                                       }
  3. {       Borland Delphi Visual Component Library         }
  4. {                                                       }
  5. {  Copyright (c) 1995-2001 Borland Software Corporation }
  6. {                                                       }
  7. {*******************************************************}
  8. unit SvcMgr;
  9. {$J+,H+,X+}
  10. interface
  11. uses
  12.   Windows, Messages, SysUtils, Classes, WinSvc;
  13. type
  14.   { TEventLogger }
  15.   TEventLogger = class(TObject)
  16.   private
  17.     FName: String;
  18.     FEventLog: Integer;
  19.   public
  20.     constructor Create(Name: String);
  21.     destructor Destroy; override;
  22.     procedure LogMessage(Message: String; EventType: DWord = 1;
  23.       Category: Word = 0; ID: DWord = 0);
  24.   end;
  25.   { TDependency }
  26.   TDependency = class(TCollectionItem)
  27.   private
  28.     FName: String;
  29.     FIsGroup: Boolean;
  30.   protected
  31.     function GetDisplayName: string; override;
  32.   published
  33.     property Name: String read FName write FName;
  34.     property IsGroup: Boolean read FIsGroup write FIsGroup;
  35.   end;
  36.   { TDependencies }
  37.   TDependencies = class(TCollection)
  38.   private
  39.     FOwner: TPersistent;
  40.     function GetItem(Index: Integer): TDependency;
  41.     procedure SetItem(Index: Integer; Value: TDependency);
  42.   protected
  43.     function GetOwner: TPersistent; override;
  44.   public
  45.     constructor Create(Owner: TPersistent);
  46.     property Items[Index: Integer]: TDependency read GetItem write SetItem; default;
  47.   end;
  48. { TServiceThread }
  49. const
  50.   CM_SERVICE_CONTROL_CODE = WM_USER + 1;
  51. type
  52.   TService = class;
  53.   TServiceThread = class(TThread)
  54.   private
  55.     FService: TService;
  56.   protected
  57.     procedure Execute; override;
  58.   public
  59.     constructor Create(Service: TService);
  60.     procedure ProcessRequests(WaitForMessage: Boolean);
  61.   end;
  62.   { TService }
  63.   TServiceController = procedure(CtrlCode: DWord); stdcall;
  64.   TServiceType = (stWin32, stDevice, stFileSystem);
  65.   TCurrentStatus = (csStopped, csStartPending, csStopPending, csRunning,
  66.     csContinuePending, csPausePending, csPaused);
  67.   TErrorSeverity = (esIgnore, esNormal, esSevere, esCritical);
  68.   TStartType = (stBoot, stSystem, stAuto, stManual, stDisabled);
  69.   TServiceEvent = procedure(Sender: TService) of object;
  70.   TContinueEvent = procedure(Sender: TService; var Continued: Boolean) of object;
  71.   TPauseEvent = procedure(Sender: TService; var Paused: Boolean) of object;
  72.   TStartEvent = procedure(Sender: TService; var Started: Boolean) of object;
  73.   TStopEvent = procedure(Sender: TService; var Stopped: Boolean) of object;
  74.   TService = class(TDataModule)
  75.   private
  76.     FAllowStop: Boolean;
  77.     FAllowPause: Boolean;
  78.     FDependencies: TDependencies;
  79.     FDisplayName: String;
  80.     FErrCode: DWord;
  81.     FErrorSeverity: TErrorSeverity;
  82.     FEventLogger: TEventLogger;
  83.     FInteractive: Boolean;
  84.     FLoadGroup: String;
  85.     FParams: TStringList;
  86.     FPassword: String;
  87.     FServiceStartName: String;
  88.     FServiceThread: TServiceThread;
  89.     FServiceType: TServiceType;
  90.     FStartType: TStartType;
  91.     FStatus: TCurrentStatus;
  92.     FStatusHandle: THandle;
  93.     FTagID: DWord;
  94.     FWaitHint: Integer;
  95.     FWin32ErrorCode: DWord;
  96.     FBeforeInstall: TServiceEvent;
  97.     FAfterInstall: TServiceEvent;
  98.     FBeforeUninstall: TServiceEvent;
  99.     FAfterUninstall: TServiceEvent;
  100.     FOnContinue: TContinueEvent;
  101.     FOnExecute: TServiceEvent;
  102.     FOnPause: TPauseEvent;
  103.     FOnShutdown: TServiceEvent;
  104.     FOnStart: TStartEvent;
  105.     FOnStop: TStopEvent;
  106.     function GetDisplayName: String;
  107.     function GetParamCount: Integer;
  108.     function GetParam(Index: Integer): String;
  109.     procedure SetStatus(Value: TCurrentStatus);
  110.     procedure SetDependencies(Value: TDependencies);
  111.     function GetNTDependencies: String;
  112.     function GetNTServiceType: Integer;
  113.     function GetNTStartType: Integer;
  114.     function GetNTErrorSeverity: Integer;
  115.     function GetNTControlsAccepted: Integer;
  116.     procedure SetOnContinue(Value: TContinueEvent);
  117.     procedure SetOnPause(Value: TPauseEvent);
  118.     procedure SetOnStop(Value: TStopEvent);
  119.     function GetTerminated: Boolean;
  120.     function AreDependenciesStored: Boolean;
  121.     procedure SetInteractive(Value: Boolean);
  122.     procedure SetPassword(const Value: string);
  123.     procedure SetServiceStartName(const Value: string);
  124.   protected
  125.     procedure Main(Argc: DWord; Argv: PLPSTR);
  126.     procedure Controller(CtrlCode: DWord);
  127.     procedure DoStart; virtual;
  128.     function DoStop: Boolean; virtual;
  129.     function DoPause: Boolean; virtual;
  130.     function DoContinue: Boolean; virtual;
  131.     procedure DoInterrogate; virtual;
  132.     procedure DoShutdown; virtual;
  133.     function DoCustomControl(CtrlCode: DWord): Boolean; virtual;
  134.   public
  135.     constructor CreateNew(AOwner: TComponent; Dummy: Integer); override;
  136.     destructor Destroy; override;
  137.     function GetServiceController: TServiceController; virtual; abstract;
  138.     procedure ReportStatus;
  139.     procedure LogMessage(Message: String; EventType: DWord = 1;
  140.       Category: Integer = 0; ID: Integer = 0);
  141.     property ErrCode: DWord read FErrCode write FErrCode;
  142.     property ParamCount: Integer read GetParamCount;
  143.     property Param[Index: Integer]: String read GetParam;
  144.     property ServiceThread: TServiceThread read FServiceThread;
  145.     property Status: TCurrentStatus read FStatus write SetStatus;
  146.     property Terminated: Boolean read GetTerminated;
  147.     property Win32ErrCode: DWord read FWin32ErrorCode write FWin32ErrorCode;
  148.   published
  149.     property AllowStop: Boolean read FAllowStop write FAllowStop default True;
  150.     property AllowPause: Boolean read FAllowPause write FAllowPause default True;
  151.     property Dependencies: TDependencies read FDependencies write SetDependencies stored AreDependenciesStored;
  152.     property DisplayName: String read GetDisplayName write FDisplayName;
  153.     property ErrorSeverity: TErrorSeverity read FErrorSeverity write FErrorSeverity default esNormal;
  154.     property Interactive: Boolean read FInteractive write SetInteractive default False;
  155.     property LoadGroup: String read FLoadGroup write FLoadGroup;
  156.     property Password: String read FPassword write SetPassword;
  157.     property ServiceStartName: String read FServiceStartName write SetServiceStartName;
  158.     property ServiceType: TServiceType read FServiceType write FServiceType default stWin32;
  159.     property StartType: TStartType read FStartType write FStartType default stAuto;
  160.     property TagID: DWord read FTagID write FTagID default 0;
  161.     property WaitHint: Integer read FWaitHint write FWaitHint default 5000;
  162.     property BeforeInstall: TServiceEvent read FBeforeInstall write FBeforeInstall;
  163.     property AfterInstall: TServiceEvent read FAfterInstall write FAfterInstall;
  164.     property BeforeUninstall: TServiceEvent read FBeforeUninstall write FBeforeUninstall;
  165.     property AfterUninstall: TServiceEvent read FAfterUninstall write FAfterUninstall;
  166.     property OnContinue: TContinueEvent read FOnContinue write SetOnContinue;
  167.     property OnExecute: TServiceEvent read FOnExecute write FOnExecute;
  168.     property OnPause: TPauseEvent read FOnPause write SetOnPause;
  169.     property OnShutdown: TServiceEvent read FOnShutdown write FOnShutdown;
  170.     property OnStart: TStartEvent read FOnStart write FOnStart;
  171.     property OnStop: TStopEvent read FOnStop write SetOnStop;
  172.   end;
  173.   { TServiceApplication }
  174.   TServiceApplication = class(TComponent)
  175.   private
  176.     FEventLogger: TEventLogger;
  177.     FTitle: string;
  178.     procedure OnExceptionHandler(Sender: TObject; E: Exception);
  179.     function GetServiceCount: Integer;
  180.   protected
  181.     procedure DoHandleException(E: Exception); dynamic;
  182.     procedure RegisterServices(Install, Silent: Boolean);
  183.     procedure DispatchServiceMain(Argc: DWord; Argv: PLPSTR);
  184.     function Hook(var Message: TMessage): Boolean;
  185.   public
  186.     constructor Create(AOwner: TComponent); override;
  187.     destructor Destroy; override;
  188.     property ServiceCount: Integer read GetServiceCount;
  189.     // The following uses the current behaviour of the IDE module manager
  190.     procedure CreateForm(InstanceClass: TComponentClass; var Reference); virtual;
  191.     procedure Initialize; virtual;
  192.     procedure Run; virtual;
  193.     property Title: string read FTitle write FTitle;
  194.   end;
  195. var
  196.   Application: TServiceApplication = nil;
  197. implementation
  198. uses
  199.   Forms, Dialogs, Consts;
  200. { TEventLogger }
  201. constructor TEventLogger.Create(Name: String);
  202. begin
  203.   FName := Name;
  204.   FEventLog := 0;
  205. end;
  206. destructor TEventLogger.Destroy;
  207. begin
  208.   if FEventLog <> 0 then
  209.     DeregisterEventSource(FEventLog);
  210.   inherited Destroy;
  211. end;
  212. procedure TEventLogger.LogMessage(Message: String; EventType: DWord;
  213.   Category: Word; ID: DWord);
  214. var
  215.   P: Pointer;
  216. begin
  217.   P := PChar(Message);
  218.   if FEventLog = 0 then
  219.     FEventLog := RegisterEventSource(nil, PChar(FName));
  220.   ReportEvent(FEventLog, EventType, Category, ID, nil, 1, 0, @P, nil);
  221. end;
  222. { TDependency }
  223. function TDependency.GetDisplayName: string;
  224. begin
  225.   if Name <> '' then
  226.     Result := Name else
  227.     Result := inherited GetDisplayName;
  228. end;
  229. { TDependencies }
  230. constructor TDependencies.Create(Owner: TPersistent);
  231. begin
  232.   FOwner := Owner;
  233.   inherited Create(TDependency);
  234. end;
  235. function TDependencies.GetItem(Index: Integer): TDependency;
  236. begin
  237.   Result := TDependency(inherited GetItem(Index));
  238. end;
  239. procedure TDependencies.SetItem(Index: Integer; Value: TDependency);
  240. begin
  241.   inherited SetItem(Index, TCollectionItem(Value));
  242. end;
  243. function TDependencies.GetOwner: TPersistent;
  244. begin
  245.   Result := FOwner;
  246. end;
  247. { TServiceThread }
  248. constructor TServiceThread.Create(Service: TService);
  249. begin
  250.   FService := Service;
  251.   inherited Create(True);
  252. end;
  253. procedure TServiceThread.Execute;
  254. var
  255.   msg: TMsg;
  256.   Started: Boolean;
  257. begin
  258.   PeekMessage(msg, 0, WM_USER, WM_USER, PM_NOREMOVE); { Create message queue }
  259.   try
  260.     FService.Status := csStartPending;
  261.     Started := True;
  262.     if Assigned(FService.OnStart) then FService.OnStart(FService, Started);
  263.     if not Started then Exit;
  264.     try
  265.       FService.Status := csRunning;
  266.       if Assigned(FService.OnExecute) then
  267.         FService.OnExecute(FService)
  268.       else
  269.         ProcessRequests(True);
  270.       ProcessRequests(False);
  271.     except
  272.       on E: Exception do
  273.         FService.LogMessage(Format(SServiceFailed,[SExecute, E.Message]));
  274.     end;
  275.   except
  276.     on E: Exception do
  277.       FService.LogMessage(Format(SServiceFailed,[SStart, E.Message]));
  278.   end;
  279. end;
  280. procedure TServiceThread.ProcessRequests(WaitForMessage: Boolean);
  281. const
  282.   ActionStr: array[1..5] of String = (SStop, SPause, SContinue, SInterrogate,
  283.     SShutdown);
  284. var
  285.   msg: TMsg;
  286.   OldStatus: TCurrentStatus;
  287.   ErrorMsg: String;
  288.   ActionOK, Rslt: Boolean;
  289. begin
  290.   while True do
  291.   begin
  292.     if Terminated and WaitForMessage then break;
  293.     if WaitForMessage then
  294.       Rslt := GetMessage(msg, 0, 0, 0)
  295.     else
  296.       Rslt := PeekMessage(msg, 0, 0, 0, PM_REMOVE);
  297.     if not Rslt then break;
  298.     if msg.hwnd = 0 then { Thread message }
  299.     begin
  300.       if msg.message = CM_SERVICE_CONTROL_CODE then
  301.       begin
  302.         OldStatus := FService.Status;
  303.         try
  304.           ActionOK := True;
  305.           case msg.wParam of
  306.             SERVICE_CONTROL_STOP: ActionOK := FService.DoStop;
  307.             SERVICE_CONTROL_PAUSE: ActionOK := FService.DoPause;
  308.             SERVICE_CONTROL_CONTINUE: ActionOK := FService.DoContinue;
  309.             SERVICE_CONTROL_SHUTDOWN: FService.DoShutDown;
  310.             SERVICE_CONTROL_INTERROGATE: FService.DoInterrogate;
  311.           else
  312.             ActionOK := FService.DoCustomControl(msg.wParam);
  313.           end;
  314.           if not ActionOK then
  315.             FService.Status := OldStatus;
  316.         except
  317.           on E: Exception do
  318.           begin
  319.             if msg.wParam <> SERVICE_CONTROL_SHUTDOWN then
  320.               FService.Status := OldStatus;
  321.             if msg.wParam in [1..5] then
  322.               ErrorMsg := Format(SServiceFailed, [ActionStr[msg.wParam], E.Message])
  323.             else
  324.               ErrorMsg := Format(SCustomError,[msg.wParam, E.Message]);
  325.             FService.LogMessage(ErrorMsg);
  326.           end;
  327.         end;
  328.       end else
  329.         DispatchMessage(msg);
  330.     end else
  331.       DispatchMessage(msg);
  332.   end;
  333. end;
  334. { TService }
  335. constructor TService.CreateNew(AOwner: TComponent; Dummy: Integer);
  336. begin
  337.   inherited CreateNew(AOwner);
  338.   FWaitHint := 5000;
  339.   FInteractive := False;
  340.   FServiceType := stWin32;
  341.   FParams := TStringList.Create;
  342.   FDependencies := TDependencies.Create(Self);
  343.   FErrorSeverity := esNormal;
  344.   FStartType := stAuto;
  345.   FTagID := 0;
  346.   FAllowStop := True;
  347.   FAllowPause := True;
  348. end;
  349. destructor TService.Destroy;
  350. begin
  351.   FDependencies.Free;
  352.   FParams.Free;
  353.   FEventLogger.Free;
  354.   inherited Destroy;
  355. end;
  356. function TService.GetDisplayName: String;
  357. begin
  358.   if FDisplayName <> '' then
  359.     Result := FDisplayName
  360.   else
  361.     Result := Name;
  362. end;
  363. procedure TService.SetInteractive(Value: Boolean);
  364. begin
  365.   if Value = FInteractive then Exit;
  366.   if Value then
  367.   begin
  368.     Password := '';
  369.     ServiceStartName := '';
  370.   end;
  371.   FInteractive := Value;
  372. end;
  373. procedure TService.SetPassword(const Value: string);
  374. begin
  375.   if Value = FPassword then Exit;
  376.   if Value <> '' then
  377.     Interactive := False;
  378.   FPassword := Value;
  379. end;
  380. procedure TService.SetServiceStartName(const Value: string);
  381. begin
  382.   if Value = FServiceStartName then Exit;
  383.   if Value <> '' then
  384.     Interactive := False;
  385.   FServiceStartName := Value;
  386. end;
  387. procedure TService.SetDependencies(Value: TDependencies);
  388. begin
  389.   FDependencies.Assign(Value);
  390. end;
  391. function TService.AreDependenciesStored: Boolean;
  392. begin
  393.   Result := FDependencies.Count > 0;
  394. end;
  395. function TService.GetParamCount: Integer;
  396. begin
  397.   Result := FParams.Count;
  398. end;
  399. function TService.GetParam(Index: Integer): String;
  400. begin
  401.   Result := FParams[Index];
  402. end;
  403. procedure TService.SetOnContinue(Value: TContinueEvent);
  404. begin
  405.   FOnContinue := Value;
  406.   AllowPause := True;
  407. end;
  408. procedure TService.SetOnPause(Value: TPauseEvent);
  409. begin
  410.   FOnPause := Value;
  411.   AllowPause := True;
  412. end;
  413. procedure TService.SetOnStop(Value: TStopEvent);
  414. begin
  415.   FOnStop := Value;
  416.   AllowStop := True;
  417. end;
  418. function TService.GetTerminated: Boolean;
  419. begin
  420.   Result := False;
  421.   if Assigned(FServiceThread) then
  422.     Result := FServiceThread.Terminated;
  423. end;
  424. function TService.GetNTDependencies: String;
  425. var
  426.   i, Len: Integer;
  427.   P: PChar;
  428. begin
  429.   Result := '';
  430.   Len := 0;
  431.   for i := 0 to Dependencies.Count - 1 do
  432.   begin
  433.     Inc(Len, Length(Dependencies[i].Name) + 1); // For null-terminator
  434.     if Dependencies[i].IsGroup then Inc(Len);
  435.   end;
  436.   if Len <> 0 then
  437.   begin
  438.     Inc(Len); // For final null-terminator;
  439.     SetLength(Result, Len);
  440.     P := @Result[1];
  441.     for i := 0 to Dependencies.Count - 1 do
  442.     begin
  443.       if Dependencies[i].IsGroup then
  444.       begin
  445.         P^ := SC_GROUP_IDENTIFIER;
  446.         Inc(P);
  447.       end;
  448.       P := StrECopy(P, PChar(Dependencies[i].Name));
  449.       Inc(P);
  450.     end;
  451.     P^ := #0;
  452.   end;
  453. end;
  454. function TService.GetNTServiceType: Integer;
  455. const
  456.   NTServiceType: array[TServiceType] of Integer = ( SERVICE_WIN32_OWN_PROCESS,
  457.     SERVICE_KERNEL_DRIVER, SERVICE_FILE_SYSTEM_DRIVER);
  458. begin
  459.   Result := NTServiceType[FServiceType];
  460.   if (FServiceType = stWin32) and Interactive then
  461.     Result := Result or SERVICE_INTERACTIVE_PROCESS;
  462.   if (FServiceType = stWin32) and (Application.ServiceCount > 1) then
  463.     Result := (Result xor SERVICE_WIN32_OWN_PROCESS) or SERVICE_WIN32_SHARE_PROCESS;
  464. end;
  465. function TService.GetNTStartType: Integer;
  466. const
  467.   NTStartType: array[TStartType] of Integer = (SERVICE_BOOT_START,
  468.     SERVICE_SYSTEM_START, SERVICE_AUTO_START, SERVICE_DEMAND_START,
  469.     SERVICE_DISABLED);
  470. begin
  471.   Result := NTStartType[FStartType];
  472.   if (FStartType in [stBoot, stSystem]) and (FServiceType <> stDevice) then
  473.     Result := SERVICE_AUTO_START;
  474. end;
  475. function TService.GetNTErrorSeverity: Integer;
  476. const
  477.   NTErrorSeverity: array[TErrorSeverity] of Integer = (SERVICE_ERROR_IGNORE,
  478.     SERVICE_ERROR_NORMAL, SERVICE_ERROR_SEVERE, SERVICE_ERROR_CRITICAL);
  479. begin
  480.   Result := NTErrorSeverity[FErrorSeverity];
  481. end;
  482. function TService.GetNTControlsAccepted: Integer;
  483. begin
  484.   Result := SERVICE_ACCEPT_SHUTDOWN;
  485.   if AllowStop then Result := Result or SERVICE_ACCEPT_STOP;
  486.   if AllowPause then Result := Result or SERVICE_ACCEPT_PAUSE_CONTINUE;
  487. end;
  488. procedure TService.LogMessage(Message: String; EventType: DWord; Category, ID: Integer);
  489. begin
  490.   if FEventLogger = nil then
  491.     FEventLogger := TEventLogger.Create(Name);
  492.   FEventLogger.LogMessage(Message, EventType, Category, ID);
  493. end;
  494. procedure TService.ReportStatus;
  495. const
  496.   LastStatus: TCurrentStatus = csStartPending;
  497.   NTServiceStatus: array[TCurrentStatus] of Integer = (SERVICE_STOPPED,
  498.     SERVICE_START_PENDING, SERVICE_STOP_PENDING, SERVICE_RUNNING,
  499.     SERVICE_CONTINUE_PENDING, SERVICE_PAUSE_PENDING, SERVICE_PAUSED);
  500.   PendingStatus: set of TCurrentStatus = [csStartPending, csStopPending,
  501.     csContinuePending, csPausePending];
  502. var
  503.   ServiceStatus: TServiceStatus;
  504. begin
  505.   with ServiceStatus do
  506.   begin
  507.     dwWaitHint := FWaitHint;
  508.     dwServiceType := GetNTServiceType;
  509.     if FStatus = csStartPending then
  510.       dwControlsAccepted := 0 else
  511.       dwControlsAccepted := GetNTControlsAccepted;
  512.     if (FStatus in PendingStatus) and (FStatus = LastStatus) then
  513.       Inc(dwCheckPoint) else
  514.       dwCheckPoint := 0;
  515.     LastStatus := FStatus;
  516.     dwCurrentState := NTServiceStatus[FStatus];
  517.     dwWin32ExitCode := Win32ErrCode;
  518.     dwServiceSpecificExitCode := ErrCode;
  519.     if ErrCode <> 0 then
  520.       dwWin32ExitCode := ERROR_SERVICE_SPECIFIC_ERROR;
  521.     if not SetServiceStatus(FStatusHandle, ServiceStatus) then
  522.       LogMessage(SysErrorMessage(GetLastError));
  523.   end;
  524. end;
  525. procedure TService.SetStatus(Value: TCurrentStatus);
  526. begin
  527.   FStatus := Value;
  528.   if not (csDesigning in ComponentState) then
  529.     ReportStatus;
  530. end;
  531. procedure TService.Main(Argc: DWord; Argv: PLPSTR);
  532. type
  533.   PPCharArray = ^TPCharArray;
  534.   TPCharArray = array [0..1024] of PChar;
  535. var
  536.   i: Integer;
  537.   Controller: TServiceController;
  538. begin
  539.   for i := 0 to Argc - 1 do
  540.     FParams.Add(PPCharArray(Argv)[i]);
  541.   Controller := GetServiceController();
  542.   FStatusHandle := RegisterServiceCtrlHandler(PChar(Name), @Controller);
  543.   if (FStatusHandle = 0) then
  544.     LogMessage(SysErrorMessage(GetLastError)) else
  545.     DoStart;
  546. end;
  547. procedure TService.Controller(CtrlCode: DWord);
  548. begin
  549.   PostThreadMessage(ServiceThread.ThreadID, CM_SERVICE_CONTROL_CODE, CtrlCode, 0);
  550.   if ServiceThread.Suspended then ServiceThread.Resume;
  551. end;
  552. procedure TService.DoStart;
  553. begin
  554.   try
  555.     Status := csStartPending;
  556.     try
  557.       FServiceThread := TServiceThread.Create(Self);
  558.       FServiceThread.Resume;
  559.       FServiceThread.WaitFor;
  560.       FreeAndNil(FServiceThread);
  561.     finally
  562.       Status := csStopped;
  563.     end;
  564.   except
  565.     on E: Exception do
  566.       LogMessage(Format(SServiceFailed,[SExecute, E.Message]));
  567.   end;
  568. end;
  569. function TService.DoStop: Boolean;
  570. begin
  571.   Result := True;
  572.   Status := csStopPending;
  573.   if Assigned(FOnStop) then FOnStop(Self, Result);
  574.   if Result then ServiceThread.Terminate;
  575. end;
  576. function TService.DoPause: Boolean;
  577. begin
  578.   Result := True;
  579.   Status := csPausePending;
  580.   if Assigned(FOnPause) then FOnPause(Self, Result);
  581.   if Result then
  582.   begin
  583.     Status := csPaused;
  584.     ServiceThread.Suspend;
  585.   end;
  586. end;
  587. function TService.DoContinue: Boolean;
  588. begin
  589.   Result := True;
  590.   Status := csContinuePending;
  591.   if Assigned(FOnContinue) then FOnContinue(Self, Result);
  592.   if Result then
  593.     Status := csRunning;
  594. end;
  595. procedure TService.DoInterrogate;
  596. begin
  597.   ReportStatus;
  598. end;
  599. procedure TService.DoShutdown;
  600. begin
  601.   Status := csStopPending;
  602.   try
  603.     if Assigned(FOnShutdown) then FOnShutdown(Self);
  604.   finally
  605.     { Shutdown cannot abort, it must stop regardless of any exception }
  606.     ServiceThread.Terminate;
  607.   end;
  608. end;
  609. function TService.DoCustomControl(CtrlCode: DWord): Boolean;
  610. begin
  611.   Result := True;
  612. end;
  613. { TServiceApplication }
  614. type
  615.   TServiceClass = class of TService;
  616. procedure ServiceMain(Argc: DWord; Argv: PLPSTR); stdcall;
  617. begin
  618.   Application.DispatchServiceMain(Argc, Argv);
  619. end;
  620. procedure DoneServiceApplication;
  621. begin
  622.   with Forms.Application do
  623.   begin
  624.     if Handle <> 0 then ShowOwnedPopups(Handle, False);
  625.     ShowHint := False;
  626.     Destroying;
  627.     DestroyComponents;
  628.   end;
  629.   with Application do
  630.   begin
  631.     Destroying;
  632.     DestroyComponents;
  633.   end;
  634. end;
  635. constructor TServiceApplication.Create(AOwner: TComponent);
  636. begin
  637.   inherited Create(AOwner);
  638.   FEventLogger := TEventLogger.Create(ExtractFileName(ParamStr(0)));
  639.   Forms.Application.HookMainWindow(Hook);
  640. end;
  641. destructor TServiceApplication.Destroy;
  642. begin
  643.   FEventLogger.Free;
  644.   Forms.Application.OnException := nil;
  645.   Forms.Application.UnhookMainWindow(Hook);
  646.   inherited Destroy;
  647. end;
  648. procedure TServiceApplication.DispatchServiceMain(Argc: DWord; Argv: PLPSTR);
  649. var
  650.   i: Integer;
  651. begin
  652.   for i := 0 to ComponentCount - 1 do
  653.     if (Components[i] is TService) and
  654.        (AnsiCompareText(PChar(Argv^), Components[i].Name) = 0) then
  655.     begin
  656.       TService(Components[i]).Main(Argc, Argv);
  657.       break;
  658.     end;
  659. end;
  660. function TServiceApplication.GetServiceCount: Integer;
  661. var
  662.   i: Integer;
  663. begin
  664.   Result := 0;
  665.   for i := 0 to ComponentCount - 1 do
  666.     if Components[i] is TService then
  667.       Inc(Result);
  668. end;
  669. procedure TServiceApplication.RegisterServices(Install, Silent: Boolean);
  670.   procedure InstallService(Service: TService; SvcMgr: Integer);
  671.   var
  672.     TmpTagID, Svc: Integer;
  673.     PTag, PSSN: Pointer;
  674.     Path: string;
  675.   begin
  676.     Path := ParamStr(0);
  677.     with Service do
  678.     begin
  679.       if Assigned(BeforeInstall) then BeforeInstall(Service);
  680.       TmpTagID := TagID;
  681.       if TmpTagID > 0 then PTag := @TmpTagID else PTag := nil;
  682.       if ServiceStartName = '' then
  683.         PSSN := nil else
  684.         PSSN := PChar(ServiceStartName);
  685.       Svc := CreateService(SvcMgr, PChar(Name), PChar(DisplayName),
  686.         SERVICE_ALL_ACCESS, GetNTServiceType, GetNTStartType, GetNTErrorSeverity,
  687.         PChar(Path), PChar(LoadGroup), PTag, PChar(GetNTDependencies),
  688.         PSSN, PChar(Password));
  689.       TagID := TmpTagID;
  690.       if Svc = 0 then
  691.         RaiseLastOSError;
  692.       try
  693.         try
  694.           if Assigned(AfterInstall) then AfterInstall(Service);
  695.         except
  696.           on E: Exception do
  697.           begin
  698.             DeleteService(Svc);
  699.             raise;
  700.           end;
  701.         end;
  702.       finally
  703.         CloseServiceHandle(Svc);
  704.       end;
  705.     end;
  706.   end;
  707.   procedure UninstallService(Service: TService; SvcMgr: Integer);
  708.   var
  709.     Svc: Integer;
  710.   begin
  711.     with Service do
  712.     begin
  713.       if Assigned(BeforeUninstall) then BeforeUninstall(Service);
  714.       Svc := OpenService(SvcMgr, PChar(Name), SERVICE_ALL_ACCESS);
  715.       if Svc = 0 then RaiseLastOSError;
  716.       try
  717.         if not DeleteService(Svc) then RaiseLastOSError;
  718.       finally
  719.         CloseServiceHandle(Svc);
  720.       end;
  721.       if Assigned(AfterUninstall) then AfterUninstall(Service);
  722.     end;
  723.   end;
  724. var
  725.   SvcMgr: Integer;
  726.   i: Integer;
  727.   Success: Boolean;
  728.   Msg: string;
  729. begin
  730.   Success := True;
  731.   SvcMgr := OpenSCManager(nil, nil, SC_MANAGER_ALL_ACCESS);
  732.   if SvcMgr = 0 then RaiseLastOSError;
  733.   try
  734.     for i := 0 to ComponentCount - 1 do
  735.       if Components[i] is TService then
  736.       try
  737.         if Install then
  738.           InstallService(TService(Components[i]), SvcMgr) else
  739.           UninstallService(TService(Components[i]), SvcMgr)
  740.       except
  741.         on E: Exception do
  742.         begin
  743.           Success := False;
  744.           if Install then
  745.             Msg := SServiceInstallFailed else
  746.             Msg := SServiceUninstallFailed;
  747.           with TService(Components[i]) do
  748.             MessageDlg(Format(Msg, [DisplayName, E.Message]), mtError, [mbOK],0);
  749.         end;
  750.       end;
  751.     if Success and not Silent then
  752.       if Install then
  753.         MessageDlg(SServiceInstallOK, mtInformation, [mbOk], 0) else
  754.         MessageDlg(SServiceUninstallOK, mtInformation, [mbOk], 0);
  755.   finally
  756.     CloseServiceHandle(SvcMgr);
  757.   end;
  758. end;
  759. function TServiceApplication.Hook(var Message: TMessage): Boolean;
  760. begin
  761.   Result := Message.Msg = WM_ENDSESSION;
  762. end;
  763. procedure TServiceApplication.CreateForm(InstanceClass: TComponentClass;
  764.   var Reference);
  765. begin
  766.   if InstanceClass.InheritsFrom(TService) then
  767.   begin
  768.     try
  769.       TComponent(Reference) := InstanceClass.Create(Self);
  770.     except
  771.       TComponent(Reference) := nil;
  772.       raise;
  773.     end;
  774.   end else
  775.     Forms.Application.CreateForm(InstanceClass, Reference);
  776. end;
  777. procedure TServiceApplication.DoHandleException(E: Exception);
  778. begin
  779.   FEventLogger.LogMessage(E.Message);
  780. end;
  781. procedure TServiceApplication.Initialize;
  782. begin
  783.   Forms.Application.ShowMainForm :=False;
  784.   Forms.Application.Initialize;
  785. end;
  786. procedure TServiceApplication.OnExceptionHandler(Sender: TObject; E: Exception);
  787. begin
  788.   DoHandleException(E);
  789. end;
  790. type
  791.   TServiceTableEntryArray = array of TServiceTableEntry;
  792.   TServiceStartThread = class(TThread)
  793.   private
  794.     FServiceStartTable: TServiceTableEntryArray;
  795.   protected
  796.     procedure DoTerminate; override;
  797.     procedure Execute; override;
  798.   public
  799.     constructor Create(Services: TServiceTableEntryArray);
  800.   end;
  801. constructor TServiceStartThread.Create(Services: TServiceTableEntryArray);
  802. begin
  803.   FreeOnTerminate := False;
  804.   ReturnValue := 0;
  805.   FServiceStartTable := Services;
  806.   inherited Create(False);
  807. end;
  808. procedure TServiceStartThread.DoTerminate;
  809. begin
  810.   inherited DoTerminate;
  811.   PostMessage(Forms.Application.Handle, WM_QUIT, 0, 0);
  812. end;
  813. procedure TServiceStartThread.Execute;
  814. begin
  815.   if StartServiceCtrlDispatcher(FServiceStartTable[0]) then
  816.     ReturnValue := 0
  817.   else
  818.     ReturnValue := GetLastError;
  819. end;
  820. procedure TServiceApplication.Run;
  821.   function FindSwitch(const Switch: string): Boolean;
  822.   begin
  823.     Result := FindCmdLineSwitch(Switch, ['-', '/'], True);
  824.   end;
  825. var
  826.   ServiceStartTable: TServiceTableEntryArray;
  827.   ServiceCount, i, J: Integer;
  828.   StartThread: TServiceStartThread;
  829. begin
  830.   AddExitProc(DoneServiceApplication);
  831.   if FindSwitch('INSTALL') then
  832.     RegisterServices(True, FindSwitch('SILENT'))
  833.   else if FindSwitch('UNINSTALL') then
  834.     RegisterServices(False, FindSwitch('SILENT'))
  835.   else
  836.   begin
  837.     Forms.Application.OnException := OnExceptionHandler;
  838.     ServiceCount := 0;
  839.     for i := 0 to ComponentCount - 1 do
  840.       if Components[i] is TService then Inc(ServiceCount);
  841.     SetLength(ServiceStartTable, ServiceCount + 1);
  842.     FillChar(ServiceStartTable[0], SizeOf(TServiceTableEntry) * (ServiceCount + 1), 0);
  843.     J := 0;
  844.     for i := 0 to ComponentCount - 1 do
  845.       if Components[i] is TService then
  846.       begin
  847.         ServiceStartTable[J].lpServiceName := PChar(Components[i].Name);
  848.         ServiceStartTable[J].lpServiceProc := @ServiceMain;
  849.         Inc(J);
  850.       end;
  851.     StartThread := TServiceStartThread.Create(ServiceStartTable);
  852.     try
  853.       while not Forms.Application.Terminated do
  854.         Forms.Application.HandleMessage;
  855.       Forms.Application.Terminate;
  856.       if StartThread.ReturnValue <> 0 then
  857.         FEventLogger.LogMessage(SysErrorMessage(StartThread.ReturnValue));
  858.     finally
  859.       StartThread.Free;
  860.     end;
  861.   end;
  862. end;
  863. procedure InitApplication;
  864. begin
  865.   Application := TServiceApplication.Create(nil);
  866. end;
  867. procedure DoneApplication;
  868. begin
  869.   Application.Free;
  870.   Application := nil;
  871. end;
  872. initialization
  873.   InitApplication;
  874. finalization
  875.   DoneApplication;
  876. end.