1
0
mirror of synced 2024-09-19 17:56:09 +00:00

Added thread for running the service in GUIContext

Added Elevation check for install / uninstall
This commit is contained in:
Mark van Renswoude 2016-10-24 17:15:03 +02:00
parent b2e6510d19
commit 360f06ae74
4 changed files with 227 additions and 51 deletions

View File

@ -6,15 +6,13 @@ uses
Vcl.Controls, Vcl.Controls,
Vcl.ExtCtrls, Vcl.ExtCtrls,
Vcl.Forms, Vcl.Forms,
Vcl.Graphics,
Vcl.StdCtrls, Vcl.StdCtrls,
Winapi.Messages, Winapi.Messages,
X2UtService.Intf; X2UtService.Intf;
const
CM_AFTERSHOW = WM_USER + 1;
type type
TX2ServiceContextGUIForm = class(TForm) TX2ServiceContextGUIForm = class(TForm)
btnClose: TButton; btnClose: TButton;
@ -33,12 +31,15 @@ type
private private
FContext: IX2ServiceContext; FContext: IX2ServiceContext;
FService: IX2Service; FService: IX2Service;
FServiceThread: TThread;
FAllowClose: Boolean;
protected protected
procedure DoShow; override; procedure DoShow; override;
procedure CMAfterShow(var Msg: TMessage); message CM_AFTERSHOW;
function GetControlCode: Byte; function GetControlCode: Byte;
procedure SetStatus(const AMessage: string; AColor: TColor);
property ServiceThread: TThread read FServiceThread;
public public
property Context: IX2ServiceContext read FContext write FContext; property Context: IX2ServiceContext read FContext write FContext;
property Service: IX2Service read FService write FService; property Service: IX2Service read FService write FService;
@ -47,9 +48,10 @@ type
implementation implementation
uses uses
System.Generics.Collections,
System.Math, System.Math,
System.SyncObjs,
System.SysUtils, System.SysUtils,
Vcl.Graphics,
Winapi.Windows; Winapi.Windows;
@ -63,36 +65,87 @@ const
StatusColorStopped = clRed; StatusColorStopped = clRed;
// #ToDo1 -oMvR: 21-10-2016: separate service handling out to thread to prevent blocking of the UI type
TX2ServiceThread = class(TThread)
private
FContext: IX2ServiceContext;
FService: IX2Service;
FWakeEvent: TEvent;
FSendCodeList: TList<Integer>;
FOnStarted: TThreadProcedure;
FOnStartFailed: TThreadProcedure;
FOnStopped: TThreadProcedure;
FOnStopFailed: TThreadProcedure;
protected
procedure Execute; override;
procedure TerminatedSet; override;
procedure FlushSendCodeList;
property Context: IX2ServiceContext read FContext;
property Service: IX2Service read FService;
property WakeEvent: TEvent read FWakeEvent;
public
constructor Create(AContext: IX2ServiceContext; AService: IX2Service);
destructor Destroy; override;
procedure SendControlCode(ACode: Byte);
property OnStarted: TThreadProcedure read FOnStarted write FOnStarted;
property OnStartFailed: TThreadProcedure read FOnStartFailed write FOnStartFailed;
property OnStopped: TThreadProcedure read FOnStopped write FOnStopped;
property OnStopFailed: TThreadProcedure read FOnStopFailed write FOnStopFailed;
end;
{ TX2ServiceContextGUIForm } { TX2ServiceContextGUIForm }
procedure TX2ServiceContextGUIForm.DoShow; procedure TX2ServiceContextGUIForm.DoShow;
var
serviceThread: TX2ServiceThread;
begin begin
inherited DoShow; inherited DoShow;
PostMessage(Self.Handle, CM_AFTERSHOW, 0, 0); if not Assigned(FServiceThread) then
end;
procedure TX2ServiceContextGUIForm.CMAfterShow(var Msg: TMessage);
begin
shpStatus.Brush.Color := StatusColorStarting;
lblStatus.Caption := 'Starting...';
Application.ProcessMessages;
if Service.Start(Context) then
begin begin
shpStatus.Brush.Color := StatusColorStarted; SetStatus('Starting...', StatusColorStarting);
lblStatus.Caption := 'Started'; serviceThread := TX2ServiceThread.Create(Context, Service);
end else serviceThread.OnStarted :=
procedure
begin begin
shpStatus.Brush.Color := StatusColorStopped; SetStatus('Started', StatusColorStarted);
lblStatus.Caption := 'Failed to start'; end;
serviceThread.OnStartFailed :=
procedure
begin
SetStatus('Start failed', StatusColorStopped);
FServiceThread := nil;
end;
serviceThread.OnStopped :=
procedure
begin
SetStatus('Stopped', StatusColorStopped);
FAllowClose := True;
Close;
end;
serviceThread.OnStopFailed :=
procedure
begin
SetStatus('Stop failed', StatusColorStarted);
end;
FServiceThread := serviceThread;
FServiceThread.Start;
end; end;
end; end;
procedure TX2ServiceContextGUIForm.edtControlCodeChange(Sender: TObject); procedure TX2ServiceContextGUIForm.edtControlCodeChange(Sender: TObject);
begin begin
edtControlCode.Text := IntToStr(GetControlCode); edtControlCode.Text := IntToStr(GetControlCode);
@ -101,7 +154,7 @@ end;
procedure TX2ServiceContextGUIForm.btnSendClick(Sender: TObject); procedure TX2ServiceContextGUIForm.btnSendClick(Sender: TObject);
begin begin
Service.DoCustomControl(GetControlCode); (ServiceThread as TX2ServiceThread).SendControlCode(GetControlCode);
end; end;
@ -113,14 +166,13 @@ end;
procedure TX2ServiceContextGUIForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean); procedure TX2ServiceContextGUIForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin begin
shpStatus.Brush.Color := StatusColorStopping; if not FAllowClose then
lblStatus.Caption := 'Stopping...'; begin
Application.ProcessMessages; SetStatus('Stopping...', StatusColorStopping);
CanClose := False;
CanClose := Service.Stop; ServiceThread.Terminate;
end;
if not CanClose then
lblStatus.Caption := 'Failed to stop';
end; end;
@ -129,4 +181,109 @@ begin
Result := Byte(Min(Max(StrToIntDef(edtControlCode.Text, 0), 128), 255)); Result := Byte(Min(Max(StrToIntDef(edtControlCode.Text, 0), 128), 255));
end; end;
procedure TX2ServiceContextGUIForm.SetStatus(const AMessage: string; AColor: TColor);
begin
shpStatus.Brush.Color := AColor;
lblStatus.Caption := AMessage;
end;
{ TX2ServiceThread }
constructor TX2ServiceThread.Create(AContext: IX2ServiceContext; AService: IX2Service);
begin
inherited Create(True);
FContext := AContext;
FService := AService;
FWakeEvent := TEvent.Create(nil, False, False, '');
FSendCodeList := TList<Integer>.Create;
end;
destructor TX2ServiceThread.Destroy;
begin
FreeAndNil(FWakeEvent);
FreeAndNil(FSendCodeList);
inherited Destroy;
end;
procedure TX2ServiceThread.Execute;
begin
try
Service.Start(Context);
except
if Assigned(FOnStartFailed) then
Synchronize(FOnStartFailed);
exit;
end;
if Assigned(FOnStarted) then
Synchronize(FOnStarted);
while True do
begin
try
WakeEvent.WaitFor(INFINITE);
if Terminated then
begin
Service.Stop;
if Assigned(FOnStopped) then
Synchronize(FOnStopped);
break;
end;
FlushSendCodeList;
except
if Assigned(FOnStopFailed) then
Synchronize(FOnStopFailed);
end;
end;
end;
procedure TX2ServiceThread.FlushSendCodeList;
var
code: Byte;
begin
System.TMonitor.Enter(FSendCodeList);
try
for code in FSendCodeList do
Service.DoCustomControl(code);
FSendCodeList.Clear;
finally
System.TMonitor.Exit(FSendCodeList);
end;
end;
procedure TX2ServiceThread.TerminatedSet;
begin
inherited TerminatedSet;
WakeEvent.SetEvent;
end;
procedure TX2ServiceThread.SendControlCode(ACode: Byte);
begin
System.TMonitor.Enter(FSendCodeList);
try
FSendCodeList.Add(ACode);
finally
System.TMonitor.Exit(FSendCodeList);
end;
WakeEvent.SetEvent;
end;
end. end.

View File

@ -19,6 +19,8 @@ type
implementation implementation
uses uses
Vcl.Forms,
X2UtService.GUIContext.Form; X2UtService.GUIContext.Form;
@ -38,16 +40,18 @@ end;
procedure TX2ServiceContextGUI.StartService(AService: IX2Service); procedure TX2ServiceContextGUI.StartService(AService: IX2Service);
begin var
with TX2ServiceContextGUIForm.Create(nil) do serviceForm: TX2ServiceContextGUIForm;
try
Caption := AService.DisplayName;
Service := AService;
ShowModal; begin
finally Application.Initialize;
Free; Application.MainFormOnTaskBar := True;
end;
Application.CreateForm(TX2ServiceContextGUIForm, serviceForm);
serviceForm.Caption := AService.DisplayName;
serviceForm.Service := AService;
Application.Run;
end; end;
end. end.

View File

@ -10,6 +10,8 @@ type
protected protected
procedure StartService(AService: IX2Service); virtual; procedure StartService(AService: IX2Service); virtual;
public public
class function IsInstallUninstall: Boolean;
constructor Create(AService: IX2Service); constructor Create(AService: IX2Service);
{ IX2ServiceContext } { IX2ServiceContext }
@ -20,7 +22,10 @@ type
implementation implementation
uses uses
System.Classes, System.Classes,
Vcl.SvcMgr; System.SysUtils,
Vcl.SvcMgr,
X2UtElevation;
type type
@ -56,9 +61,21 @@ end;
{ TX2ServiceContextService } { TX2ServiceContextService }
class function TX2ServiceContextService.IsInstallUninstall: Boolean;
begin
Result := FindCmdLineSwitch('install', ['-', '/'], True) or
FindCmdLineSwitch('uninstall', ['-', '/'], True);
end;
constructor TX2ServiceContextService.Create(AService: IX2Service); constructor TX2ServiceContextService.Create(AService: IX2Service);
begin begin
inherited Create; inherited Create;
if IsInstallUninstall and (not IsElevated) then
raise Exception.Create('Elevation is required for install or uninstall');
StartService(AService);
end; end;
@ -76,8 +93,8 @@ begin
Application.Initialize; Application.Initialize;
ServiceModuleInstance := TX2ServiceModule.Create(Application, Self, AService); ServiceModuleInstance := TX2ServiceModule.Create(Application, Self, AService);
try try
ServiceModuleInstance.DisplayName := '';// ServiceModuleInstance.DisplayName := AService.DisplayName;
ServiceModuleInstance.ServiceStartName := '';// ServiceModuleInstance.Name := AService.ServiceName;
Application.Run; Application.Run;
finally finally
@ -89,7 +106,8 @@ end;
{ TX2ServiceModule } { TX2ServiceModule }
constructor TX2ServiceModule.Create(AOwner: TComponent; AContext: IX2ServiceContext; AService: IX2Service); constructor TX2ServiceModule.Create(AOwner: TComponent; AContext: IX2ServiceContext; AService: IX2Service);
begin begin
inherited Create(AOwner); // Skip default constructor to prevent DFM streaming
CreateNew(AOwner);
FContext := AContext; FContext := AContext;
FService := AService; FService := AService;

View File

@ -53,12 +53,9 @@ end;
{ TX2Service } { TX2Service }
class function TX2Service.Run(AService: IX2Service): IX2ServiceContext; class function TX2Service.Run(AService: IX2Service): IX2ServiceContext;
begin begin
if FindCmdLineSwitch('install', ['-', '/'], True) or if TX2ServiceContextService.IsInstallUninstall or (not IsUserInteractive) then
FindCmdLineSwitch('uninstall', ['-', '/'], True) or Result := TX2ServiceContextService.Create(AService)
(not IsUserInteractive) then else
begin
Result := TX2ServiceContextService.Create(AService);
end else
Result := TX2ServiceContextGUI.Create(AService); Result := TX2ServiceContextGUI.Create(AService);
end; end;