1

最近シリアル通信を行っているので、読み取りや書き込みなどを行うすべてのWindowsAPI関数へのシンプルなインターフェイスであるクラスを用意しました。このクラス内のすべてのI/O操作は非同期で処理されます。

質問に進む前に、シリアルポートからデータを読み書きする方法を紹介します(書き込み機能はまったく同じで、両方を表示しても意味がないため、これは読み取り機能のみです)。 。

function TSerialPort.Read(var pBuffer; const lBufferSize: Cardinal): Cardinal;
var
  lOverlapped: OVERLAPPED;
  lLastError: Cardinal;
  lEvent: TEvent;
begin
  lEvent := TEvent.Create(nil, True, False, '');
  try
    FillChar(lOverlapped, SizeOf(lOverlapped), 0);
    lOverlapped.hEvent := lEvent.Handle;

    if not ReadFile(FSerialPortHandle, pBuffer, lBufferSize, Result, @lOverlapped) then
    begin
      lLastError := GetLastError;
      if (lLastError <> ERROR_IO_PENDING) and (lLastError <> ERROR_SUCCESS) then
        raise Exception.Create(SysErrorMessage(lLastError));

      case lEvent.WaitFor(INFINITE) of
        wrSignaled:
          if not GetOverlappedResult(FSerialPortHandle, lOverlapped, Result, False) then
            raise Exception.Create(SysErrorMessage(GetLastError));

        wrError:
          begin
            lLastError := lEvent.LastError;
            //this is a call to Windows.CancelIo(FSerialPortHandle);
            if Self.CancelIO() then
              lEvent.WaitFor(INFINITE);
            raise Exception.Create(SysErrorMessage(lLastError));
          end;
      end;
    end;
  finally
    FreeAndNil(lEvent);
  end;
end;

この関数が読み取り操作の終了を待機しているときに、重複する操作のためにシリアルポートを開く理由を尋ねる前に、ここで説明します。この方法でシリアルポートを開く場合にのみ、WaitCommEvent()メソッドがイベントを待機する時間を指定できます。重複しない操作のためにポートを開いた場合、WaitCommEvent()は、シリアルポートにイベントが表示されるまでブロックします。これは常に発生するとは限らず、呼び出し元のスレッドが永久にブロックされます。

それでも、上記のRead()関数に集中しましょう。

1)まず、イベントが設定されるのを時間制限なしで待ちます。そのため、現在のスレッドが何らかの理由で永久にブロックされる可能性はありますか?読み取り操作を非同期で実行するスレッドによってイベントが遅かれ早かれ設定されることを100%確信できるかどうかはわかりません。シリアルポートの読み取りタイムアウトがすべてゼロに設定されている場合、指定されたバイト数が読み取られるまで読み取り操作は終了しないことを知っていますが、これは私が知っている動作です。私の質問は、イベントが設定されず、WaitFor()メソッドが永久に待機する原因となる予期しない状況に関するものです-それは起こりそうですか?

2)WaitFor()は、待機操作中にエラーが発生したことを通知するwrErrorを返す場合があります(ただし、これは重複読み取り操作とはまったく関係ありませんよね?)。したがって、読み取り操作が終了するのを待つ必要はないと思います。イベントハンドルが使用できなくなる可能性があるからです。そこで、CancelIO()メソッドを呼び出して読み取り操作をキャンセルし、キャンセルされた読み取りを非同期的に実行するスレッドによってイベントが設定されるのを待ってから、例外を発生させます。Read()メソッドをすぐに(I / Oをキャンセルせずに)終了すると、そのスレッドがそのデータ(オーバーラップしたレコードデータ)をローカル変数に書き込むため、そのスレッドによって読み取りがキャンセルされるのを待ちます。その時はもう有効ではありませんよね?一方で、

上記の内容が正しいかどうか教えていただければ幸いです。コメントをお願いします。

事前にどうもありがとうございました。

4

1 に答える 1

1

不思議なことに、既存のシリアルコンポーネントを使用してみませんか?

GPSメッセージの受信にTurboPowerAsyncを使用していますが、他にも無料で利用できるものがたくさんあります:http ://www.efg2.com/Lab/Library/Delphi/IO/PortIO.htm

それらのほとんどは、はるかに高いレベルでシリアル通信を行うことを可能にし、すべての低レベルのIOとスレッドのものを抽象化します。

そうすればonreceive、受信するハンドラーを作成し、呼び出しsend()て送信するだけで済みます。

于 2009-08-22T15:42:35.677 に答える