4

システム時刻の変更を通知するWindowProcを作成しました。

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;

このコードは、Windowsサービス内で実行されています。問題は、システム時刻を変更しても起動されないことです。

ブロードキャストメッセージ(WM_TIMECHANGE)がウィンドウに配信されないのはなぜですか?ループなしでこれを行う別の方法はありますか?


編集

理由はわかりませんが、そのウィンドウへのメッセージを処理するようにPeekMessageをハードコーディングすると、すべてが正常に機能するようになります。以下のコードは私の問題を解決しました:

 var 
   msg: TMsg;

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

この回避策は非常に奇妙です。私はすでに他のウィンドウでメッセージを処理しているので(一般的なProcessMessagesによって)、このウィンドウだけがメッセージを処理していません。

4

1 に答える 1

8

WM_TIMECHANGEウィンドウがメッセージを受信しなかった理由は、ウィンドウがセカンダリスレッドから作成されているためです。

プロセス内の各スレッドには、独自のメッセージキューがあります。同期メッセージはメッセージキューを処理するときに配信されるため、キューに入れられていないメッセージのWM_TIMECHANGE場合は、メッセージを配信するためにセカンダリスレッドのメッセージキューを処理する必要があります。

たとえばGetMessage、キュ​​ーのメッセージをプルする最も一般的な方法であるのドキュメントを参照してください。

この関数は、投稿されたメッセージを取得できるようになるまで、送信された着信メッセージをディスパッチします。

同じことが。にも当てはまりますPeekMessage。キューをピークする前に、送信された着信メッセージをディスパッチします。

送信されたメッセージをディスパッチする方法は他にもいくつかありますが、これらが主要な方法です。

さて、セカンダリスレッドから定期的にメッセージをディスパッチするのは不便かもしれません。セカンダリスレッドが他に何もしない場合は、従来の、、ループに単純に配置GetMessageできTranslateMessageますDispatchMessage。そして、ほとんどの場合、GetMessage送信された着信メッセージのディスパッチを喜んでブロックします。しかし、セカンダリスレッドがより多くの作業を行う場合、それはおそらく実行可能なオプションではありません。

メインサービススレッドでメッセージキューをすでに実行してサービスを提供しています。リスナーウィンドウをメインサービススレッドと親和性を持たせる方が理にかなっている場合があります。これを行うには、メインサービススレッドで実行されるコードから作成します。

AllocateHWndまた、スレッドセーフではないことが文書化されていることにも注意してください。プロセスのメインスレッド以外のスレッドから呼び出すことはできません。したがって、セカンダリスレッドに留まりたい場合は、CreateWindowではなくを使用する必要がありますAllocateHWnd。しかし、これはおそらく、このウィンドウをメインスレッドに移動するもう1つの理由です。

于 2013-02-14T13:22:16.513 に答える