2009-07-02 17 views
6

Simplemente comprobando si hay alguna práctica recomendada al escribir un servicio de Windows.Servicio de Windows: no funciona en los momentos especificados (Delphi)

El Servicio (Single-hilo) tiene que trabajar en intervalos de tiempo especificados, en este momento sólo puedo pensar en:

  1. Uso del sueño(), a continuación, comprobar el tiempo en un bucle?
  2. ¿Utiliza un TTimer?

¿Algún consejo?

Respuesta

10

¿Esto necesita ser un servicio? ¿Podría quizás configurar una tarea programada en Windows?

+0

También me vino a la mente la primera pregunta. http://weblogs.asp.net/jgalloway/archive/2005/10/24/428303.aspx – ilitirit

+0

Una lectura interesante. Tiene puntos muy válidos, y también algunos de los comentarios. Me gusta especialmente esta cita: "es mejor utilizar un servicio existente que hacer el suyo que hace lo mismo. Pero ... el Programador de tareas de Windows apesta". Así que sí, el programador de tareas debe ser lo primero que debe intentar, y escribir un servicio solo debe hacerse en casos extremos. Pero estos casos existen, escribí un servicio de programación solo después de haber luchado contra el servicio del programador de tareas durante mucho tiempo y finalmente tuve que aceptar que simplemente no era lo suficientemente versátil. – mghie

3

Me gustaría utilizar el sueño.

Ambas opciones no tienen garantía de tiempo exacta, pero sleep devuelve los recursos a otros procesos.

+1

mensajes del temporizador se pueden utilizar con un bucle de mensajes, y GetMessage() hace bloquean si no hay mensajes en la cola. Los recursos no se desperdician de ninguna manera. – mghie

13

Realmente no importa que su servicio es de un solo hilo, como un servicio tendrá su código siempre se llama en diferentes contextos de rosca:

  • El jefe de servicio se iniciar, detener, pausar y reanudar la ejecución del servicio, y solicita el estado del servicio actual.

  • El servicio en sí tendrá al menos un hilo haciendo el trabajo real, que debe reaccionar a las solicitudes del administrador del servicio, cambiar el estado de ejecución del servicio solicitado y devolver la información solicitada. Un servicio debe reaccionar a las solicitudes del administrador de servicios en un tiempo razonablemente corto; de lo contrario, considerará que el servicio se cuelga y lo matará. Es por eso que, si el servicio puede tener código de ejecución o bloqueo a largo plazo, puede ser mejor tener más de un hilo de servicio.

si se debe usar Sleep() temporizador o mensajes de qué dependen también de la disponibilidad de un suministro de mensajes en los hilos de servicio. Si no tiene un mensaje de bomba debe usar Sleep() o devoluciones de llamada del temporizador. Si tiene un mensaje de bomba de todos modos, porque necesita comunicarse con otros procesos o subprocesos a través de mensajes de Windows, o necesita hacer cosas OLE, entonces puede ser más fácil utilizar mensajes de temporizador.

Hace algunos años escribí un servicio para la ejecución de tareas de fondo temporizadas, similar a la funcionalidad de Windows at o Unix cron. No utiliza gran parte de la VCL, solo algunas clases base. El Run() método del servicio es el siguiente:

procedure TScheduleService.Run; 
var 
    RunState: TServiceRunState; 
begin 
    while TRUE do begin 
    RunState := GetServiceRunState; 
    if (RunState = srsStopped) or (fEvent = nil) then 
     break; 
    if RunState = srsRunning then begin 
     PeriodicWork; 
     Sleep(500); 
    end else 
     fEvent.WaitFor(3000); 
    Lock; 
    if fServiceRunStateWanted <> srsNone then begin 
     fServiceRunState := fServiceRunStateWanted; 
     fServiceRunStateWanted := srsNone; 
    end; 
    Unlock; 
    end; 
end; 

Este utiliza Sleep() en un bucle, pero una solución utilizando

while integer(GetMessage(Msg, HWND(0), 0, 0)) > 0 do begin 
    TranslateMessage(Msg); 
    DispatchMessage(Msg); 
end; 

funcionaría igual de bien, y entonces se podrían usar los mensajes del temporizador de Windows.

+0

Evitaría el bucle de mensajes a menos que realmente lo necesite. El primer ciclo es básicamente lo que hago, aunque en un hilo. – mj2008

3

TTimer no es seguro para la rosca. Si tiene que usar el enfoque tipo TTimer en un hilo, sugeriría TDSiTimer de DSiWin32.

2

Nunca use un TTimer en un servicio, no siempre se comportará como esperaba, y no es seguro para la rosca.

Dentro de un servicio siempre utilizo mis propias variables de intervalo de tiempo, y duermo entre los tiempos de ejecución de tareas. Para mantener la respuesta del servicio, duermo durante periodos cortos, generalmente 1-2000 ms, y luego proceso los mensajes antes de verificar mi intervalo para saber si es el momento de ejecutar la 'tarea'. Si aún no es el momento, vuelva a dormir, y vuelva a verificar después - en un bucle. De esta forma, le devuelve recursos, pero también puede responder a la entrada del usuario (Detener, Pausa) antes de que se ejecute la siguiente tarea.

0

I alwasy usar algo como esto en un servicio:

unit uCopy; 

interface 

uses 
    Windows, Messages,.......; 

procedure MyTimerProc(hWindow : HWND; uMsg : cardinal; idEvent : cardinal; dwTime : DWORD); stdcall; 

type 
    TFileCopy= class(TService) 
    procedure ServiceStart(Sender: TService; var Started: Boolean); 
    procedure ServiceStop(Sender: TService; var Stopped: Boolean); 
    private 
    { Private declarations } 
    public 
     { Public declarations } 
    end; 

VAR 
    timerID : UINT; 

const 
SECONDS = 900000; 

procedure TFileCopy.ServiceStart(Sender: TService; 
    var Started: Boolean); 
Begin 
timerID := 0; //Probably not needed. 
timerID := SetTimer(0, 1, SECONDS, @MyTimerProc); 
End; 

procedure MyTimerProc(hWindow : HWND; uMsg : cardinal; idEvent : cardinal;dwTime : DWORD); stdcall; 
Begin 
    //Kill timer while trying.. If this function takes longer than the interval to run, I didn't want a build up. If that was possible. 
    KillTimer(0, timerID); 
    timerID := 0; //Is it needed? 
//DO WORK. 
{ 
//I was Connecting to a Network Drive, Minutes, seconds..... 
//I only wanted to run this Every day at 2 AM. 
//So I had my timer set to 15 minutes, once it got between 30 and 10 minutes of my 2 AM deadline, 
//i killed the existing timer and started a new one at 60000. 
//If it was within 10 minutes of my 2 AM, my interval changed to 500. 
//This seems to work for me, my files get copied everyday at 2 AM. 
} 
//Work Complete. Start timer back up. 
    timerID := SetTimer(0, 1, SECONDS, @MyTimerProc); 
End; 

procedure TFileCopy.ServiceStop(Sender: TService; 
    var Stopped: Boolean); 
Begin 
if timerID > 0 then 
    KillTimer(0, timerID); 
End; 

Por supuesto, tenía algunas try..catch en la mayoría de los lugares junto con la escritura a los registros y correo electrónico .... Tengo un servicio que funciona con estas técnicas desde hace más de un año. De ninguna manera es la regla. Por favor dígame si hay una mejor manera. Siempre estoy buscando formas de mejorar mi conocimiento de Delphi. Además, lo siento si me perdí la fecha límite para publicar una pregunta.

-Trey Aughenbaugh

Cuestiones relacionadas