2

シリアルポートを非同期的に処理するクラスを作成しました。モデムとの通信に使用します。理由はわかりませんが、アプリケーションを閉じるとブルースクリーンが表示され、コンピューターが再起動することがあります。コードを段階的に記録しましたが、BSODが表示され、コンピューターを再起動すると、データを記録していたファイルに空白しか含まれていませんでした。したがって、BSODの理由が何であるかはわかりません。

コードを注意深く調べたところ、問題の考えられる理由がいくつか見つかりました(割り当てられていないメモリにアクセスし、AV例外を引き起こす可能性のあるすべての理由を探していました)。

非同期操作のアイデアを再考したとき、いくつかのことが頭に浮かびました。これらが正しいかどうかを確認してください。

1)WaitCommEvent()は、オーバーラップした構造体へのポインターを受け取ります。したがって、関数内でWaitCommEvent()を呼び出してから関数を終了すると、オーバーラップした構造体をローカル変数にすることはできません。イベントマスク変数とイベントハンドルもそうですね。

2)ReadFile()およびWriteFile()も、変数への参照またはポインターを取ります。したがって、これらの変数はすべて、重複する読み取りまたは書き込み操作が終了するまでアクセス可能である必要があります。

3)WaitCommEvent()を1回だけ呼び出し、その結果をループでチェックし、その間に他のことを行います。非同期操作を終了する方法がわからないため(可能ですか?)、シリアルポートへのハンドルを保持しているクラスを破棄するときは、最初にハンドルを閉じてから、使用されたオーバーラップ構造でイベントを待ちますWaitCommEvent()関数を呼び出すとき。これは、commイベントを非同期的に待機するスレッドが、破棄されたクラスのフィールドにアクセスしないようにするためです。それは良い考えですか、それとも愚かですか?

try
  CloseHandle(FSerialPortHandle);
  if Assigned(FWaitCommEvent) then
    FWaitCommEvent.WaitFor(INFINITE);      
finally
  FSerialPortHandle := INVALID_HANDLE_VALUE;
  FreeAndNil(FWaitCommEvent);
end;

これらすべてに気付く前に、ポイント1と2で言及された変数のほとんどは、上記の3つのメソッドを呼び出した関数のローカル変数でした。それがBSODの理由でしょうか、それともコード内の他の間違いを探す必要がありますか?

コードを修正すると、BSODが発生しなくなりましたが、偶然かもしれません。あなたはどのように思いますか?

任意のアイデアをいただければ幸いです。前もって感謝します。


CancelIo()関数のドキュメントを読みましたが、このメソッドは、呼び出し元のスレッドによって発行されたすべてのI/O操作をキャンセルすると記載されています。WaitCommEvent()がCancelIo()を呼び出すスレッドとは異なるスレッドによって発行されたことがわかっている場合、CancelIo()を呼び出した後にFWaitCommEventを待つことは問題ありませんか?

  if Assigned(FWaitCommEvent) and CancelIo(FSerialPortHandle) then
  begin
    FWaitCommEvent.WaitFor(INFINITE);
    FreeAndNil(FWaitCommEvent);
  end;

このような場合に何が起こるかを確認したところ、このコードを呼び出すスレッドは、WaitCommEvent()を発行しなくてもデッドロックしませんでした。私はWindows7でテストしました(重要な場合)。コードをそのままにしておいてもいいですか、それとも危険ですか?たぶん私はドキュメントを誤解しました、そしてこれが私の質問の理由です。たくさんの質問をされたことをお詫びしますが、それについては本当に確信する必要があります。

ありがとう。

4

2 に答える 2

5

標準ユーザーとして実行されているアプリケーションは、バグチェック(別名BSOD)を引き起こしてはなりません。(そして、管理者として実行されているアプリケーションは、そうするために邪魔にならないようにする必要があります。)ドライバーのバグに遭遇したか、ハードウェアが不良です。

デフォルトでは、Windowsは%SystemRoot%\minidump、バグチェックが発生するたびにミニダンプを保存するように構成されています。WinDbgにミニダンプファイルをロードし、Microsoftパブリックシンボルストアを使用するようにWinDbgを構成し、WinDbgでコマンドを実行することにより、クラッシュに関する詳細情報を確認できる場合があります!analyze -v。少なくとも、これにより、おそらく障害のあるドライバーを特定する必要があります(ただし、モデムドライバーだと思います)。

于 2009-07-12T21:34:38.977 に答える
4
  1. TOverlappedはい、オーバーラップした操作の間、構造を利用できるようにしておく必要があります。GetOverlappedResultある時点で呼び出しGetOverlappedResult、オーバーラップ操作の開始時に使用された構造体へのポインターを受け取る必要があると言います。必要に応じて、イベントマスクとハンドルをローカル変数に格納できます。TOverlappedとにかく、構造内にそれらのコピーがあります。

  2. ReadFileはい、使用するバッファWriteFileは有効なままである必要があります。内部で使用するために独自のローカルコピーを作成することはありません。のドキュメントにReadFileもそう書かれています:

    このバッファは、読み取り操作の間有効である必要があります。呼び出し元は、読み取り操作が完了するまでこのバッファーを使用してはなりません。

    そのルールに従わなかった場合は、予約されていないスタックスペースを読み取っていた可能性があり、これにより、あらゆる種類の予期しない動作が簡単に発生する可能性があります。

  3. 重複したI/O操作をキャンセルするには、を使用しますCancelIoTOverlapped関連する操作が終了したことを確認するまで、レコードのメモリを解放しないことが重要です。同様に、読み取りまたは書き込みを行っているバッファーについても同様です。CancelIoは操作をすぐにキャンセルしないため、呼び出した後でもバッファが使用されている可能性があります。

于 2009-07-12T21:12:11.313 に答える