24 de novembro de 2009

Criando Serviços do Windows - parte 2

O Delphi e o C++ Builder têm um tipo de projeto específico para a criação de Serviços do Windows. Então, o procedimento para criar uma estrutura básica de um Serviço nesses ambientes é selecionar o menu File -> New e escolher Service Application.

Um novo projeto será criado e incluirá um fonte com uma classe que representa o Serviço do Windows. A estrutura desse fonte é similar a um Data Module e permite que sejam adicionados componentes não-visuais para uso pelo Serviço - lembre-se que o serviço não tem interface gráfica e, portanto, não faz sentido admitir a inclusão de componentes visuais. O trecho abaixo reproduz a declaração inicial da classe gerada:
type
TWMyService = class(TService)
private
{ Private declarations }
public
function GetServiceController: TServiceController; override;
{ Public declarations }
end;

Veja que a base para a classe de Serviço chama-se TService. É essa classe da VCL que encapsula um Serviço e quem introduz as propriedades que devemos configurar e os eventos que teremos que responder para que o Serviço execute suas tarefas.

O passo seguinte, então, é configurar visualmente as propriedades, conforme a necessidade. Segue abaixo uma lista com as principais propriedades da classe TService e uma breve explicação de sua utilidade.
DisplayName é o nome que identificará nosso serviço no Service Control Manager (SCM), que é a parte do Windows responsável pelo gerenciamento de Serviços e que pode ser acessado através da aplicação Services existente no Painel de Controle do Windows.
ServiceType é o tipo de Serviço que se está criando. O valor padrão (e situação mais comum) é stWin32, indicando que se trata de um serviço convencional. Os outros valores permitidos transformam o Serviço num Device Driver ou File System Driver.
ServiceStartName e Password indicam o nome de usuário e senha com o qual o Serviço irá se apresentar ao Windows (quando ServiceType está configurado com stWin32. As credenciais (permissões de acesso) desse usuário é que determinarão o que o Serviço pode ou não fazer dentro do Windows. Conforme dito no post anterior, pode-se informar um dos usuários criados e mantidos pelo Windows (Local System, LocalService ou Network Service). Se for deixado em branco, o Windows assume que deve ser considerado o usuário Local System.
Dependencies lista os Serviços dos quais nosso Serviço depende. Isto é, se nosso Serviço só funcionar quando um ou mais Serviços também estiverem funcionando corretamente, os nomes desses Serviços devem ser incluídos nesta lista. O SCM, então, só poderá iniciar a execução do nosso Serviço após receber confirmação de que os Serviços dos quais ele depende estão com status ok.
StartType indica o modo de inicialização do Serviço. O valor padrão é stAuto, sinalizando que o Serviço deve ser iniciado automaticamente, junto com o Windows e sem que seja necessário um usuário se logar.
WaitHint é o tempo máximo (em milissegundos) que o SCM deve aguardar quando interagindo com o Serviço. Assim, se o tempo se esgotar o SCM interpretará que o serviço "pendurou". Caso as tarefas executadas pelo Serviço sejam demoradas, é conveniente chamar em pontos estratégicos do código a função ReportStatus da classe TService. Isso permitirá ao SCM detectar que o Serviço está ativo mas executando uma operação demorada.
A propriedade Terminated informa ao Serviço que ele deve cessar a execução. Isso pode ser devido ao fato de que alguém solicitou a interrupção do Serviço ou que o próprio Serviço detectou que sua execução não é mais necessária.

Em relação aos eventos disponíveis para a classe TService, o principal é o OnExecute. É aqui que o funcionamento do Serviço ocorre, seja através do monitoramento de eventos, seja através da publicação de meios para comunicação com outros processos (ou ambos). O evento OnExecute é disparado uma única vez e, quando ele termina sua execução, determina também o término da execução do Serviço. Juntando esse fato com o funcionamento da propriedade Terminated citada antes, a codificação do OnExecute tem, na maioria das vezes, a seguinte aparência:
procedure TWMyService.ServiceExecute(Sender: TService);
begin
while (not Terminated) do
begin
{ Processa aqui as tarefas relacionadas com o Serviço }
{ ... }

ReportStatus;
end;
end;

No caso do serviço que executa processos agendados na ABC71, esse laço dentro do evento OnExecute verifica de tempos em tempos os registros existentes no banco de dados do ERP e executa o processo numa thread separada quando é atingida a data e hora agendada para execução.

Isso levanta uma outra questão. Onde armazenar as configurações para conexão com o Banco de Dados ? É muito comum que isso seja feito através de um arquivo mas optei por armazenar tais informações no Registry do Windows, mais apropriado na minha opinião. Foi construído, então, um programa com interface gráfica que permite aos usuários configurar a conexão e monitorar a execução dos processos. Como o usuário usado pelo Serviço é diferente do usuário logado no Windows para realizar as confugurações, a gravação no Registry deve ser feita sob a chave HKEY_LOCAL_MACHINE, facilmente acessível por ambos.

Nenhum comentário :

Postar um comentário

OBS: Os comentários enviados a este Blog são submetidos a moderação. Por isso, eles serão publicados somente após aprovação.

Observação: somente um membro deste blog pode postar um comentário.