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 + + + + X2Utils.dpk + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + False + True + True + + + 12 + + + + + MainSource + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
X2ServiceContextGUIForm
+
+ + + + + + 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.