diff --git a/.gitignore b/.gitignore
index bc36587..5127620 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,3 @@
-__history/
-*.local
-*.identcache
+__history/
+*.local
+*.identcache
diff --git a/Packages/D10/X2Utils.dpk b/Packages/D10/X2Utils.dpk
new file mode 100644
index 0000000..d669045
--- /dev/null
+++ b/Packages/D10/X2Utils.dpk
@@ -0,0 +1,70 @@
+package X2Utils;
+
+{$R *.res}
+{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users}
+{$ALIGN 8}
+{$ASSERTIONS ON}
+{$BOOLEVAL OFF}
+{$DEBUGINFO OFF}
+{$EXTENDEDSYNTAX ON}
+{$IMPORTEDDATA ON}
+{$IOCHECKS ON}
+{$LOCALSYMBOLS ON}
+{$LONGSTRINGS ON}
+{$OPENSTRINGS ON}
+{$OPTIMIZATION ON}
+{$OVERFLOWCHECKS OFF}
+{$RANGECHECKS OFF}
+{$REFERENCEINFO ON}
+{$SAFEDIVIDE OFF}
+{$STACKFRAMES ON}
+{$TYPEDADDRESS OFF}
+{$VARSTRINGCHECKS ON}
+{$WRITEABLECONST ON}
+{$MINENUMSIZE 1}
+{$IMAGEBASE $400000}
+{$ENDIF IMPLICITBUILDING}
+{$DESCRIPTION 'X2Utils'}
+{$LIBSUFFIX 'D10'}
+{$RUNONLY}
+{$IMPLICITBUILD ON}
+
+requires
+ rtl,
+ vcl,
+ xmlrtl;
+
+contains
+ X2UtApp in '..\..\X2UtApp.pas',
+ X2UtBits in '..\..\X2UtBits.pas',
+ X2UtGraphics in '..\..\X2UtGraphics.pas',
+ X2UtHandCursor in '..\..\X2UtHandCursor.pas',
+ X2UtHashes in '..\..\X2UtHashes.pas',
+ X2UtHashesVariants in '..\..\X2UtHashesVariants.pas',
+ X2UtMisc in '..\..\X2UtMisc.pas',
+ X2UtOS in '..\..\X2UtOS.pas',
+ X2UtStrings in '..\..\X2UtStrings.pas',
+ X2UtImageInfo in '..\..\X2UtImageInfo.pas',
+ X2UtTempFile in '..\..\X2UtTempFile.pas',
+ X2UtIniParser in '..\..\X2UtIniParser.pas',
+ X2UtProcess in '..\..\X2UtProcess.pas',
+ X2UtSingleInstance in '..\..\X2UtSingleInstance.pas',
+ X2UtStreams in '..\..\X2UtStreams.pas',
+ X2UtNamedFormat in '..\..\X2UtNamedFormat.pas',
+ X2UtPersist in '..\..\X2UtPersist.pas',
+ X2UtPersistForm in '..\..\X2UtPersistForm.pas',
+ X2UtPersistIntf in '..\..\X2UtPersistIntf.pas',
+ X2UtPersistRegistry in '..\..\X2UtPersistRegistry.pas',
+ X2UtElevation in '..\..\X2UtElevation.pas',
+ X2UtPersistXML in '..\..\X2UtPersistXML.pas',
+ X2UtPersistXMLBinding in '..\..\X2UtPersistXMLBinding.pas',
+ XMLDataBindingUtils in '..\..\XMLDataBindingUtils.pas',
+ X2UtDelphiCompatibility in '..\..\X2UtDelphiCompatibility.pas',
+ X2UtCursors in '..\..\X2UtCursors.pas',
+ X2UtService.GUIContext.Form in '..\..\X2UtService.GUIContext.Form.pas' {X2ServiceContextGUIForm},
+ X2UtService.GUIContext in '..\..\X2UtService.GUIContext.pas',
+ X2UtService.Intf in '..\..\X2UtService.Intf.pas',
+ X2UtService in '..\..\X2UtService.pas',
+ X2UtService.ServiceContext in '..\..\X2UtService.ServiceContext.pas';
+
+end.
diff --git a/Packages/D10/X2Utils.dproj b/Packages/D10/X2Utils.dproj
new file mode 100644
index 0000000..9483666
--- /dev/null
+++ b/Packages/D10/X2Utils.dproj
@@ -0,0 +1,241 @@
+
+
+ {3cd28184-f9a5-4320-9ad8-80ef25ba762e}
+ X2Utils.dpk
+ Debug
+ DCC32
+ P:\algemeen\bin\D2007\X2Utils2007.bpl
+ VCL
+ 18.2
+ True
+ Debug
+ Win32
+ 3
+ Package
+
+
+ true
+
+
+ true
+ Base
+ true
+
+
+ true
+ Base
+ true
+
+
+ true
+ Base
+ true
+
+
+ true
+ Base
+ true
+
+
+ true
+ Cfg_1
+ true
+ true
+
+
+ true
+ Cfg_1
+ true
+ true
+
+
+ true
+ Base
+ true
+
+
+ true
+ Cfg_2
+ true
+ true
+
+
+ true
+ Cfg_2
+ true
+ true
+
+
+ true
+ true
+ 00400000
+ true
+ X2Utils
+ Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;System;Xml;Data;Datasnap;Web;Soap;Winapi;System.Win;$(DCC_Namespace)
+ true
+ 1043
+ true
+ CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=
+ D10
+ true
+ X2Utils
+
+
+ package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey=
+ Debug
+ false
+ android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services.dex.jar
+
+
+ $(DELPHIBIN)
+ $(DELPHILIB)
+ $(DELPHIBIN)
+ X2Utils_Icon.ico
+ Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)
+ 1033
+
+
+ Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace)
+ $(DELPHIBIN64)
+ 1033
+ $(DELPHIBIN64)
+ $(DELPHILIB64)
+ X2Utils_Icon.ico
+
+
+ 7.0
+ 0
+ True
+ False
+ True
+ 0
+ RELEASE;$(DCC_Define)
+
+
+ 1033
+
+
+ 1033
+
+
+ 7.0
+ True
+ True
+ $(DELPHILIB)
+ $(DELPHILIB)
+
+
+ 1033
+
+
+ 1033
+
+
+ Delphi.Personality.12
+ Package
+
+
+
+ False
+ True
+ False
+
+
+ True
+ False
+ 1
+ 0
+ 0
+ 0
+ False
+ False
+ False
+ False
+ False
+ 1043
+ 1252
+
+
+
+
+ 1.0.0.0
+
+
+
+
+
+ 1.0.0.0
+
+
+
+
+ Microsoft Office 2000 Sample Automation Server Wrapper Components
+ Microsoft Office XP Sample Automation Server Wrapper Components
+
+
+
+ False
+ True
+ True
+
+
+ 12
+
+
+
+
+ MainSource
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Cfg_2
+ Base
+
+
+ Base
+
+
+ Cfg_1
+ Base
+
+
+
+
+
diff --git a/Packages/D10/X2Utils.res b/Packages/D10/X2Utils.res
new file mode 100644
index 0000000..df7d058
Binary files /dev/null and b/Packages/D10/X2Utils.res differ
diff --git a/Packages/D10/X2Utils.stat b/Packages/D10/X2Utils.stat
new file mode 100644
index 0000000..822d5bf
--- /dev/null
+++ b/Packages/D10/X2Utils.stat
@@ -0,0 +1,10 @@
+[Stats]
+EditorSecs=43
+DesignerSecs=1
+InspectorSecs=1
+CompileSecs=1667
+OtherSecs=12
+StartTime=6-7-2017 15:40:36
+RealKeys=0
+EffectiveKeys=0
+DebugSecs=1
diff --git a/Packages/D10/X2Utils_Icon.ico b/Packages/D10/X2Utils_Icon.ico
new file mode 100644
index 0000000..7ade9bf
Binary files /dev/null and b/Packages/D10/X2Utils_Icon.ico differ
diff --git a/X2UtElevation.pas b/X2UtElevation.pas
index d749888..bc53f72 100644
--- a/X2UtElevation.pas
+++ b/X2UtElevation.pas
@@ -336,11 +336,11 @@ begin
end;
except
on E: Exception do
- {$IFDEF VER230}
+ {$IF CompilerVersion >= 23}
raise EOleRegistrationError.Create(E.Message, 0, 0);
{$ELSE}
raise EOleRegistrationError.Create(E.Message);
- {$ENDIF}
+ {$IFEND}
end;
end;
diff --git a/X2UtService.GUIContext.Form.dfm b/X2UtService.GUIContext.Form.dfm
index b0cf4a8..5bcdc8d 100644
--- a/X2UtService.GUIContext.Form.dfm
+++ b/X2UtService.GUIContext.Form.dfm
@@ -1,132 +1,132 @@
-object X2ServiceContextGUIForm: TX2ServiceContextGUIForm
- Left = 0
- Top = 0
- BorderIcons = [biSystemMenu, biMinimize]
- BorderStyle = bsSingle
- Caption = 'X2ServiceContextGUIForm'
- ClientHeight = 204
- ClientWidth = 439
- Color = clBtnFace
- Font.Charset = DEFAULT_CHARSET
- Font.Color = clWindowText
- Font.Height = -11
- Font.Name = 'Tahoma'
- Font.Style = []
- OldCreateOrder = False
- Position = poScreenCenter
- OnCloseQuery = FormCloseQuery
- OnCreate = FormCreate
- DesignSize = (
- 439
- 204)
- PixelsPerInch = 96
- TextHeight = 13
- object btnClose: TButton
- Left = 8
- Top = 171
- Width = 75
- Height = 25
- Anchors = [akLeft, akBottom]
- Caption = '&Close'
- TabOrder = 0
- OnClick = btnCloseClick
- end
- object gbStatus: TGroupBox
- AlignWithMargins = True
- Left = 8
- Top = 8
- Width = 423
- Height = 57
- Margins.Left = 8
- Margins.Top = 8
- Margins.Right = 8
- Margins.Bottom = 0
- Align = alTop
- Caption = ' Status '
- TabOrder = 1
- ExplicitWidth = 358
- object lblStatus: TLabel
- Left = 34
- Top = 26
- Width = 50
- Height = 13
- Caption = 'Starting...'
- end
- object shpStatus: TShape
- Left = 12
- Top = 24
- Width = 16
- Height = 16
- Brush.Color = 33023
- Shape = stCircle
- end
- end
- object gbCustomControl: TGroupBox
- AlignWithMargins = True
- Left = 8
- Top = 73
- Width = 423
- Height = 88
- Margins.Left = 8
- Margins.Top = 8
- Margins.Right = 8
- Margins.Bottom = 0
- Align = alTop
- Caption = ' Custom control '
- TabOrder = 2
- ExplicitWidth = 358
- DesignSize = (
- 423
- 88)
- object lblControlCode: TLabel
- Left = 12
- Top = 27
- Width = 25
- Height = 13
- Caption = 'Code'
- end
- object edtControlCode: TEdit
- Left = 72
- Top = 24
- Width = 256
- Height = 21
- Anchors = [akLeft, akTop, akRight]
- TabOrder = 0
- Text = '128'
- OnChange = edtControlCodeChange
- ExplicitWidth = 191
- end
- object btnSend: TButton
- Left = 334
- Top = 24
- Width = 75
- Height = 21
- Anchors = [akTop, akRight]
- Caption = '&Send'
- TabOrder = 1
- OnClick = btnSendClick
- ExplicitLeft = 269
- end
- object cmbControlCodePredefined: TComboBox
- Left = 72
- Top = 51
- Width = 256
- Height = 21
- Style = csDropDownList
- Anchors = [akLeft, akTop, akRight]
- TabOrder = 2
- ExplicitWidth = 220
- end
- object btnSendPredefined: TButton
- Left = 334
- Top = 51
- Width = 75
- Height = 21
- Anchors = [akTop, akRight]
- Caption = '&Send'
- TabOrder = 3
- OnClick = btnSendPredefinedClick
- ExplicitLeft = 269
- end
- end
-end
+object X2ServiceContextGUIForm: TX2ServiceContextGUIForm
+ Left = 0
+ Top = 0
+ BorderIcons = [biSystemMenu, biMinimize]
+ BorderStyle = bsSingle
+ Caption = 'X2ServiceContextGUIForm'
+ ClientHeight = 204
+ ClientWidth = 439
+ Color = clBtnFace
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clWindowText
+ Font.Height = -11
+ Font.Name = 'Tahoma'
+ Font.Style = []
+ OldCreateOrder = False
+ Position = poScreenCenter
+ OnCloseQuery = FormCloseQuery
+ OnCreate = FormCreate
+ DesignSize = (
+ 439
+ 204)
+ PixelsPerInch = 96
+ TextHeight = 13
+ object btnClose: TButton
+ Left = 8
+ Top = 171
+ Width = 75
+ Height = 25
+ Anchors = [akLeft, akBottom]
+ Caption = '&Close'
+ TabOrder = 0
+ OnClick = btnCloseClick
+ end
+ object gbStatus: TGroupBox
+ AlignWithMargins = True
+ Left = 8
+ Top = 8
+ Width = 423
+ Height = 57
+ Margins.Left = 8
+ Margins.Top = 8
+ Margins.Right = 8
+ Margins.Bottom = 0
+ Align = alTop
+ Caption = ' Status '
+ TabOrder = 1
+ ExplicitWidth = 358
+ object lblStatus: TLabel
+ Left = 34
+ Top = 26
+ Width = 50
+ Height = 13
+ Caption = 'Starting...'
+ end
+ object shpStatus: TShape
+ Left = 12
+ Top = 24
+ Width = 16
+ Height = 16
+ Brush.Color = 33023
+ Shape = stCircle
+ end
+ end
+ object gbCustomControl: TGroupBox
+ AlignWithMargins = True
+ Left = 8
+ Top = 73
+ Width = 423
+ Height = 88
+ Margins.Left = 8
+ Margins.Top = 8
+ Margins.Right = 8
+ Margins.Bottom = 0
+ Align = alTop
+ Caption = ' Custom control '
+ TabOrder = 2
+ ExplicitWidth = 358
+ DesignSize = (
+ 423
+ 88)
+ object lblControlCode: TLabel
+ Left = 12
+ Top = 27
+ Width = 25
+ Height = 13
+ Caption = 'Code'
+ end
+ object edtControlCode: TEdit
+ Left = 72
+ Top = 24
+ Width = 256
+ Height = 21
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 0
+ Text = '128'
+ OnChange = edtControlCodeChange
+ ExplicitWidth = 191
+ end
+ object btnSend: TButton
+ Left = 334
+ Top = 24
+ Width = 75
+ Height = 21
+ Anchors = [akTop, akRight]
+ Caption = '&Send'
+ TabOrder = 1
+ OnClick = btnSendClick
+ ExplicitLeft = 269
+ end
+ object cmbControlCodePredefined: TComboBox
+ Left = 72
+ Top = 51
+ Width = 256
+ Height = 21
+ Style = csDropDownList
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 2
+ ExplicitWidth = 220
+ end
+ object btnSendPredefined: TButton
+ Left = 334
+ Top = 51
+ Width = 75
+ Height = 21
+ Anchors = [akTop, akRight]
+ Caption = '&Send'
+ TabOrder = 3
+ OnClick = btnSendPredefinedClick
+ ExplicitLeft = 269
+ end
+ end
+end
diff --git a/X2UtService.GUIContext.Form.pas b/X2UtService.GUIContext.Form.pas
index 8821114..00a23a2 100644
--- a/X2UtService.GUIContext.Form.pas
+++ b/X2UtService.GUIContext.Form.pas
@@ -1,342 +1,342 @@
-unit X2UtService.GUIContext.Form;
-
-interface
-uses
- System.Classes,
- Vcl.Controls,
- Vcl.ExtCtrls,
- Vcl.Forms,
- Vcl.Graphics,
- Vcl.StdCtrls,
- Winapi.Messages,
-
- X2UtService.Intf;
-
-
-type
- TX2ServiceContextGUIForm = class(TForm)
- btnClose: TButton;
- gbStatus: TGroupBox;
- lblStatus: TLabel;
- shpStatus: TShape;
- gbCustomControl: TGroupBox;
- lblControlCode: TLabel;
- edtControlCode: TEdit;
- btnSend: TButton;
- cmbControlCodePredefined: TComboBox;
- btnSendPredefined: TButton;
-
- procedure FormCreate(Sender: TObject);
- procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
- procedure edtControlCodeChange(Sender: TObject);
- procedure btnSendClick(Sender: TObject);
- procedure btnSendPredefinedClick(Sender: TObject);
- procedure btnCloseClick(Sender: TObject);
- private
- FContext: IX2ServiceContext;
- FService: IX2Service;
- FServiceThread: TThread;
- FAllowClose: Boolean;
- protected
- procedure DoShow; override;
-
- procedure UpdatePredefinedControlCodes; virtual;
-
- function GetControlCode: Byte;
- procedure SetStatus(const AMessage: string; AColor: TColor);
-
- property ServiceThread: TThread read FServiceThread;
- public
- property Context: IX2ServiceContext read FContext write FContext;
- property Service: IX2Service read FService write FService;
- end;
-
-
-implementation
-uses
- System.Generics.Collections,
- System.Math,
- System.SyncObjs,
- System.SysUtils,
- Winapi.Windows;
-
-
-{$R *.dfm}
-
-
-const
- StatusColorStarting = $00B0FFB0;
- StatusColorStarted = clGreen;
- StatusColorStopping = $008080FF;
- StatusColorStopped = clRed;
-
-
-type
- TX2ServiceThread = class(TThread)
- private
- FContext: IX2ServiceContext;
- FService: IX2Service;
- FWakeEvent: TEvent;
- FSendCodeList: TList;
-
- 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 }
-procedure TX2ServiceContextGUIForm.FormCreate(Sender: TObject);
-begin
- btnClose.Left := (ClientWidth - btnClose.Width) div 2;
-end;
-
-
-procedure TX2ServiceContextGUIForm.DoShow;
-var
- serviceThread: TX2ServiceThread;
-begin
- inherited DoShow;
-
- if not Assigned(FServiceThread) then
- begin
- UpdatePredefinedControlCodes;
-
- SetStatus('Starting...', StatusColorStarting);
- serviceThread := TX2ServiceThread.Create(Context, Service);
- serviceThread.OnStarted :=
- procedure
- begin
- SetStatus('Started', StatusColorStarted);
- 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;
-
-
-
-procedure TX2ServiceContextGUIForm.edtControlCodeChange(Sender: TObject);
-begin
- edtControlCode.Text := IntToStr(GetControlCode);
-end;
-
-
-procedure TX2ServiceContextGUIForm.btnSendClick(Sender: TObject);
-begin
- (ServiceThread as TX2ServiceThread).SendControlCode(GetControlCode);
-end;
-
-
-procedure TX2ServiceContextGUIForm.btnSendPredefinedClick(Sender: TObject);
-var
- code: Byte;
-
-begin
- if cmbControlCodePredefined.ItemIndex > -1 then
- begin
- code := Byte(cmbControlCodePredefined.Items.Objects[cmbControlCodePredefined.ItemIndex]);
- (ServiceThread as TX2ServiceThread).SendControlCode(code);
- end;
-end;
-
-
-procedure TX2ServiceContextGUIForm.btnCloseClick(Sender: TObject);
-begin
- Close;
-end;
-
-
-procedure TX2ServiceContextGUIForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
-begin
- if not FAllowClose then
- begin
- SetStatus('Stopping...', StatusColorStopping);
- CanClose := False;
-
- ServiceThread.Terminate;
- end;
-end;
-
-
-procedure TX2ServiceContextGUIForm.UpdatePredefinedControlCodes;
-var
- serviceCustomControl: IX2ServiceCustomControl;
-
-begin
- cmbControlCodePredefined.Items.Clear;
-
- if Supports(Service, IX2ServiceCustomControl, serviceCustomControl) then
- begin
- serviceCustomControl.EnumCustomControlCodes(
- procedure(ACode: Byte; const ADescription: string)
- begin
- cmbControlCodePredefined.Items.AddObject(Format('%s (%d)', [ADescription, ACode]), TObject(ACode));
- end);
-
- cmbControlCodePredefined.Enabled := True;
- cmbControlCodePredefined.ItemIndex := 0;
- btnSendPredefined.Enabled := cmbControlCodePredefined.Items.Count > 0;
- end else
- begin
- cmbControlCodePredefined.Enabled := False;
- btnSendPredefined.Enabled := False;
- end;
-end;
-
-
-function TX2ServiceContextGUIForm.GetControlCode: Byte;
-begin
- Result := Byte(Min(Max(StrToIntDef(edtControlCode.Text, 0), 128), 255));
-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.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.
+unit X2UtService.GUIContext.Form;
+
+interface
+uses
+ System.Classes,
+ Vcl.Controls,
+ Vcl.ExtCtrls,
+ Vcl.Forms,
+ Vcl.Graphics,
+ Vcl.StdCtrls,
+ Winapi.Messages,
+
+ X2UtService.Intf;
+
+
+type
+ TX2ServiceContextGUIForm = class(TForm)
+ btnClose: TButton;
+ gbStatus: TGroupBox;
+ lblStatus: TLabel;
+ shpStatus: TShape;
+ gbCustomControl: TGroupBox;
+ lblControlCode: TLabel;
+ edtControlCode: TEdit;
+ btnSend: TButton;
+ cmbControlCodePredefined: TComboBox;
+ btnSendPredefined: TButton;
+
+ procedure FormCreate(Sender: TObject);
+ procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
+ procedure edtControlCodeChange(Sender: TObject);
+ procedure btnSendClick(Sender: TObject);
+ procedure btnSendPredefinedClick(Sender: TObject);
+ procedure btnCloseClick(Sender: TObject);
+ private
+ FContext: IX2ServiceContext;
+ FService: IX2Service;
+ FServiceThread: TThread;
+ FAllowClose: Boolean;
+ protected
+ procedure DoShow; override;
+
+ procedure UpdatePredefinedControlCodes; virtual;
+
+ function GetControlCode: Byte;
+ procedure SetStatus(const AMessage: string; AColor: TColor);
+
+ property ServiceThread: TThread read FServiceThread;
+ public
+ property Context: IX2ServiceContext read FContext write FContext;
+ property Service: IX2Service read FService write FService;
+ end;
+
+
+implementation
+uses
+ System.Generics.Collections,
+ System.Math,
+ System.SyncObjs,
+ System.SysUtils,
+ Winapi.Windows;
+
+
+{$R *.dfm}
+
+
+const
+ StatusColorStarting = $00B0FFB0;
+ StatusColorStarted = clGreen;
+ StatusColorStopping = $008080FF;
+ StatusColorStopped = clRed;
+
+
+type
+ TX2ServiceThread = class(TThread)
+ private
+ FContext: IX2ServiceContext;
+ FService: IX2Service;
+ FWakeEvent: TEvent;
+ FSendCodeList: TList;
+
+ 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 }
+procedure TX2ServiceContextGUIForm.FormCreate(Sender: TObject);
+begin
+ btnClose.Left := (ClientWidth - btnClose.Width) div 2;
+end;
+
+
+procedure TX2ServiceContextGUIForm.DoShow;
+var
+ serviceThread: TX2ServiceThread;
+begin
+ inherited DoShow;
+
+ if not Assigned(FServiceThread) then
+ begin
+ UpdatePredefinedControlCodes;
+
+ SetStatus('Starting...', StatusColorStarting);
+ serviceThread := TX2ServiceThread.Create(Context, Service);
+ serviceThread.OnStarted :=
+ procedure
+ begin
+ SetStatus('Started', StatusColorStarted);
+ 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;
+
+
+
+procedure TX2ServiceContextGUIForm.edtControlCodeChange(Sender: TObject);
+begin
+ edtControlCode.Text := IntToStr(GetControlCode);
+end;
+
+
+procedure TX2ServiceContextGUIForm.btnSendClick(Sender: TObject);
+begin
+ (ServiceThread as TX2ServiceThread).SendControlCode(GetControlCode);
+end;
+
+
+procedure TX2ServiceContextGUIForm.btnSendPredefinedClick(Sender: TObject);
+var
+ code: Byte;
+
+begin
+ if cmbControlCodePredefined.ItemIndex > -1 then
+ begin
+ code := Byte(cmbControlCodePredefined.Items.Objects[cmbControlCodePredefined.ItemIndex]);
+ (ServiceThread as TX2ServiceThread).SendControlCode(code);
+ end;
+end;
+
+
+procedure TX2ServiceContextGUIForm.btnCloseClick(Sender: TObject);
+begin
+ Close;
+end;
+
+
+procedure TX2ServiceContextGUIForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
+begin
+ if not FAllowClose then
+ begin
+ SetStatus('Stopping...', StatusColorStopping);
+ CanClose := False;
+
+ ServiceThread.Terminate;
+ end;
+end;
+
+
+procedure TX2ServiceContextGUIForm.UpdatePredefinedControlCodes;
+var
+ serviceCustomControl: IX2ServiceCustomControl;
+
+begin
+ cmbControlCodePredefined.Items.Clear;
+
+ if Supports(Service, IX2ServiceCustomControl, serviceCustomControl) then
+ begin
+ serviceCustomControl.EnumCustomControlCodes(
+ procedure(ACode: Byte; const ADescription: string)
+ begin
+ cmbControlCodePredefined.Items.AddObject(Format('%s (%d)', [ADescription, ACode]), TObject(ACode));
+ end);
+
+ cmbControlCodePredefined.Enabled := True;
+ cmbControlCodePredefined.ItemIndex := 0;
+ btnSendPredefined.Enabled := cmbControlCodePredefined.Items.Count > 0;
+ end else
+ begin
+ cmbControlCodePredefined.Enabled := False;
+ btnSendPredefined.Enabled := False;
+ end;
+end;
+
+
+function TX2ServiceContextGUIForm.GetControlCode: Byte;
+begin
+ Result := Byte(Min(Max(StrToIntDef(edtControlCode.Text, 0), 128), 255));
+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.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.
diff --git a/X2UtService.GUIContext.pas b/X2UtService.GUIContext.pas
index 06b22f0..2ed5cd2 100644
--- a/X2UtService.GUIContext.pas
+++ b/X2UtService.GUIContext.pas
@@ -1,70 +1,70 @@
-unit X2UtService.GUIContext;
-
-interface
-uses
- System.Classes,
-
- X2UtService.Intf;
-
-
-type
- TX2ServiceContextGUI = class(TInterfacedObject, IX2ServiceContext, IX2InteractiveServiceContext)
- protected
- procedure StartService(AService: IX2Service); virtual;
- public
- constructor Create(AService: IX2Service);
-
- { IX2ServiceContext }
- function GetMode: TX2ServiceMode;
-
-
- { IX2InteractiveServiceContext }
- procedure RunInteractive(AProc: TThreadProcedure);
- end;
-
-
-implementation
-uses
- Vcl.Forms,
-
- X2UtService.GUIContext.Form;
-
-
-{ TX2ServiceContextGUI }
-constructor TX2ServiceContextGUI.Create(AService: IX2Service);
-begin
- inherited Create;
-
- StartService(AService);
-end;
-
-
-function TX2ServiceContextGUI.GetMode: TX2ServiceMode;
-begin
- Result := smInteractive;
-end;
-
-
-procedure TX2ServiceContextGUI.StartService(AService: IX2Service);
-var
- serviceForm: TX2ServiceContextGUIForm;
-
-begin
- Application.Initialize;
- Application.MainFormOnTaskBar := True;
-
- Application.CreateForm(TX2ServiceContextGUIForm, serviceForm);
- serviceForm.Caption := AService.DisplayName;
- serviceForm.Context := Self;
- serviceForm.Service := AService;
-
- Application.Run;
-end;
-
-
-procedure TX2ServiceContextGUI.RunInteractive(AProc: TThreadProcedure);
-begin
- TThread.Queue(nil, AProc);
-end;
-
-end.
+unit X2UtService.GUIContext;
+
+interface
+uses
+ System.Classes,
+
+ X2UtService.Intf;
+
+
+type
+ TX2ServiceContextGUI = class(TInterfacedObject, IX2ServiceContext, IX2InteractiveServiceContext)
+ protected
+ procedure StartService(AService: IX2Service); virtual;
+ public
+ constructor Create(AService: IX2Service);
+
+ { IX2ServiceContext }
+ function GetMode: TX2ServiceMode;
+
+
+ { IX2InteractiveServiceContext }
+ procedure RunInteractive(AProc: TThreadProcedure);
+ end;
+
+
+implementation
+uses
+ Vcl.Forms,
+
+ X2UtService.GUIContext.Form;
+
+
+{ TX2ServiceContextGUI }
+constructor TX2ServiceContextGUI.Create(AService: IX2Service);
+begin
+ inherited Create;
+
+ StartService(AService);
+end;
+
+
+function TX2ServiceContextGUI.GetMode: TX2ServiceMode;
+begin
+ Result := smInteractive;
+end;
+
+
+procedure TX2ServiceContextGUI.StartService(AService: IX2Service);
+var
+ serviceForm: TX2ServiceContextGUIForm;
+
+begin
+ Application.Initialize;
+ Application.MainFormOnTaskBar := True;
+
+ Application.CreateForm(TX2ServiceContextGUIForm, serviceForm);
+ serviceForm.Caption := AService.DisplayName;
+ serviceForm.Context := Self;
+ serviceForm.Service := AService;
+
+ Application.Run;
+end;
+
+
+procedure TX2ServiceContextGUI.RunInteractive(AProc: TThreadProcedure);
+begin
+ TThread.Queue(nil, AProc);
+end;
+
+end.
diff --git a/X2UtService.Intf.pas b/X2UtService.Intf.pas
index a3781e4..5446dc8 100644
--- a/X2UtService.Intf.pas
+++ b/X2UtService.Intf.pas
@@ -1,102 +1,102 @@
-unit X2UtService.Intf;
-
-interface
-uses
- System.Classes,
- System.SysUtils;
-
-
-type
- TX2ServiceMode = (smService, smInteractive);
-
-
- IX2ServiceContext = interface
- ['{0AC283A7-B46C-4E4E-8F36-F8AA1272E04B}']
- function GetMode: TX2ServiceMode;
-
- property Mode: TX2ServiceMode read GetMode;
- end;
-
-
- IX2InteractiveServiceContext = interface(IX2ServiceContext)
- ['{82E69997-013D-4349-8060-B9F31B72CDF4}']
- procedure RunInteractive(AProc: TThreadProcedure);
- end;
-
-
- IX2Service = interface
- ['{C8597906-87B8-444E-847B-37A034F72FFC}']
- function GetServiceName: string;
- function GetDisplayName: string;
-
-
- { Called when the service starts. Return True if succesful.
- Storing a reference to AContext is allowed, but must be released when Stop is called. }
- function Start(AContext: IX2ServiceContext): Boolean;
-
- { Called when the service is about to stop.
- Return True if succesful. }
- function Stop: Boolean;
-
- { Called for control codes in the user-defined range of 128 to 255. }
- function DoCustomControl(ACode: Byte): Boolean;
-
-
- property ServiceName: string read GetServiceName;
- property DisplayName: string read GetDisplayName;
- end;
-
-
-
- TX2ServiceCustomControlProc = reference to procedure(ACode: Byte; const ADescription: string);
-
- { Implement this to enable discovery of supported custom control codes
- for use in interactive contexts. }
- IX2ServiceCustomControl = interface
- ['{D6363AC5-3DD5-4897-90A7-6F63D82B6A74}']
- procedure EnumCustomControlCodes(Yield: TX2ServiceCustomControlProc);
- end;
-
-
- TX2CustomService = class(TInterfacedObject, IX2Service)
- private
- FContext: IX2ServiceContext;
- protected
- property Context: IX2ServiceContext read FContext;
- public
- { IX2Service }
- function GetServiceName: string; virtual; abstract;
- function GetDisplayName: string; virtual; abstract;
-
- function Start(AContext: IX2ServiceContext): Boolean; virtual;
- function Stop: Boolean; virtual;
-
- function DoCustomControl(ACode: Byte): Boolean; virtual;
- end;
-
-
-
-implementation
-
-
-{ TX2CustomService }
-function TX2CustomService.Start(AContext: IX2ServiceContext): Boolean;
-begin
- FContext := AContext;
- Result := True;
-end;
-
-
-function TX2CustomService.Stop: Boolean;
-begin
- FContext := nil;
- Result := True;
-end;
-
-
-function TX2CustomService.DoCustomControl(ACode: Byte): Boolean;
-begin
- Result := True;
-end;
-
-end.
+unit X2UtService.Intf;
+
+interface
+uses
+ System.Classes,
+ System.SysUtils;
+
+
+type
+ TX2ServiceMode = (smService, smInteractive);
+
+
+ IX2ServiceContext = interface
+ ['{0AC283A7-B46C-4E4E-8F36-F8AA1272E04B}']
+ function GetMode: TX2ServiceMode;
+
+ property Mode: TX2ServiceMode read GetMode;
+ end;
+
+
+ IX2InteractiveServiceContext = interface(IX2ServiceContext)
+ ['{82E69997-013D-4349-8060-B9F31B72CDF4}']
+ procedure RunInteractive(AProc: TThreadProcedure);
+ end;
+
+
+ IX2Service = interface
+ ['{C8597906-87B8-444E-847B-37A034F72FFC}']
+ function GetServiceName: string;
+ function GetDisplayName: string;
+
+
+ { Called when the service starts. Return True if succesful.
+ Storing a reference to AContext is allowed, but must be released when Stop is called. }
+ function Start(AContext: IX2ServiceContext): Boolean;
+
+ { Called when the service is about to stop.
+ Return True if succesful. }
+ function Stop: Boolean;
+
+ { Called for control codes in the user-defined range of 128 to 255. }
+ function DoCustomControl(ACode: Byte): Boolean;
+
+
+ property ServiceName: string read GetServiceName;
+ property DisplayName: string read GetDisplayName;
+ end;
+
+
+
+ TX2ServiceCustomControlProc = reference to procedure(ACode: Byte; const ADescription: string);
+
+ { Implement this to enable discovery of supported custom control codes
+ for use in interactive contexts. }
+ IX2ServiceCustomControl = interface
+ ['{D6363AC5-3DD5-4897-90A7-6F63D82B6A74}']
+ procedure EnumCustomControlCodes(Yield: TX2ServiceCustomControlProc);
+ end;
+
+
+ TX2CustomService = class(TInterfacedObject, IX2Service)
+ private
+ FContext: IX2ServiceContext;
+ protected
+ property Context: IX2ServiceContext read FContext;
+ public
+ { IX2Service }
+ function GetServiceName: string; virtual; abstract;
+ function GetDisplayName: string; virtual; abstract;
+
+ function Start(AContext: IX2ServiceContext): Boolean; virtual;
+ function Stop: Boolean; virtual;
+
+ function DoCustomControl(ACode: Byte): Boolean; virtual;
+ end;
+
+
+
+implementation
+
+
+{ TX2CustomService }
+function TX2CustomService.Start(AContext: IX2ServiceContext): Boolean;
+begin
+ FContext := AContext;
+ Result := True;
+end;
+
+
+function TX2CustomService.Stop: Boolean;
+begin
+ FContext := nil;
+ Result := True;
+end;
+
+
+function TX2CustomService.DoCustomControl(ACode: Byte): Boolean;
+begin
+ Result := True;
+end;
+
+end.
diff --git a/X2UtService.ServiceContext.pas b/X2UtService.ServiceContext.pas
index a1a8ce9..a11ce5d 100644
--- a/X2UtService.ServiceContext.pas
+++ b/X2UtService.ServiceContext.pas
@@ -1,146 +1,146 @@
-unit X2UtService.ServiceContext;
-
-interface
-uses
- X2UtService.Intf;
-
-
-type
- TX2ServiceContextService = class(TInterfacedObject, IX2ServiceContext)
- protected
- procedure StartService(AService: IX2Service); virtual;
- public
- class function IsInstallUninstall: Boolean;
-
- constructor Create(AService: IX2Service);
-
- { IX2ServiceContext }
- function GetMode: TX2ServiceMode;
- end;
-
-
-implementation
-uses
- System.Classes,
- System.SysUtils,
- Vcl.SvcMgr,
-
- X2UtElevation;
-
-
-type
- TX2ServiceModule = class(TService)
- private
- FContext: IX2ServiceContext;
- FService: IX2Service;
- protected
- function GetServiceController: TServiceController; override;
-
- procedure HandleStart(Sender: TService; var Started: Boolean); virtual;
- procedure HandleStop(Sender: TService; var Stopped: Boolean); virtual;
-
- function DoCustomControl(CtrlCode: Cardinal): Boolean; override;
-
- property Context: IX2ServiceContext read FContext;
- property Service: IX2Service read FService;
- public
- constructor Create(AOwner: TComponent; AContext: IX2ServiceContext; AService: IX2Service); reintroduce;
- end;
-
-
-var
- ServiceModuleInstance: TX2ServiceModule;
-
-
-procedure ServiceController(CtrlCode: Cardinal); stdcall;
-begin
- if Assigned(ServiceModuleInstance) then
- ServiceModuleInstance.Controller(CtrlCode);
-end;
-
-
-
-{ TX2ServiceContextService }
-class function TX2ServiceContextService.IsInstallUninstall: Boolean;
-begin
- Result := FindCmdLineSwitch('install', ['-', '/'], True) or
- FindCmdLineSwitch('uninstall', ['-', '/'], True);
-end;
-
-
-constructor TX2ServiceContextService.Create(AService: IX2Service);
-begin
- inherited Create;
-
- if IsInstallUninstall and (not IsElevated) then
- raise Exception.Create('Elevation is required for install or uninstall');
-
- StartService(AService);
-end;
-
-
-function TX2ServiceContextService.GetMode: TX2ServiceMode;
-begin
- Result := smService;
-end;
-
-
-procedure TX2ServiceContextService.StartService(AService: IX2Service);
-begin
- if Assigned(ServiceModuleInstance) then
- raise EInvalidOperation.Create('An instance of TX2ServiceContextService is already running');
-
- Application.Initialize;
- ServiceModuleInstance := TX2ServiceModule.Create(Application, Self, AService);
- try
- ServiceModuleInstance.DisplayName := AService.DisplayName;
- ServiceModuleInstance.Name := AService.ServiceName;
-
- Application.Run;
- finally
- ServiceModuleInstance := nil;
- end;
-end;
-
-
-{ TX2ServiceModule }
-constructor TX2ServiceModule.Create(AOwner: TComponent; AContext: IX2ServiceContext; AService: IX2Service);
-begin
- // Skip default constructor to prevent DFM streaming
- CreateNew(AOwner);
-
- FContext := AContext;
- FService := AService;
-
- OnStart := HandleStart;
- OnStop := HandleStop;
-end;
-
-
-function TX2ServiceModule.GetServiceController: TServiceController;
-begin
- Result := ServiceController;
-end;
-
-
-function TX2ServiceModule.DoCustomControl(CtrlCode: Cardinal): Boolean;
-begin
- Result := True;
-
- if (CtrlCode >= 128) and (CtrlCode <= 255) then
- Result := Service.DoCustomControl(Byte(CtrlCode));
-end;
-
-
-procedure TX2ServiceModule.HandleStart(Sender: TService; var Started: Boolean);
-begin
- Started := Service.Start(Context);
-end;
-
-
-procedure TX2ServiceModule.HandleStop(Sender: TService; var Stopped: Boolean);
-begin
- Stopped := Service.Stop;
-end;
-
-end.
+unit X2UtService.ServiceContext;
+
+interface
+uses
+ X2UtService.Intf;
+
+
+type
+ TX2ServiceContextService = class(TInterfacedObject, IX2ServiceContext)
+ protected
+ procedure StartService(AService: IX2Service); virtual;
+ public
+ class function IsInstallUninstall: Boolean;
+
+ constructor Create(AService: IX2Service);
+
+ { IX2ServiceContext }
+ function GetMode: TX2ServiceMode;
+ end;
+
+
+implementation
+uses
+ System.Classes,
+ System.SysUtils,
+ Vcl.SvcMgr,
+
+ X2UtElevation;
+
+
+type
+ TX2ServiceModule = class(TService)
+ private
+ FContext: IX2ServiceContext;
+ FService: IX2Service;
+ protected
+ function GetServiceController: TServiceController; override;
+
+ procedure HandleStart(Sender: TService; var Started: Boolean); virtual;
+ procedure HandleStop(Sender: TService; var Stopped: Boolean); virtual;
+
+ function DoCustomControl(CtrlCode: Cardinal): Boolean; override;
+
+ property Context: IX2ServiceContext read FContext;
+ property Service: IX2Service read FService;
+ public
+ constructor Create(AOwner: TComponent; AContext: IX2ServiceContext; AService: IX2Service); reintroduce;
+ end;
+
+
+var
+ ServiceModuleInstance: TX2ServiceModule;
+
+
+procedure ServiceController(CtrlCode: Cardinal); stdcall;
+begin
+ if Assigned(ServiceModuleInstance) then
+ ServiceModuleInstance.Controller(CtrlCode);
+end;
+
+
+
+{ TX2ServiceContextService }
+class function TX2ServiceContextService.IsInstallUninstall: Boolean;
+begin
+ Result := FindCmdLineSwitch('install', ['-', '/'], True) or
+ FindCmdLineSwitch('uninstall', ['-', '/'], True);
+end;
+
+
+constructor TX2ServiceContextService.Create(AService: IX2Service);
+begin
+ inherited Create;
+
+ if IsInstallUninstall and (not IsElevated) then
+ raise Exception.Create('Elevation is required for install or uninstall');
+
+ StartService(AService);
+end;
+
+
+function TX2ServiceContextService.GetMode: TX2ServiceMode;
+begin
+ Result := smService;
+end;
+
+
+procedure TX2ServiceContextService.StartService(AService: IX2Service);
+begin
+ if Assigned(ServiceModuleInstance) then
+ raise EInvalidOperation.Create('An instance of TX2ServiceContextService is already running');
+
+ Application.Initialize;
+ ServiceModuleInstance := TX2ServiceModule.Create(Application, Self, AService);
+ try
+ ServiceModuleInstance.DisplayName := AService.DisplayName;
+ ServiceModuleInstance.Name := AService.ServiceName;
+
+ Application.Run;
+ finally
+ ServiceModuleInstance := nil;
+ end;
+end;
+
+
+{ TX2ServiceModule }
+constructor TX2ServiceModule.Create(AOwner: TComponent; AContext: IX2ServiceContext; AService: IX2Service);
+begin
+ // Skip default constructor to prevent DFM streaming
+ CreateNew(AOwner);
+
+ FContext := AContext;
+ FService := AService;
+
+ OnStart := HandleStart;
+ OnStop := HandleStop;
+end;
+
+
+function TX2ServiceModule.GetServiceController: TServiceController;
+begin
+ Result := ServiceController;
+end;
+
+
+function TX2ServiceModule.DoCustomControl(CtrlCode: Cardinal): Boolean;
+begin
+ Result := True;
+
+ if (CtrlCode >= 128) and (CtrlCode <= 255) then
+ Result := Service.DoCustomControl(Byte(CtrlCode));
+end;
+
+
+procedure TX2ServiceModule.HandleStart(Sender: TService; var Started: Boolean);
+begin
+ Started := Service.Start(Context);
+end;
+
+
+procedure TX2ServiceModule.HandleStop(Sender: TService; var Stopped: Boolean);
+begin
+ Stopped := Service.Stop;
+end;
+
+end.
diff --git a/X2UtService.pas b/X2UtService.pas
index 4942ed2..6c46535 100644
--- a/X2UtService.pas
+++ b/X2UtService.pas
@@ -1,62 +1,62 @@
-unit X2UtService;
-
-interface
-uses
- X2UtService.Intf;
-
-
-type
- TX2Service = class(TObject)
- public
- class function Run(AService: IX2Service): IX2ServiceContext;
- end;
-
-
- function IsUserInteractive: Boolean;
-
-
-implementation
-uses
- System.SysUtils,
- Winapi.Windows,
-
- X2UtService.GUIContext,
- X2UtService.ServiceContext;
-
-
-
-function IsUserInteractive: Boolean;
-var
- windowStation: HWINSTA;
- userObject: TUserObjectFlags;
- lengthNeeded: Cardinal;
-
-begin
- Result := True;
-
- windowStation := GetProcessWindowStation;
- if windowStation <> 0 then
- begin
- lengthNeeded := 0;
- FillChar(userObject, SizeOf(userObject), 0);
-
- if GetUserObjectInformation(windowStation, UOI_FLAGS, @userObject, SizeOf(userObject), lengthNeeded) and
- ((userObject.dwFlags and WSF_VISIBLE) = 0) then
- begin
- Result := False;
- end;
- end;
-end;
-
-
-
-{ TX2Service }
-class function TX2Service.Run(AService: IX2Service): IX2ServiceContext;
-begin
- if TX2ServiceContextService.IsInstallUninstall or (not IsUserInteractive) then
- Result := TX2ServiceContextService.Create(AService)
- else
- Result := TX2ServiceContextGUI.Create(AService);
-end;
-
-end.
+unit X2UtService;
+
+interface
+uses
+ X2UtService.Intf;
+
+
+type
+ TX2Service = class(TObject)
+ public
+ class function Run(AService: IX2Service): IX2ServiceContext;
+ end;
+
+
+ function IsUserInteractive: Boolean;
+
+
+implementation
+uses
+ System.SysUtils,
+ Winapi.Windows,
+
+ X2UtService.GUIContext,
+ X2UtService.ServiceContext;
+
+
+
+function IsUserInteractive: Boolean;
+var
+ windowStation: HWINSTA;
+ userObject: TUserObjectFlags;
+ lengthNeeded: Cardinal;
+
+begin
+ Result := True;
+
+ windowStation := GetProcessWindowStation;
+ if windowStation <> 0 then
+ begin
+ lengthNeeded := 0;
+ FillChar(userObject, SizeOf(userObject), 0);
+
+ if GetUserObjectInformation(windowStation, UOI_FLAGS, @userObject, SizeOf(userObject), lengthNeeded) and
+ ((userObject.dwFlags and WSF_VISIBLE) = 0) then
+ begin
+ Result := False;
+ end;
+ end;
+end;
+
+
+
+{ TX2Service }
+class function TX2Service.Run(AService: IX2Service): IX2ServiceContext;
+begin
+ if TX2ServiceContextService.IsInstallUninstall or (not IsUserInteractive) then
+ Result := TX2ServiceContextService.Create(AService)
+ else
+ Result := TX2ServiceContextGUI.Create(AService);
+end;
+
+end.