2016-06-14 7 views
1

私はDelphiでサーバーを作成し、クライアントの応答を待ち受けるプログラムを作成しました。クライアントはサーバーに接続し、データを送信し、直ちに切断します。問題は、データが受信されたときにプログラムが応答を停止することがあることです。そして、私がプログラムを終了する時代のほとんどは、EOSError 1400 [Invalid window handle。](私はこのエラーがソックススレッドのためであることを知っています)を見ています。ウィンドウを閉じる前にTCPServerのActiveプロパティをfalseに設定しています。私はTTCPServerとTIdTCPServerの両方をテストしましたが、問題は解決しません。TCPServerとError1400

これはTTCPServerのための私のコードです:

procedure TMonitorFrm.TcpSerAccept(Sender: TObject; 
    ClientSocket: TCustomIpClient); 
var 
    b: array [0..300] of Byte; 
    z, k: Byte; 
    s: String; 
begin 
repeat 
    z := ClientSocket.ReceiveBuf(b, SizeOf(b), 0); 
    s := ''; 
    if (z > 6) then 
    begin 
    for k := 0 to z - 1 do 
     begin 
     s := s + IntToHex(b[k], 2); 
     if (k in [2, 5, 6]) then s := s + ' '; 
     end; 
    FullLst.Items.Add(s); 
    FullMessageEdt.Text := s; 
    if (Length(s) > 17) then Delete(s, 1, 17) else s := ''; 
    k := MessagesGrd.RowCount; 
    MessagesGrd.RowCount := k + 1; 
    MessagesGrd.Cells[0, k] := Format('%d.%d.%d', [b[3], b[4], b[5]]); 
    MessagesGrd.Cells[1, k] := Format('%d.%d.%d:%d', [b[0], b[1], b[2], b[6]]); 
    MessagesGrd.Cells[2, k] := s; 
    MessagesGrd.Cells[3, k] := TimeToStr(Now); 
    MessagesGrd.Row := k; 
    end; 
until (z = 0); 
Application.ProcessMessages; 
end; 

そして、これがTIdTCPServerのために私のコードです:

procedure TMonitorFrm.IdTCPSerExecute(AContext: TIdContext); 
var 
    r: TIdBytes; 
    k: Byte; 
    s: String; 
begin 
AContext.Connection.IOHandler.ReadTimeout := TCPTimeOut; 
AContext.Connection.IOHandler.ReadBytes(r, -1, False); 
if (Length(r) > 6) then 
    begin 
    for k := 0 to High(r) do 
    begin 
    s := s + IntToHex(r[k], 2); 
    if (k in [2, 5, 6]) then s := s + ' '; 
    end; 
    FullLst.Items.Add(s); 
    FullMessageEdt.Text := s; 
    if (Length(s) > 17) then Delete(s, 1, 17) else s := ''; 
    k := MessagesGrd.RowCount; 
    MessagesGrd.RowCount := k + 1; 
    MessagesGrd.Cells[0, k] := Format('%d.%d.%d', [b[3], b[4], b[5]]); 
    MessagesGrd.Cells[1, k] := Format('%d.%d.%d:%d', [b[0], b[1], b[2], b[6]]); 
    MessagesGrd.Cells[2, k] := s; 
    MessagesGrd.Cells[3, k] := TimeToStr(Now); 
    MessagesGrd.Row := k; 
    end; 
Finalize(r); 
Application.ProcessMessages; 
end; 
+1

1 http://stackoverflow.com/questions/6353903/possible-causes-of-eoserror-([GUIコンポーネントを変更し、マルチスレッドアプリケーションでEOSError約1400本SOエントリー]を見てください1400-invalid-window-handle)、UIの変更を同期しようとします。それ以外の場合(少なくともTTCPServer /の場合)は、embarcaderoの例によく似ています。 2. TTCPServerコードの場合、ReceiveBufはソケットエラー(またはそのように設定されている場合は例外)で負の値を返します。一般に、ループ/終了条件にオープン範囲を残さないでください。 – makadev

答えて

2

両方のコード例での問題は、彼らがコンテキスト外からUIコントロールを操作しているということですメインのUIスレッドのどちらのコードもワーカースレッドでクライアントI/Oを実行しているため、がメインのUIスレッドと同期する必要があります。

procedure TMonitorFrm.TcpSerAccept(Sender: TObject; 
    ClientSocket: TCustomIpClient); 
var 
    b: array [0..300] of Byte; 
    z, k: Byte; 
    s: String; 
begin 
    repeat 
    z := ClientSocket.ReceiveBuf(b, SizeOf(b), 0); 
    s := ''; 
    if (z > 6) then 
    begin 
     for k := 0 to z - 1 do 
     begin 
     s := s + IntToHex(b[k], 2); 
     if (k in [2, 5, 6]) then s := s + ' '; 
     end; 
     TThread.Synchronize(nil, 
     procedure 
     begin 
      FullLst.Items.Add(s); 
      FullMessageEdt.Text := s; 
      k := MessagesGrd.RowCount; 
      MessagesGrd.RowCount := k + 1; 
      MessagesGrd.Cells[0, k] := Format('%d.%d.%d', [b[3], b[4], b[5]]); 
      MessagesGrd.Cells[1, k] := Format('%d.%d.%d:%d', [b[0], b[1], b[2], b[6]]); 
      MessagesGrd.Cells[2, k] := Copy(s, 18, MaxInt); 
      MessagesGrd.Cells[3, k] := TimeToStr(Now); 
      MessagesGrd.Row := k; 
     end 
    ); 
    end; 
    until (z = 0); 
end; 

procedure TMonitorFrm.IdTCPSerConnect(AContext: TIdContext); 
begin 
    AContext.Connection.IOHandler.ReadTimeout := TCPTimeOut; 
end; 

procedure TMonitorFrm.IdTCPSerExecute(AContext: TIdContext); 
var 
    r: TIdBytes; 
    k: Byte; 
    s: String; 
begin 
    AContext.Connection.IOHandler.ReadBytes(r, -1, False); 
    if (Length(r) > 6) then 
    begin 
    for k := 0 to High(r) do 
    begin 
     s := s + IntToHex(r[k], 2); 
     if (k in [2, 5, 6]) then s := s + ' '; 
    end; 
    TThread.Synchronize(nil, 
     procedure 
     begin 
     FullLst.Items.Add(s); 
     FullMessageEdt.Text := s; 
     k := MessagesGrd.RowCount; 
     MessagesGrd.RowCount := k + 1; 
     MessagesGrd.Cells[0, k] := Format('%d.%d.%d', [b[3], b[4], b[5]]); 
     MessagesGrd.Cells[1, k] := Format('%d.%d.%d:%d', [b[0], b[1], b[2], b[6]]); 
     MessagesGrd.Cells[2, k] := Copy(s, 18, MaxInt); 
     MessagesGrd.Cells[3, k] := TimeToStr(Now); 
     MessagesGrd.Row := k; 
     end 
    ); 
    end; 
end; 

しかし、そうは言って、メインに同期して、メインUIスレッドからいずれかのサーバーを無効にしないように注意してください:それを行う1つの方法はTThread.Synchronize()方法、例えばでありますUIスレッド。それは保証されたデッドロックです。

  1. サーバーを無効にする前に、同期要求が実行されていないことを確認してください。

  2. 同期アップデートの代わりに非同期UIアップデートを使用します。 TThread.Queue()TIdNotifyなどを使用するか、データをスレッドセーフ変数に格納し、UIタイマーを使用して定期的にUIを更新します。この方法では、I/Oスレッドはブロックされませんが、メインのUIスレッドがサーバーを非アクティブにしています。

  3. 他のスレッドを使用してサーバーを非アクティブにすると、非アクティブ化中はメインUIスレッドが同期要求を処理し続けることができます。

+1

ありがとう、私はPostMessageとGlobalAllocを使ってデータをUIウィンドウに送りました。 – Vahid

+0

'Application.Handle'や' AllocateHWnd() 'など、永続的なHWNDを使用していることを確認してください。 TFormのようなUIコントロールの 'Handle'プロパティを使わないでください。スレッドの存続期間中は永続的になることは保証されていません。 –

+0

よろしくお願いします。 – Vahid

関連する問題