Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to be notified for time changes in Windows Service

I created a WindowProc to be notified for system time changes:

constructor TJJWScheduler.Create;
begin 
  fTimeChangeWnd := Classes.AllocateHWnd(TimeChangeWndProc);
end;

procedure TJJWScheduler.TimeChangeWndProc(var msg: TMessage);
var
  i: integer;
begin
  case msg.Msg of
    WM_TIMECHANGE:
      begin
        // my things
      end;
  end;
end;

This code is running inside a Windows Service. The problem is that it isn't fired when I change the system time!

Why not the broadcast message (WM_TIMECHANGE) isn't delivered to my window? There is another way to do this without a loop?


EDIT

I don't known why, but I hardcoded the PeekMessage to process messages to that window, and everything comes to work fine. The code below solved my problem:

 var 
   msg: TMsg;

 if PeekMessage(msg, fTimeChangeWnd, WM_TIMECHANGE, WM_TIMECHANGE, PM_REMOVE) then
 begin
   TranslateMessage(msg);
   DispatchMessage(msg);
 end;

This workaround is very strange, because I already have others windows processing messages (by generic ProcessMessages), only this one isn't processing its messages.

like image 405
Beto Neto Avatar asked Feb 18 '23 13:02

Beto Neto


1 Answers

The reason your window was not receiving the WM_TIMECHANGE messages is that your window is created from a secondary thread.

Each thread in a process has its own message queue. Synchronous messages are delivered when you service the message queue, so ever for a non-queued message like WM_TIMECHANGE you do need to service the secondary threads message queue in order for messages to be delivered.

For example, look at the documentation for GetMessage, the most common way to pull messages of the queue:

The function dispatches incoming sent messages until a posted message is available for retrieval.

The same is true for PeekMessage. It dispatches incoming sent messages before peeking the queue.

There are a handful of other ways for sent messages to be dispatched, but these are the primary ones.

Now, I suspect that it may be inconvenient for you to periodically dispatch messages from your secondary thread. If your secondary thread does nothing else then it can simply sit in the traditional GetMessage, TranslateMessage, DispatchMessage loop. And most of the time it will happily block in GetMessage dispatching any incoming sent messages. But if your secondary thread does more work then that's probably not a viable option.

You are already running and servicing a message queue on the main service thread. It may make more sense to make your listener window have affinity with the main service thread. Do that by creating it from code that runs in the main service thread.

Note also that AllocateHWnd is documented not to be thread-safe. You must not call it from any thread other than the main thread of the process. So, if you do wish to remain on a secondary thread, you'll need to use CreateWindow rather than AllocateHWnd. But this is perhaps yet another good reason to move this window onto the main thread.

like image 90
David Heffernan Avatar answered Feb 20 '23 03:02

David Heffernan