シリアルポートを非同期的に処理するクラスを作成しました。モデムとの通信に使用します。理由はわかりませんが、アプリケーションを閉じるとブルースクリーンが表示され、コンピューターが再起動することがあります。コードを段階的に記録しましたが、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でテストしました(重要な場合)。コードをそのままにしておいてもいいですか、それとも危険ですか?たぶん私はドキュメントを誤解しました、そしてこれが私の質問の理由です。たくさんの質問をされたことをお詫びしますが、それについては本当に確信する必要があります。
ありがとう。