3

サーバーが名前付きパイプを使用してクライアントにメッセージを送信する小さなクライアントサーバーアプリケーションがあります。クライアントには、メインの GUI スレッドと 1 つの「受信スレッド」の 2 つのスレッドがあり、名前付きパイプを介してサーバーから送信されたメッセージを受信し続けます。なんらかのメッセージを受信するたびに、カスタム イベントを発生させたいのですが、そのイベントは呼び出し元のスレッドではなく、メインの GUI スレッドで処理する必要があります。それも可能です)。

これが私がこれまでに持っているものです:

tMyMessage = record
    mode: byte;
    //...some other fields...
end;

TMsgRcvdEvent = procedure(Sender: TObject; Msg: tMyMessage) of object;

TReceivingThread = class(TThread)
private
  FOnMsgRcvd: TMsgRcvdEvent;
  //...some other members, not important here...
protected
  procedure MsgRcvd(Msg: tMyMessage); dynamic;
  procedure Execute; override;
public
  property OnMsgRcvd: TMsgRcvdEvent read FOnMsgRcvd write FOnMsgRcvd;
  //...some other methods, not important here...
end;

procedure TReceivingThread.MsgRcvd(Msg: tMyMessage);
begin
  if Assigned(FOnMsgRcvd) then FOnMsgRcvd(self, Msg);
end;

procedure TReceivingThread.Execute;
var Msg: tMyMessage
begin
  //.....
  while not Terminated do begin //main thread loop
    //.....
    if (msgReceived) then begin
      //message was received and now is contained in Msg variable
      //fire OnMsgRcvdEvent and pass it the received message as parameter
      MsgRcvd(Msg); 
    end;
    //.....
  end; //end main thread loop
  //.....
end;

たとえば、TForm1クラスのメンバーとしてイベントハンドラーを作成できるようにしたいと思います

procedure TForm1.MessageReceived(Sender: TObject; Msg: tMyMessage);
begin
  //some code
end;

これは受信スレッドではなく、メイン UI スレッドで実行されます。特に、受信スレッドがイベントを発生させ、イベント ハンドラー メソッドの戻りを待たずに実行を継続することを希望します (基本的には、.NET Control.BeginInvokeメソッドのようなものが必要です) 。

私はこれが本当に初心者なので(数時間前にカスタムイベントを定義する方法を学ぼうとしました)、それが可能かどうか、または何か間違っているかどうかはわかりません。あなたの助け。

4

5 に答える 5

2

メッセージをウィンドウに送信するには、PostMessage (非同期) または SendMessage (同期) API を使用する必要があります。ある種の「キュー」を使用するか、素晴らしい OmniThreadLibrary を使用してこれを行うこともできます (強くお勧めします)。

于 2010-08-27T17:32:39.377 に答える
2

すでにいくつかの回答がありましたが、質問の厄介な部分については言及していません。

tMyMessage = record
    mode: byte;
    //...some other fields...
end;

ネイティブの Windows メッセージ処理に Delphi やその他のラッパーを使用する場合、.NET 環境で当たり前のことをすべて実行できるわけではないことに注意してください。ランダムなデータ構造をイベント ハンドラーに渡すことができると期待するかもしれませんが、それはうまくいきません。その理由は、メモリ管理の必要性です。

.NET では、どこからも参照されなくなったデータ構造は、ガベージ コレクションによって確実に破棄されます。Delphi では、このような余裕はありません。割り当てられたメモリ ブロックも正しく解放されるようにする必要があります。

Windows では、メッセージの受信者は、ユーザーが操作するウィンドウ ハンドル (a HWND)SendMessage()PostMessage()、ユーザーが操作するスレッドですPostThreadMessage()。どちらの場合も、メッセージは 2 つのデータ メンバーのみを運ぶことができます。これらは両方とも機械語幅であり、最初の型WPARAMは 、2 番目の型は ですLPARAM)。任意のランダム レコードをメッセージ パラメータとして単純に送信または投稿することはできません。

Delphi が使用するすべてのメッセージ レコード タイプは基本的に同じ構造を持ち、上記のデータ サイズ制限に対応します。

2 つ以上の 32 ビット サイズの変数で構成される別のスレッドにデータを送信する場合は、注意が必要です。送信できる値のサイズ制限により、レコード全体を送信できず、そのアドレスのみを送信できる場合があります。これを行うには、送信側スレッドでデータ構造を動的に割り当て、アドレスをメッセージ パラメーターの 1 つとして渡し、受信側スレッドで同じパラメーターを同じ型の変数のアドレスとして再解釈してから、レコードを解放し、動的に割り当てられたメモリ構造を解放します。

そのため、イベント ハンドラーに送信する必要があるデータの量によっては、tMyMessageレコードを変更する必要がある場合があります。これを機能させることはできますが、イベント データの型チェックを使用できないため、必要以上に困難です。

これには少し違った方法で取り組むことをお勧めします。ワーカー スレッドから GUI スレッドに渡す必要のあるデータはわかっています。メッセージと共に直接送信する代わりに、イベント パラメータ データを入れるキュー データ構造を作成するだけです。このキューをスレッド セーフにします。つまり、クリティカル セクションでキューを保護し、異なるスレッドから同時にキューへの追加またはキューからの削除が安全に行われるようにします。

新しいイベント処理を要求するには、データをキューに追加するだけです。最初のデータ要素が以前は空だったキューに追加されたときにのみ、メッセージを受信スレッドにポストします。受信スレッドは、メッセージを受信して​​処理し、キューからデータ要素をポップして、キューが再び空になるまで一致するイベント ハンドラーを呼び出す必要があります。最高のパフォーマンスを得るには、キューをできるだけ早くロックする必要があり、イベント ハンドラーが呼び出されている間、一時的にロックを解除する必要があります。

于 2010-08-27T23:43:04.120 に答える
1

プライベート メンバーを宣言する

FRecievedMessage: TMyMEssage

そして、保護された手順

procedure PostRecievedMessage;
begin
   if Assigned(FOnMsgRcvd) then FOnMsgRcvd(self, FRecievedMessage);
   FRecievedMessage := nil;
end;

そして、ループ内のコードを次のように変更します

if (msgReceived) then begin
  //message was received and now is contained in Msg variable
  //fire OnMsgRcvdEvent and pass it the received message as parameter
  FRecievedMessage := Msg;
  Synchronize(PostRecievedMessage); 
end;

完全に非同期にしたい場合は、代わりに PostMessage API を使用してください。

于 2010-08-27T17:45:05.967 に答える
0

確認したい場合は、私のフレームワークでこれを行うことができます ( http://www.csinnovations.com/framework_overview.htm )。

于 2011-02-20T10:03:54.083 に答える
0

Synchronize メソッドのドキュメントを確認してください。あなたのようなタスクのために設計されています。

于 2010-08-27T16:52:02.190 に答える