Delphi – Tornando um Aplicativo Formulário em “Windows Service”

Cenário

Tenho um aplicativo que roda como executável windows executando atividades que não requerem intervenção do usuário para o seu funcionamento. Se manter o aplicativo minimizado fica muito fácil de fecha-lo por engano, parando o serviço que se encontra em execução. Uma opções é deixar o aplicativo na bandeja (aqueles ICON no canto direito do windows) – a desvantagem neste caso é que depende que seja feito login para  “INICIAR” a execução do aplicativo.
Nestas situações criar um serviço que sobe junto com o windows pode ser uma alternativa interessante – ponto que irei focar neste POST.

Considerações sobre  um formulário preexistente

Considerações sobre as diferenças em executar um APP com formulário X Serviço que não possui um formulário:
1) Considere um formulário padrão de execução para um aplicativo que mostra o formulário; Quando se usa como aplicativo ele inicia a execução pelo evento onFormShow, mas quando inicia como serviço não é aconselhável gerar o evento FormShow, já que executando como serviço não há um terminal/janela para mostrar as mensagens.
2) Outra consideração importante é não permitir chamadas para funções que mostram mensagens para o usuário (ex: showmessage); Uma alternativa é criar um FAKE para a função ShowMessage (… e outras) direcionando a mensagem para uma saída em disco (um LOG);

[code lang=”pascal”]
implementation

{$R *.dfm}

procedure TForm10.Execute;
begin // procedure a ser executa quando estiver chamando como serviço
// nao executa o SHOW do formulário
FServico := true;
Init;
end;

procedure TForm10.FormCreate(Sender: TObject);
begin
FContador := 0;
FServico := false;
end;

procedure TForm10.FormShow(Sender: TObject);
begin
// quando é executa como Aplicativo, usa o show para dar inicio ao funcinamento
FServico := false;
init;
end;

procedure TForm10.Init;
begin
Timer1.Enabled := true;
end;

procedure TForm10.Log(texto: string);
begin
TThread.Queue(nil,
procedure // sincroniza a escrita no memo1 – previne chamada multi-thread
begin
Memo1.Lines.Add(texto); // mostra o texto em um memo.
end);
end;

procedure TForm10.Timer1Timer(Sender: TObject);
begin
inc(FContador);
Log(‘Chamada: ‘ + intToStr(FContador));
end;

[/code]

Criando um “Service Application” padrão 

Criar um projeto padrão do DELPHI do tipo “Service Application” que será a base para iniciar o nosso serviço;

Com base no projeto padrão, no evento “onStart” criar uma instância para o formulário padrão do aplicativo. Importante neste ponto é trocar o ShowModal por Execute; ou seja, não vamos mostrar o fomulário – somente inciar sua execução;

[code lang=”pascal”]

var
Service10: TService10;

implementation

{$R *.dfm}
uses UnitApp;

var LAppForm : TForm10;

procedure ServiceController(CtrlCode: DWord); stdcall;
begin
Service10.Controller(CtrlCode);
end;

function TService10.GetServiceController: TServiceController;
begin
Result := ServiceController;
end;

procedure TService10.ServiceStart(Sender: TService; var Started: Boolean);
begin
LAppForm := TForm10.create(nil);
LAppForm.execute;
end;

procedure TService10.ServiceStop(Sender: TService; var Stopped: Boolean);
begin
LAppForm.free;
end;

[/code]

Alteração no formulário para LOG em arquivo

[code lang=”pascal”]

procedure TForm10.Log(texto: string);
procedure LogServico;
begin
System.TMonitor.Enter(FLock); // controle de acesso ao arquivo de LOG
try
WriteLogText( ‘AppService.log’,texto);
finally
System.TMonitor.Exit(FLock);
end;
end;

begin
if FServico then
LogServico
else
TThread.Queue(nil,
procedure // sincroniza a escrita no memo1 – previne chamada multi-thread
begin
Memo1.Lines.Add(texto); // mostra o texto em um memo.
end);
end;
[/code]

Alterando o projeto para selecionar o Formulário  Alternando com Serviço

Como padrão um projeto “Service Application” inicializa o objeto TService e executa chamando RUN.
Quando o windows inicializa o serviço faz uma chamada /start para o projeto, então o primeiro passo é testar se o serviço já foi instalado “/install”. Se foi instalado executa o serviço, se não foi instalado ainda ou utilizar o parâmetro /app passar para executar o formulário.

[code lang=”pascal”]
program ProjAppService;

uses
Vcl.SvcMgr,
uServiceApp,
System.SysUtils,
UnitApp,
UnitService in ‘UnitService.pas’ {AppService: TService};

const
NomeServico = ‘AppService’;

{$R *.RES}

begin

{ parametros
/app -> executa como aplicativo
/install -> instala como serviço
/uninstall -> desinstala o serviço
}

if IsServiceInstalled(NomeServico) and
(not(FindCmdLineSwitch(‘app’, [‘-‘, ‘\’, ‘/’], true))) then
begin // é serviço
try
if not Application.DelayInitialize or Application.Installing then
Application.Initialize;
Application.CreateForm(TAppService, AppService);
AppService.name := NomeServico;
Application.Run;
finally
end;
end
else
begin // é app
Application.CreateForm(TForm10, Form10);
form10.ShowModal;
end;

end.
[/code]

 

 

Projeto de Exemplo

 

 

1 comentário

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *