Windows Services Serviços no windows nada mais são do que aplicativos que rodam em segundo plano, normalmente, escritos para assumir uma tarefa especifica. Com esta simples afirmação traz à tona vários momentos em que poderia ter sido escrito um serviço, mas ao invés disto, por um motivo ou outro acabou sendo implementado um aplicativo, normal..., que fica-lá, ao lado do relógio. (lembrou?). Como dito, os motivos podem ser muitos, acredito que o principal é a falta de familiaridade com os recursos da ferramenta de desenvolvimento ou os padrões de software adotados. O momento certo para desenvolver um aplicativo as vezes passa despercebido pela maioria, que vai logo em File | New | VCL Forms application... (acertei). A principal razão dos serviços é que não devem ter contato com o utilizador, na verdade, o minimo possível, algo como nada alem do iniciar e parar. Não que isto seja um fator limitador para o desenvolvimento, pois, um serviço poderá como qualquer outro aplicativo ter suas configurações para determinar a sua forma de execução, mas sim o ideal a ser feito. As sugestões para desenvolvimento são: a) O aplicativo precisará de alguma interação com o usuário? sim, (confirmação de alguma coisa) desenvolva um aplicativo normal (VCL Forms Application). b) O aplicativo será autosuficiente? sim, Crie um serviço (Service Application); Criando um serviço simples Crie um novo serviço pelo e salve os arquivos como segue: Menu File | New | Other... Service Application. Unit1 – uWindowsService.pas Project1 – WindowsService.dpr/droj Por padrão sempre que um novo serviço é criado – TService1 (uWindowsService.pas) que é uma especie de DataModule é criado, de forma a permitir a inclusão de componentes não visuais, tais como queries e acesso a dados. Altere a property Name deste serviço para srvWindowsServicesDemo (ou outro de sua preferência, apenas lembre o nome dele) Não cometa o engano de continuar chamando isto de DataModule, e sim, apenas de serviço, pois, observe que diferentemente as propriedades são bem diferentes. Sobre as propriedades, bem sugestivas inclusive. AllowPause: Permite que o serviço seja pausado, momentaneamente. AllowStop: Permite que o serviço seja paralisado. DisplayName: Nome de exibição; ServiceStartName: Nome de usuário que o serviço utilizará para iniciar. Conforme o nível dos usuários do windows, permitindo que rode à nivel (e com permissões) do usuário definido. O padrão é Local System (em branco). Password: Senha do usuário ou em branco para o nível padrão. ServiceType: Tipo de serviço (são 3, serviço, driver e driver de serviço de arquivos) StartType: Tipo de inicio do serviço. Apenas ajuste a propriedade DisplayName por hora para: Demonstração ServDemo (ou algo de sua preferência). Caminho dos projeto (opcional) Por padrão e opcional, aqui os arquivos do projeto serão salvos em locais separados. Este ajuste pode ser realizado nas opções de projeto. (Ctrl + Shift + F11) bin\ - local para arquivos de saida (executavel e dlls compilados). dcu\ - local para units compiladas. Em resumo isto indica que quando o projeto for compilado (F9) os arquivos gerados pelo delphi serão em locais separados, sempre nos respectivos sub-diretorios do local onde foi salvo o projeto. Isto é uma opção particular, faça a sua! Ex: D:\WindowsServices\ + bin\ D:\WindowsServices\ + dcu\ Compilando o serviço Compilar o serviço não tem nada de especial, mas antes disto existe algo de interessante a ver no que tange como os serviçoes são executados. Você tranquilamente poderia pressionar F9 e ver o serviço rodar ...e fechar sem fazer absolutamente nada, o que no momento é obvio. Portanto apenas compile e veja o seguinte: -install – utilizado para instalar um serviço no gerenciamento do windows; -uninstall – utilizado para desinstalar um serviço net start %NomeServico% - utilizado para iniciar um serviço net stop %NomeServico% - utilizado para parar um serviço net pause %NomeServico% - utilizado para pausar um serviço net continue %NomeServico% - utilizado para continuar a execução do serviço Para instalar o serviço é necessário faze-lo via linha de comando (existe formas via programação, mas não serão abordadas aqui) e executar o aplicativo do serviço utilizando o parametro desejado. NomeServico.exe -Install ou NomeServico.exe -Uninstall. Aproveitando a linha de comando, utilize os comandos net start, net stop para deixar o serviço rodando ou parado. Duas imagens exibindo o serviço rodando normalmente. Mantenha esta tela aberta, irá precisar da mesma mais vezes até o final do artigo. Eventos de um serviço Como visto nas imagens anteriores nosso serviço não está exibindo a descrição do mesmo, (esta funcionalidade não foi implementada na classe base TService) entretanto o meio simples de definir a mesma é via programação, ajustando diretamente no registro do windows, aproveitando para conhecer alguns eventos de TService. Eventos de TService Todos os nomes bem sugestivos e seguindo o padrão de outros componentes já bem conhecidos. AfterInstall – Ocorre logo após a instalação do serviço, este será o evento utilizado para registrar a descrição do serviço. OnExecute – Semelhante ao execute de uma Thread, é onde o serviço propriamente faz o seu trabalho. OnStart – Indicado para iniciar o processamento do serviço, neste evento por exemplo poderia ser utilizado para implementar a inicialização de uma Thread. OnStop – Indicado para finalizar o processamento do serviço, neste evento por exemplo poderia ser implementado a finalização de uma Thread; OnPause – Indicado para implementar a pausa no processamento. OnContinue – Indicado para implementar a continuação de execução de um processamento. Código para definir a descrição do serviço é muito simples, será apenas instanciado um manipulador do registro e atribuindo a descrição no local especifico, adicione o codigo abaixo ao evento, que assim que instalado novamente a descrição será exibida! ... uses ..., Registry; ... ... procedure TsrvWindowsServicesDemo.ServiceAfterInstall(Sender: TService); var FRegistro : TRegistry; begin { Manipulado do registro } FRegistro := Tregistry.Create; try { Abrir o local especifico e ajustas as infromações da descrição } FRegistro.RootKey := HKEY_LOCAL_MACHINE; if FRegistro.OpenKey('\SYSTEM\CurrentControlSet\Services\' + Name, False) then begin FRegistro.WriteString('Description', 'Delphi - Windows Services exemplo'); FRegistro.CloseKey; end; finally FRegistro.Free; end; { Gravar mensagem nos eventos do windows, yes, você pode fazer isto! } LogMessage('Descrição do serivço foi adicionada normalmente', EVENTLOG_SUCCESS); end; ... Apenas como titulo de exemplo, será utilizado os eventos Execute, Start, Stop, Pause e Continue nos próximos sources para descrever um serviço simples, basicamente é um contador, incrementando sequencialmente uma variável e respeitando os respectivos eventos citados. Atente que para casos onde a tarefa executada seja mais longa (do que incrementar uma variável) em termos de tempo de execução é aconselhado a implementação de uma Thread própria para realizar tal tarefa, pois os métodos necessariamente precisam retornar o mais rápido possível (já estão dentro de uma thread principal) evitando que o serviço pare de responder ao gerenciador (windows). ... private { Private declarations } FCount : Integer; FStop : Boolean; ... ... procedure TsrvWindowsServicesDemo.ServiceStart(Sender: TService; var Started: Boolean); begin FCount := 0; FStop := False; end; procedure TsrvWindowsServicesDemo.ServiceStop(Sender: TService; var Stopped: Boolean); begin FStop := True; Stopped := True; LogMessage('Solicitação service stop', EVENTLOG_SUCCESS); end; procedure TsrvWindowsServicesDemo.ServicePause(Sender: TService; var Paused: Boolean); begin FStop := True; Paused := True; LogMessage('Solicitação service pause', EVENTLOG_SUCCESS); end; procedure TsrvWindowsServicesDemo.ServiceContinue(Sender: TService; var Continued: Boolean); begin // FCount := 0; Se zerar vai não vai continuar de onde parou FStop := False; Continued := True; LogMessage('Solicitação service continue', EVENTLOG_SUCCESS); end; procedure TsrvWindowsServicesDemo.ServiceExecute(Sender: TService); begin { Como um serviço é uma thread e neste caso a tarefa é simples, nada mais justo do que fazer um loop na mesma. Utilizado um if para não realizar processamento algum enquanto estiver paralisada. } while not Terminated do begin if not FStop then begin Inc(FCount); LogMessage(Format('Contador %d', [FCount]), EVENTLOG_SUCCESS); end; { Neste caso estritamente, atual como o conhecido Application.ProcessMessages. A grosso modo, desta forma não interrompe a comunicação do serviço com o gerenciador. Não seria necessário em caso de utilizar uma Thread especifica para fazer o trabalho } Sleep(1000); ServiceThread.ProcessRequests(False); end; end; Facilmente os métodos OnStart e OnStop implementar um procedimento de persistência em disco do contador para conseguir ser inicializado sempre no ultimo valor, mesmo após a desinstalação-instalação do serviço. Testando o serviço Uma vez o serviço instalado, net start nele, e abra o visualizador de eventos do windows, acompanhe a sequencia: ... Contador 1 Contador 2 ... Contador 22 solicitação de service pause net continue srvWindowServicesDemo solicitação de service continue Contador 23 Contador 24 ... Sugestões Se você estiver implementando, compilando, testando o serviço, evento por evento provavelmente encontre algumas dificuldades onde sugere que o aplicativo (serviço) esteja em uso, impedindo a compilação, tente apenas renomear para outro nome qualquer e em seguida tente compilar novamente. Em alguns casos, conforme o andamento da implementação dos metodos, é possível apenas utilizar os comandos Net Stop/Start via comando para compilar (e substituir) o serviço. Em outros casos, apenas o gerenciador de tarefas resolveu :-P; Revisão: Elazar Dornelles Ceza – 2013.05.02.0124 – Criação do artigo.