Newsgroup: it.comp.lang.delphi
Titolo: invio di controlli user-defined ad un servizio

Ciao a tutti.
Volevo utilizzare l'API ControlService per inviare un controllo ad un
servizio.
Dal Windows SDK Help:
-------------------------------------------------------
The ControlService function sends a control code to a Win32 service.
BOOL ControlService(
    SC_HANDLE hService,	// handle to service
    DWORD dwControl,	// control code
    LPSERVICE_STATUS lpServiceStatus 	// pointer to service status
structure
   );
hService
Identifies the service. This handle is returned by the OpenService or
CreateService function. The access required for this handle depends on
the dwControl code requested.
dwControl
Specifies the requested control code. This value can be one of the
standard control codes in the table, or it can be a user-defined
control code in the range of 128 to 255, inclusive. For user-defined
control codes, the hService handle must have
SERVICE_USER_DEFINED_CONTROL access, and the service defines the action
associated with the control code
[.....]
------------------------------------------------------------
Leggendo questo, io ho capito (correggetemi se ho capito male :D ) che
ai codici di controllo 128-255 posso associare comportamenti del
servizio user-defined.
Ora, se e' cosi', come posso nel mio servizio definire (come da ultima
riga dello stralcio riportato) la/le azioni associate al/ai miei codici
di controllo?
Per inciso, vi chiedo questo perchè ho sviluppato un mini-servizio,
molto scarno, i cui parametri configurazione sono in un'apposita chiave
di registro, e la configurazione e' possibile mediante un'altra
applicazione, avviata in automatico, che consente inoltre di
visualizzare lo stato del servizio. Soluzione classica, insomma, e
anche funzionante.
Ora volevo provare -magari me lo sconsiglierete- ad utilizzare appunto
l'API ControlService per consentire all'applicazione di configurazione
di comunicare al servizio che i parametri di funzionamento sono
cambiati.
Sto contemporaneamente provando anche ad imparare ad usare le named
pipes, ma.. quanto sono ostiche!
Cmq grazie a chi puo' aiutarmi
Ciao

 di controllo?
Devi fare l'override del metodo virtuale DoCustomControl del tuo
oggetto TService. Esempio:
// Definisci una costante per il messaggio da gestire
const
 cmdPersonalizzato = 140;
Poi:
  TService1 = class(TService)
    procedure ServiceCreate(Sender: TObject);
  private
   // Definisci la procedura da eseguire quando invierai il comando al
   // servizio
   procedure MiaProcedura;
 protected
   // Fai l'override del metodo virtuale
   function DoCustomControl(CtrlCode: Cardinal): Boolean; override;
  public
    function GetServiceController: TServiceController; override;
    { Public declarations }
  end;
Nel codice, gestisci l'evento personalizzato:
implementation
function TService1.DoCustomControl(CtrlCode: Cardinal): Boolean;
begin
 if  CtrlCode = cmdPersonalizzato Then MiaProcedura;
end;
Infine, nell'applicazione manderai il codice di controllo utilizzando l'API
in questione:
if ControlService(HServizio, CODICE_DI_CONTROLLO_PERSONALIZZATO,
StatoDelServizio) then ecc...
Tutto NON testato.
Ciao
-- 
ACSH
[TurboDelphi]
Chi fa da se', fatica il doppio

 StatoDelServizio) then ecc...
Ho scoperto pero' (consultando la guida della funzione
TService.GetServiceController) che
c'e' un modo molto semplice perchè il servizio gestisca in modo
customizzato i codici di controllo user-defined: modificare la funzione
ServiceController preparata dal wizard di creazione del servizio.
Riporto il codice generato dal wizard, anche se noto a tutti, per
facilita' di riferimento
--------------
type
  TService1 = class(TService)
  private
    { Private declarations }
  public
    function GetServiceController: TServiceController; override;
    { Public declarations }
  end;
var
  Service1: TService1;
implementation
{$R *.DFM}
procedure ServiceController(CtrlCode: DWord); stdcall;
begin
  Service1.Controller(CtrlCode);
end;
function TService1.GetServiceController: TServiceController;
begin
  Result := ServiceController;
end;
--------------
La guida di TService.GetServiceController accenna alla API
RegisterServiceCtrlHandler, illustrando cosi' la necessita' di una
procedura esterna alla classe.
Questa procedura e' appunto ServiceController, che chiama il metodo
Controller dell'istanza (nell'esempio Service1) inclusa nella clausola
var della unit stessa.
Aggiungendo codice a ServiceController, possiamo gestire codici di
controllo user-defined:
--------------
procedure ServiceController(CtrlCode: DWord); stdcall;
begin
  Service1.Controller(CtrlCode);
  IF CTRLCODE = CODICE_DI_CONTROLLO_PERSONALIZZATO THEN
    [...]
end;
--------------
Qui sorge pero' -almeno per me :) - un nuovo problema.
In questo modo infatti l'implementazione del servizio non può
assolutamente prescindere dall'istanza Service1.
Come posso allora ereditare dalla classe che creo?
Se cioe' volessi creare una classe discendente da TService, e a sua
volta classe base per tutti i miei
"servizi-capaci-di-rispondere-a-controlli-user-defined", come fare?
Ciao e grazie!

> StatoDelServizio) then ecc...
Prima di usare ControlService, come hai aperto il servizio? Hai usato
SERVICE_USER_DEFINED_CONTROL, cioè:
hServizio := OpenService(HandleRecuperatoDaOpenSCManager,
NomeDelServizio, SERVICE_USER_DEFINED_CONTROL)?
A dire la verità, il resto del tuo post non l'ho compreso bene:
cosa non va nell'ereditare dalla classe "generalista"?


 cosa non va nell'ereditare dalla classe "generalista"?
Dunque, io ho avuto questa difficolta'. Spero di spiegarmi bene ;-)
Se tu leggi la guida online di TService.GetController, ti spiega come
la procedura ServiceController introdotta dal wizard sia quella
registrata dall'API RegisterServiceCtrlHandler all'avvio del servizio
come gestore dei messaggi di controllo del servizio.
Magari la cosa ti incuriosisce, puoi vederlo qui:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/service_functions.asp
Dicevo, la guida online spiega come l'handler debba necessariamente
essere un metodo non della classe. Ecco perche', nel proprio body,
ServiceController non può fare riferimento all'oggetto servizio come
"self" ma come -passami l'espressione- "oggetto esterno":
[...]
var MioServizio: TMioServizio;
[...]
procedure ServiceController(CtrlCode: DWord); stdcall;
begin
  MioServizio.Controller(CtrlCode);
  if CtrlCode in CUSTOM_CODES then MiaProcedura(CtrlCode);
end;
[...]
Dunque la riga:
var MioServizio: TMioServizio;
diviene imprescindibile, perche' RegisterServiceCtrlHandler non puo'
effettuare la registrazione dell'handler senza che sia stata istanziato
un oggetto della classe.
E questo mi ha creato un po' di magagne quando ho provato ad ereditare
dalla mia classe di servizio.
Questo e' lo scenario.
Ho creato una classe,diciamo TServizioBase,che eredita da
TService,pensando di farne -come dicevo nel post precedente- la classe
radice di una gerarchia di servizi di un certo tipo - per inciso, i
servizi "controllabili da utente". La classe è definita in una sua
unit, diciamo u_base,
ed e' una sorta di interfaccia, uno scheletro, in cui pero' ho già
fatto l'opportuno override del metodo DoCustomControl e ho aggiunto
alcuni membri oggetto opportuni, che verranno inizializzati in modo
apposito dalle classi discendenti, per es. esempio un set customcodes
di codici, riferito dalla DoCustomControl (..if ctrlcode in
customcodes..).
Quando voglio ereditare da TServizioBase, faccio qualcosa del tipo:
[...]
uses [...] u_base;
type
  TNuovoServizio = class(TServizioBase)
  [...]
  end;
var NuovoServizio: TNuovoServizio;
[...]
e qui viene il bello, perche' non potendo evitare di istanziare la
classe base, nel mio progetto finale avro' inevitabilmente un'istanza
TServizioBase ed una TNuovoServizio.
Tra l'altro ho provato ad implementare tutto questo, il progetto
compila correttamente, ma quando successivamente riavvio l'IDE di
Delphi e riapro il progetto mi da un messaggio d'errore davvero
bizzarro, dicendo che nella classe discendente non e' definita la
proprieta' DisplayName.
A prescindere dalla mia finalita', credo che questo mi darebbe problemi
ogni volta che volessi creare -come dicevo appunto nel vecchio post-
ogni volta che volessi creare un classe erede di TService ma ancestor
di altre classi.
Scusa la lungaggine, spero di essere stato un po' piu' chiaro, grazie
se vorrai rispondermi.
Ciao