mORMot and Open Source friends
Check-in [215e44e66b]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Service handler uses a global variable for service handler (works in all cases) - see http://synopse.info/forum/viewtopic.php?pid=3683#p3683
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 215e44e66b1226ae772594cd9b0b917031da60bb
User & Date: G018869 2012-04-10 13:17:23
Context
2012-04-10
16:25
  • start conversion to FPC 2.7.1 (using CodeTyphon release) - not yet finished
  • fixed some code issues by the way, due to more complete warning information from FPC
check-in: c8fe64da02 user: G018869 tags: trunk
13:17
Service handler uses a global variable for service handler (works in all cases) - see http://synopse.info/forum/viewtopic.php?pid=3683#p3683 check-in: 215e44e66b user: G018869 tags: trunk
09:26
new ForceID parameter for TSQLRest.Add() to allow adding a given ID check-in: 3f9985da02 user: G018869 tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to SQLite3/SQLite3Service.pas.

143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
...
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
...
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
...
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
...
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
...
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
...
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
      the startup program logs the error and displays a message but continues
      the startup operation), SERVICE_ERROR_SEVERE,
      SERVICE_ERROR_CRITICAL }
    constructor CreateNewService(const TargetComputer, DatabaseName,
      Name, DisplayName, Path: string;
      const OrderGroup: string = ''; const Dependances: string = '';
      const Username: string = ''; const Password: string = '';
      DesiredAccess: DWord = SERVICE_ALL_ACCESS;
      ServiceType: DWord = SERVICE_WIN32_OWN_PROCESS or SERVICE_INTERACTIVE_PROCESS;
      StartType: DWord = SERVICE_DEMAND_START; ErrorControl: DWord = SERVICE_ERROR_NORMAL);
    {{ Opens an existing service, in order  to control it or its configuration
      from your application. Parameters (strings are unicode-ready since Delphi 2009):
   - TargetComputer - set it to empty string if local computer is the target.
   - DatabaseName - set it to empty string if the default database is supposed
                ('ServicesActive').
   - Name - name of a service.
   - DesiredAccess - a combination of following flags:
................................................................................
      Delphi compiler), either UnicodeString (till Delphi 2009) }
    function Start(const Args: array of PChar): boolean;
  end;

  TService = class;

  /// callback procedure for Windows Service Controller
  TServiceCtrlHandler = procedure(CtrlCode: DWord); stdcall;
  /// event triggered for Control handler
  TServiceControlEvent = procedure(Sender: TService; Code: DWORD) of object;
  /// event triggered to implement the Service functionality
  TServiceEvent = procedure(Sender: TService) of object;

  {{ TService is the class used to implement a service provided by an application }
  TService = class
  protected
    fSName: String;
    fDName: String;
    fStartType: dword;
    fServiceType: dword;
    fData: DWORD;
    fCtrlHandler: TServiceCtrlHandler;
    fOnControl: TServiceControlEvent;
    fOnInterrogate: TServiceEvent;
    fOnPause: TServiceEvent;
    fOnShutdown: TServiceEvent;
    fOnStart: TServiceEvent;
................................................................................
    // - main application must call the global ServicesRun procedure to actually
    // start the services
    // - caller must free the TService instance when it's no longer used
    constructor Create(const aServiceName, aDisplayName: String); reintroduce; virtual;
    /// free memory and release handles
    destructor Destroy; override;
    {{ Reports new status to the system }
    function ReportStatus(dwState, dwExitCode, dwWait:DWORD):BOOL;
    {{ Installs the service in the database
      - return true on success
      - create a local TServiceController with the current executable file,
        with the supplied command line parameters}
    function Install(const Params: string=''): boolean;
    {{ Removes the service from database
      - uses a local TServiceController with the current Service Name }
................................................................................
    {{ this is the main method, in which the Service should implement its run  }
    procedure Execute; virtual;
    {{ Name of the service. Must be unique }
    property ServiceName: String read fSName;
    {{ Display name of the service }
    property DisplayName: String read fDName write fDName;
    {{ Type of service }
    property ServiceType: dword read fServiceType write fServiceType;
    {{ Type of start of service }
    property StartType: dword read fStartType write fStartType;
    {{ Number of arguments passed to the service by the service controler }
    property ArgCount: Integer read GetArgCount;
    {{ List of arguments passed to the service by the service controler }
    property Args[Idx: Integer]: String read GetArgs;
    {{ Current service status
      - To report new status to the system, assign another
       value to this record, or use ReportStatus method (better) }
................................................................................
{{ launch the registered Services execution
  - the registered list of service provided by the aplication is sent
   to the operating system }
procedure ServicesRun;

{{ convert the Control Code retrieved from Windows into a service state
  enumeration item }
function CurrentStateToServiceState(CurrentState: DWord): TServiceState;

/// return the ready to be displayed text of a TServiceState value
function ServiceStateText(State: TServiceState): string;


implementation

................................................................................
      raise Exception.CreateFmt('Cannot allocate memory for service jump gate: %s',
        [fSName]);
    JumperAddr := @JumpToService;
    AfterCallAddr := Pointer(PtrInt(FJumper)+5);
    Offset :=  PtrInt(JumperAddr)-PtrInt(AfterCallAddr);
    PByte   (PtrInt(FJumper)+0)^ := $E8;          // call opcode
    PInteger(PtrInt(FJumper)+1)^ := Offset;       // points to JumpToService
    PPtrInt (PtrInt(FJumper)+5)^ := PtrInt(self); // will be set as EAX=sef
    Result := FJumper;
  end;
end;

function CurrentStateToServiceState(CurrentState: DWord): TServiceState;
begin
  case CurrentState of
    SERVICE_STOPPED:          result := ssStopped;
    SERVICE_START_PENDING:    result := ssStarting;
    SERVICE_STOP_PENDING:     result := ssStopping;
    SERVICE_RUNNING:          result := ssRunning;
    SERVICE_CONTINUE_PENDING: result := ssResuming;
................................................................................
     if (schService>0) then begin
       result := true;
       CloseServiceHandle(schService);
     end;
  end;
end;

{procedure TService.MessageExecute(EndCode: dword);
var aMsg: TMsg;
begin
  while fCurrentCode<>EndCode do begin
    while PeekMessage(aMsg,0,0,0,PM_REMOVE) do begin
      TranslateMessage(aMsg);
      DispatchMessage(aMsg);
    end;






|
|
|







 







|










|
|







 







|







 







|

|







 







|







 







|




|







 







|







143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
...
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
...
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
...
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
...
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
...
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
...
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
      the startup program logs the error and displays a message but continues
      the startup operation), SERVICE_ERROR_SEVERE,
      SERVICE_ERROR_CRITICAL }
    constructor CreateNewService(const TargetComputer, DatabaseName,
      Name, DisplayName, Path: string;
      const OrderGroup: string = ''; const Dependances: string = '';
      const Username: string = ''; const Password: string = '';
      DesiredAccess: DWORD = SERVICE_ALL_ACCESS;
      ServiceType: DWORD = SERVICE_WIN32_OWN_PROCESS or SERVICE_INTERACTIVE_PROCESS;
      StartType: DWORD = SERVICE_DEMAND_START; ErrorControl: DWORD = SERVICE_ERROR_NORMAL);
    {{ Opens an existing service, in order  to control it or its configuration
      from your application. Parameters (strings are unicode-ready since Delphi 2009):
   - TargetComputer - set it to empty string if local computer is the target.
   - DatabaseName - set it to empty string if the default database is supposed
                ('ServicesActive').
   - Name - name of a service.
   - DesiredAccess - a combination of following flags:
................................................................................
      Delphi compiler), either UnicodeString (till Delphi 2009) }
    function Start(const Args: array of PChar): boolean;
  end;

  TService = class;

  /// callback procedure for Windows Service Controller
  TServiceCtrlHandler = procedure(CtrlCode: DWORD); stdcall;
  /// event triggered for Control handler
  TServiceControlEvent = procedure(Sender: TService; Code: DWORD) of object;
  /// event triggered to implement the Service functionality
  TServiceEvent = procedure(Sender: TService) of object;

  {{ TService is the class used to implement a service provided by an application }
  TService = class
  protected
    fSName: String;
    fDName: String;
    fStartType: DWORD;
    fServiceType: DWORD;
    fData: DWORD;
    fCtrlHandler: TServiceCtrlHandler;
    fOnControl: TServiceControlEvent;
    fOnInterrogate: TServiceEvent;
    fOnPause: TServiceEvent;
    fOnShutdown: TServiceEvent;
    fOnStart: TServiceEvent;
................................................................................
    // - main application must call the global ServicesRun procedure to actually
    // start the services
    // - caller must free the TService instance when it's no longer used
    constructor Create(const aServiceName, aDisplayName: String); reintroduce; virtual;
    /// free memory and release handles
    destructor Destroy; override;
    {{ Reports new status to the system }
    function ReportStatus(dwState, dwExitCode, dwWait: DWORD): BOOL;
    {{ Installs the service in the database
      - return true on success
      - create a local TServiceController with the current executable file,
        with the supplied command line parameters}
    function Install(const Params: string=''): boolean;
    {{ Removes the service from database
      - uses a local TServiceController with the current Service Name }
................................................................................
    {{ this is the main method, in which the Service should implement its run  }
    procedure Execute; virtual;
    {{ Name of the service. Must be unique }
    property ServiceName: String read fSName;
    {{ Display name of the service }
    property DisplayName: String read fDName write fDName;
    {{ Type of service }
    property ServiceType: DWORD read fServiceType write fServiceType;
    {{ Type of start of service }
    property StartType: DWORD read fStartType write fStartType;
    {{ Number of arguments passed to the service by the service controler }
    property ArgCount: Integer read GetArgCount;
    {{ List of arguments passed to the service by the service controler }
    property Args[Idx: Integer]: String read GetArgs;
    {{ Current service status
      - To report new status to the system, assign another
       value to this record, or use ReportStatus method (better) }
................................................................................
{{ launch the registered Services execution
  - the registered list of service provided by the aplication is sent
   to the operating system }
procedure ServicesRun;

{{ convert the Control Code retrieved from Windows into a service state
  enumeration item }
function CurrentStateToServiceState(CurrentState: DWORD): TServiceState;

/// return the ready to be displayed text of a TServiceState value
function ServiceStateText(State: TServiceState): string;


implementation

................................................................................
      raise Exception.CreateFmt('Cannot allocate memory for service jump gate: %s',
        [fSName]);
    JumperAddr := @JumpToService;
    AfterCallAddr := Pointer(PtrInt(FJumper)+5);
    Offset :=  PtrInt(JumperAddr)-PtrInt(AfterCallAddr);
    PByte   (PtrInt(FJumper)+0)^ := $E8;          // call opcode
    PInteger(PtrInt(FJumper)+1)^ := Offset;       // points to JumpToService
    PPtrInt (PtrInt(FJumper)+5)^ := PtrInt(self); // will be set as EAX=self
    Result := FJumper;
  end;
end;

function CurrentStateToServiceState(CurrentState: DWORD): TServiceState;
begin
  case CurrentState of
    SERVICE_STOPPED:          result := ssStopped;
    SERVICE_START_PENDING:    result := ssStarting;
    SERVICE_STOP_PENDING:     result := ssStopping;
    SERVICE_RUNNING:          result := ssRunning;
    SERVICE_CONTINUE_PENDING: result := ssResuming;
................................................................................
     if (schService>0) then begin
       result := true;
       CloseServiceHandle(schService);
     end;
  end;
end;

{procedure TService.MessageExecute(EndCode: DWORD);
var aMsg: TMsg;
begin
  while fCurrentCode<>EndCode do begin
    while PeekMessage(aMsg,0,0,0,PM_REMOVE) do begin
      TranslateMessage(aMsg);
      DispatchMessage(aMsg);
    end;

Changes to SQLite3/Samples/10 - Background Http service/httpservice.dpr.

46
47
48
49
50
51
52











53
54
55
56
57
58

59
60
61
62
63
64
65
...
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
    destructor Destroy; override;
  end;


const
  SERVICENAME = 'mORMotHttpServerService';
  SERVICEDISPLAYNAME = 'mORMot Http Server Service';












{ TSQLite3HttpService }

constructor TSQLite3HttpService.Create;
begin
  inherited Create(SERVICENAME,SERVICEDISPLAYNAME);

  OnStart := DoStart;
  OnStop := DoStop;
  OnResume := DoStart; // trivial Pause/Resume actions
  OnPause := DoStop;
end;

destructor TSQLite3HttpService.Destroy;
................................................................................
          Start([]);
      end;
  finally
    Free;
  end;
end;

var Service: TSQLite3HttpService;
begin
  if ParamCount<>0 then
    CheckParameters else begin
    Service := TSQLite3HttpService.Create;
    try
      // launches the registered Services execution = do all the magic
      ServicesRun;
    finally
      Service.Free;
    end;
  end;
end.






>
>
>
>
>
>
>
>
>
>
>






>







 







<












46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
...
130
131
132
133
134
135
136

137
138
139
140
141
142
143
144
145
146
147
148
    destructor Destroy; override;
  end;


const
  SERVICENAME = 'mORMotHttpServerService';
  SERVICEDISPLAYNAME = 'mORMot Http Server Service';

var
  /// use a global variable for service handler (works in all cases)
  // - see http://synopse.info/forum/viewtopic.php?pid=3683#p3683
  Service: TSQLite3HttpService;

procedure ServiceControlHandler(CtrlCode: DWORD); stdcall;
begin
  Service.DoCtrlHandle(CtrlCode);
end;


{ TSQLite3HttpService }

constructor TSQLite3HttpService.Create;
begin
  inherited Create(SERVICENAME,SERVICEDISPLAYNAME);
  ControlHandler := ServiceControlHandler;
  OnStart := DoStart;
  OnStop := DoStop;
  OnResume := DoStart; // trivial Pause/Resume actions
  OnPause := DoStop;
end;

destructor TSQLite3HttpService.Destroy;
................................................................................
          Start([]);
      end;
  finally
    Free;
  end;
end;


begin
  if ParamCount<>0 then
    CheckParameters else begin
    Service := TSQLite3HttpService.Create;
    try
      // launches the registered Services execution = do all the magic
      ServicesRun;
    finally
      Service.Free;
    end;
  end;
end.