Clique para saber mais...
  Home     Download     Produtos / Cursos     Revista     Vídeo Aulas     Fórum     Contato   Clique aqui para logar | 12 de Junho de 2026
  Login

Codinome
Senha
Salvar informações

 Esqueci minha senha
 Novo Cadastro

  Usuários
80 Usuários Online

  Revista ActiveDelphi
 Assine Já!
 Edições
 Sobre a Revista

  Conteúdo
 Apostilas
 Artigos
 Componentes
 Dicas
 News
 Programas / Exemplos
 Vídeo Aulas

  Serviços
 Active News
 Fórum
 Produtos / Cursos

  Outros
 Colunistas
 Contato
 Top 10

  Publicidade

  [Artigos]  [Intermediário] - Criando um serviço do windows no Delphi
Publicado por rboaro : Quinta, Maio 02, 2013 - 01:54 GMT-3 (1095 leituras)
Comentários 3 Comentários   Enviar esta notícia a um amigo Enviar para um amigo   Versão para Impressão Versão para impressão
Diego Garcia Uma boa opção para criação de uma aplicação que precisa estar no ar o tempo todo e não deve ou não precisa ter intervenção humana, é a criação de um serviço do windows. Criando um serviço do windows podemos passar a responsabilidade de iniciar a aplicação para o próprio windows, entre outras vantagens. Então mão na massa. O Delphi já possui um esqueleto pronto para a criação de um serviço do windows, basta ir em File -> New -> Other -> Delphi Projects -> Service Application:


Essa opção irá criar um novo projeto com uma unit contendo a classe do serviço (descendente da classe TService) já com dois métodos implementados, o ServiceController e o GetServiceController, métodos necessários para a execução do serviço, não sendo necessário realizar alterações. Essá unit criada contem uma espécie de DataModule que possui alguns eventos e propriedades cruciais para o funcionamento do serviço. Vejamos alguns dos principaiseventos de execução do serviço:

OnStart: Este evento é executado assim que o serviço é iniciado.
OnExecute: Este é o evento onde será implementado a funcionalidade do serviço, ele é executado logo após o evento OnStart, ou seja, quando o serviço entrar em execução definitiva. Funciona semelhante ao execute das threads.
OnStop: Este evento é executado assim que o serviço é parado.
OnPause: Este evento é executado quando o serviço é pausado.
OnContinue: Este evento é executando quando o serviço voltar a ser executado após uma pausa.
Alem dos eventos de execução, existem também os eventos de instalação do serviço:

BeforeInstall / AfterInstall: Eventos executados antes (before) ou depois (after) da instalação do serviço
Assim como os eventos, existem propriedades cruciais para o bom funcionamento de um serviço do windows, dentre essas propriedades, destaco as seguintes:

StartType: Esta propriedade define o modo de iniciação do serviço, por padrão essa opção vem marcada como stAuto que significa que o serviço será iniciado junto com o windows. Além da opção stAuto destaco a opção stManual que define que o serviço deverá ser iniciado manualmente e a opção stDisabled que define que somente um administrador pode iniciar o serviço.
DisplayName: Esta propriedade define o nome que será apresentado para o serviço no gerenciador de serviços do windows.
Dependencies: Define uma lista de dependências para a execução do serviço, ou seja, o serviço só será iniciado se todas as suas dependências (outros serviços) já estejam iniciados.
Conhecendo os principais eventos e principais propriedades da classe TService podemos começar a implementar nosso serviço do windows.
Como exemplo, faremos um serviço de autenticação via protocolo TCP/IP. Obviamente, será um exemplo didático com o único intuito de demonstrar a criação e o funcionamento de um serviço do windows, sendo assim, não entraremos em maiores detalhes de como essa autenticação será feita.

Para que seja mais fácil a manutenção do nosso código e a realização dos testes, vamos separar o código que gerencia o serviço do windows da nossa regra de negócios, para isso, o nosso processo de “autenticação” será feito através de um DataModule com um componente IdTCPServer.

Sem nos aprofundar muito na “regra de negócio”, temos a seguinte classe em nosso data module:

type
TdmNucleo = class(TDataModule)
IdTCPServer1: TIdTCPServer;
procedure IdTCPServer1Execute(AContext: TIdContext);
private
public
function IniciarServidorTCP() : boolean;
procedure PararServidorTCP();
function Autenticar(const AUserAndPass : string):boolean;
end;

Com a seguinte implementação:

function TdmNucleo.Autenticar(const AUserAndPass: string): boolean;
begin
Result := AnsiSameText(AUserAndPass,'admin@admin');
end;

function TdmNucleo.IniciarServidorTCP : boolean;
begin
try
Self.IdTCPServer1.DefaultPort := 9056;
Self.IdTCPServer1.Active := true;
Result := true;
except
Result := false;
end;
end;

procedure TdmNucleo.PararServidorTCP;
begin
Self.IdTCPServer1.Active := false;
end;

procedure TdmNucleo.IdTCPServer1Execute(AContext: TIdContext);
const
C_PROTOCOLO_REQUEST = 'TCP.Server.Autentication.Request';
C_PROTOCOLO_RESPONSE = 'TCP.Server.Autentication.Response';
var
sMsg: string;
begin
sMsg := AContext.Connection.IOHandler.ReadLn();
if AnsiSameText(sMsg,C_PROTOCOLO_REQUEST) then
begin
sMsg := AContext.Connection.IOHandler.ReadLn();
AContext.Connection.IOHandler.WriteLn(C_PROTOCOLO_RESPONSE);
AContext.Connection.IOHandler.WriteLn(IfThen(Self.Autenticar(sMsg),'OK','FALHA'));
end;
end;

Vale ressaltar para quem não conhece o funcionamento do componente IdTCPServer, o evento OnExecute é executado constantemente enquanto o objeto está ativo (propriedade Active setada como True), sendo assim, esse evento é utilizado para ficar “ouvindo” as requisições TCP/IP na porta em que o componente está configurado (propriedade Port). Como prometido, implementação extremamente simples, para que o foco não seja desviado e por falar em foco, voltemos ao assunto do post, o serviço do windows. Com nossa regra de negócio já resolvida, vamos preparar nosso serviço. Como optamos por separar a regra de negócio do controlador do serviço, a parte de fazer o serviço funcionar se tornou extremamente fácil. Para que a nossa “autenticação” já passe a funcionar, basta escrever o evento OnStart do nosso serviço:

procedure TsrvTCPServer.ServiceStart(Sender: TService; var Started: Boolean);
begin
Started := dmNucleo.IniciarServidorTCP();
end;

Veja que se, caso ocorra um erro no método dmNucleo.IniciarServidorTCP() e este retorne False, o serviço não será iniciado, graças ao parâmetro Started do evento OnStart, ou seja, o que determina se o serviço será iniciado ou não é o valor atribuído a este parâmetro. Caso tudo corra bem, isso é o suficiente para ver nosso serviço funcionando. Porém, vamos deixar nosso serviço mais completo, vamos nos certificar que o nosso componente IdTCPServer seja fechado quando o serviço parar, para isso, vamos utilizar o evento OnStop:

procedure TsrvTCPServer.ServiceStop(Sender: TService; var Stopped: Boolean);
begin
dmNucleo.PararServidorTCP();
end;

E para finalizar e finalmente colocar o serviço para rodar, vamos definir um texto para servir de nome do serviço no gerenciador de serviços do windows. Como em nosso exemplo estamos monitorando a porta 9056, colocaremos um nome que descreva isso. Para isso, utilizaremos o propriedade DisplayName do serviço e para tornar essa rotina dinâmica, iremos configurar essa propriedade no evento OnBeforeInstall:

procedure TsrvTCPServer.ServiceBeforeInstall(Sender: TService);
begin
Self.DisplayName := 'TCP Server (PORTA: 9056)'
end;

Caso a escolha por qual porta monitorar estivesse dentro de um arquivo INI por exemplo e sejam necessárias varias instancias desse serviço, faz todo sentido mostrar no nome do serviço a qual porta ele está responsavél, por isso, é uma boa pratica atribuir a um evento a responsabilidade de nomear o serviço e na minha opinião o OnBeforeInstall é o melhor para isso. Outro recurso interessante é adicionar a descrição do serviço, porem isso é um pouco mais complicado, sendo necessário manipular o registro do windows, para isso, o mais correto é a utilização do evento OnAfterInstall, já que só conseguiremos atualizar as configurações do registro do windows para o serviço após realizarmos a instalação do mesmo:

procedure TsrvTCPServer.ServiceAfterInstall(Sender: TService);
var
regEdit : TRegistry;
begin
regEdit := TRegistry.Create(KEY_READ or KEY_WRITE);
try
regEdit.RootKey := HKEY_LOCAL_MACHINE;
if regEdit.OpenKey('\SYSTEM\CurrentControlSet\Services\' + Name,False) then
begin
regEdit.WriteString('Description','Servidor de autenticação TCP');
regEdit.CloseKey;
end;
finally
FreeAndNil(regEdit);
end;
end;

Finalmente podemos testar nosso serviço, só é preciso realizar a sua instalação no windows, o que é muito simples, via linha de comando, basta (como administrador) executar a seguinte instrução: “PATH_COMPLETO_DO_SERVIÇO /INSTALL”, exemplo:

D:\Projetos\SrvAutenticacao\Bin\SrvAutenticacao.exe /INSTALL

Após rodar o comando acima e obter uma mensagem de sucesso, nosso serviço já está instalado, basta agora dar o seu start.



Para desinstalar o serviço, basta (também como administrador) executar esta instrução:“PATH_COMPLETO_DO_SERVIÇO /UNINSTALL”, exemplo:

D:\Projetos\SrvAutenticacao\Bin\SrvAutenticacao.exe /UNINSTALL

E assim, o básico para criação de um serviço do windows está feito. Caso alguém esteja seguindo o exemplo e gostaria de testar, basta criar um aplicativo cliente com um IdTCPClient e fazer o seguinte código:

procedure TFrmTeste.TestarServicoTCPServer;
begin
Self.IdTCPClient1.Host := 'localhost';
Self.IdTCPClient1.Port := 9056;
Self.IdTCPClient1.Connect;
Self.IdTCPClient1.Socket.WriteLn('TCP.Server.Autentication.Request');
Self.IdTCPClient1.Socket.WriteLn('admin@admin');

if AnsiSameText(Self.IdTCPClient1.Socket.ReadLn(), 'TCP.Server.Autentication.Response') then
ShowMessage(Self.IdTCPClient1.Socket.ReadLn());
end;

Para finalizar, uma das coisas chatas de se trabalhar com um serviço do windows (ou qualquer aplicação que não requer interface gráfica) é realizar o debug. Nem sempre o recurso do Attach to Process funciona como desejamos. Pensando nisso, eu geralmente crio um formulário com uma interface GUI e utilizo ele para realizar testes e depurações. Agora vem a pergunta, “como farei para usar esse formulário?”, simples, criaremos a seguinte regra, se por linha de comando nosso serviço receber como parâmetro a palavra ”GUI“, significa que usaremos a interface gráfica, caso contrário, iniciaremos como um serviço normal.
Ótimo, mas como podemos fazer isso ? simples, basta usar esse macete no arquivo DPR do projeto:

var
sModo : string;
begin
sModo := ParamStr(1);
Application.CreateForm(TdmNucleo, dmNucleo);
if AnsiSameText(sModo,'GUI') then
begin
Vcl.forms.Application.CreateForm(TfrmGUIService, frmGUIService);
Vcl.forms.Application.Run;
end
else
begin
if not Vcl.SvcMgr.Application.DelayInitialize or Vcl.SvcMgr.Application.Installing then
Vcl.SvcMgr.Application.Initialize;
Vcl.SvcMgr.Application.CreateForm(TsrvTCPServer, srvTCPServer);
Vcl.SvcMgr.Application.Run;
end;

Basicamente, através da função ParamStr() que retorna as strings da linha de comando, estamos tomando a decisão se iremos iniciar a aplicação como uma aplicação VCL comum ou um serviço do windows. É possível sempre enviar o parâmetro GUI quando estiver executando o projeto pelo Delphi. Bastar ir em Project -> Options -> Debugger -> Parameters e colocar a palavra GUI, esta opção define quais serão os parâmetros de execução do projeto. Neste meu exemplo, quando eu estiver executando o projeto com o parâmetro GUI, terei uma interface como esta:



Com isso, fica muito mais fácil de realizar as depurações. Vale ressaltar também que isso foi possível pois optamos por não deixar a regra de negocio diretamente no serviço.

Diego Garcia
http://drgarcia1986.wordpress.com
http://twitter.com/drgarcia1986


Comentários Comentários
   Ordem:  
Comentários pertencem aos seus respectivos autores. Não somos responsáveis pelo seus conteúdos.


por: tiagoshimizu (tiagoshimizu@hotmail.com) : Mai 02, 2013 - 03:46
(Informações sobre o membro | Enviar uma mensagem) http://http://
Só resaltando, não observei se está no texto, mas na função PararServidorTCP, para a linha "AContext.Connection.IOHandler.WriteLn(IfThen(Self.Autenticar(sMsg),'OK','FALHA'));", declarar a unit
StrUtils.
Parabéns pela matéria.
  Edição 112

Revista ActiveDelphi

  50 Programas Fontes


  Produtos

Conheça Nossos Produtos

Copyright© 2001-2016 – Active Delphi – Todos os direitos reservados