Timer Queue in Windows Service

♀尐吖头ヾ 提交于 2019-12-19 09:44:01

问题


For a Windows Service, I need a timer to perform a certain task regularly. Of course, there are many options that seem superior to a timer (multithreading, calling method directly from the service's main thread), but they all have their disadvantages in this particular situation.

However, for obvious reasons, SetTimer() does not work without the message queue of a GUI. What I have done (in Free Pascal) is the following:

Create the timer:

MyTimerID := SetTimer(0, 0, 3333, @MyTimerProc);

In the main loop of the service, run the timer queue:

procedure TMyServiceThread.Execute;
var
  AMessage: TMsg;
begin
  repeat
    // Some calls
    if PeekMessage(AMessage, -1, WM_TIMER, WM_TIMER, PM_REMOVE) then begin
      TranslateMessage(AMessage);
      DispatchMessage(AMessage);
    end;
    // Some more calls
    TerminateEventObject.WaitFor(1000);
  until Terminated;
end;

And at the end, kill the timer:

KillTimer(0, MyTimerID)

Except of KillTimer always returning False, this works as anticipated.

I am interested in your feedback, however, if my implementation is correct - I just want to avoid messing with other application's messages and other side effects I am not aware of because of my inexperience with message handling.

Thanks!


回答1:


As discussed in the comments, you may not need a timer at all. You can simply use the timeout of the wait on your event to create a regular pulse:

while not Terminated do
begin
  case TerminateEventObject.WaitFor(Interval) of
  wrSignaled:
    break;
  wrTimeout:
    // your periodic work goes here
  wrError:
    RaiseLastOSError;
  end;
end;

The period of the pulse will be the interval plus the time taken to do the work. If you need a specific interval, and the work takes significant time, the Remy's suggestion of a waitable timer is the thing to do.

What you really don't want to do, at all costs, is use a message loop based timer. That's not appropriate for a service.




回答2:


I would opt for a waitable timer. No message queue is needed.

function WaitableTimerDelayFromMilliseconds(milliseconds: Integer): TLargeInteger;
begin
  Result := 0 - (TLargeInteger(milliseconds) * 10000);
end;

procedure TMyServiceThread.Execute;
var
  TimerInterval: Integer;
  DueTime: TLargeInteger;
  hTimer: THandle;
  Handles: array[0..1] of THandle;
begin
  TimerInterval := 10000; // use whatever interval you need
  DueTime := WaitableTimerDelayFromMilliseconds(TimerInterval);

  hTimer := CreateWaitableTimer(nil, FALSE, nil);
  if hTimer = 0 then RaiseLastOSError;
  try
    if not SetWaitableTimer(hTimer, DueTime, TimerInterval, nil, nil, False) then RaiseLastOSError;
    try
      Handles[0] := TerminateEventObject.Handle;
      Handles[1] := hTimer;

      while not Terminated do
      begin
        case WaitForMultipleObjects(2, PWOHandleArray(@Handles), False, INFINITE) of
          WAIT_FAILED:
            RaiseLastOSError;
          WAIT_OBJECT_0+0:
            Terminate;
          WAIT_OBJECT_0+1:
          begin
            // do your work
          end;
        end;
      end;
    finally
      CancelWaitableTimer(hTimer);
    end;
  finally
    CloseHandle(hTimer);
  end;
end;

Update: Or, as David Heffernan suggests, you could just wait on the termination event by itself:

procedure TMyServiceThread.Execute;
var
  TimerInterval: Integer;
begin
  TimerInterval := 10000; // use whatever interval you need

  while not Terminated do
  begin
    case TerminateEventObject.WaitFor(TimerInterval) of
      wrSignaled:
        Terminate;
      wrTimeout:
      begin
        // do your work
      end;
      wrError:
        RaiseLastOSError;
    end;
  end;
end;


来源:https://stackoverflow.com/questions/24702965/timer-queue-in-windows-service

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!