トップ回答者
SERIALコントロールのDataReceivedイベントについて

質問
-
はじめて投稿します。
シリアル通信プログラムを作成しているのですが、コマンドを送信後、受信データをすぐ処理したいのですが、うまくいきません。
送信後、強制的にApplication.DoEvents()でイベントを発生させると意図した処理ができます。
なぜ、Application.DoEvents() がないとDataReceivedの処理をしないのでしょうか?
Delegate Sub AddMessageDelegate(ByVal sStr As String)
Dim sREVDATA As StringPrivate Sub btnConnect_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnConnect.Click
_Serial.Open()
_Serial.Write("#AT" & vbCrLf)System.Threading.Thread.Sleep(200)
_Serial.Write("#RCAP" & vbCrLf)
System.Threading.Thread.Sleep(200)
’Application.DoEvents()’ここでsREVDATAを処理
_Serial.Write("#RPGSB" & vbCrLf)
System.Threading.Thread.Sleep(200)
’Application.DoEvents()’ここでsREVDATAを処理
End SubPrivate Sub _Serial_DataReceived(ByVal sender As System.Object, _
ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) _
Handles _Serial.DataReceived
Dim addmsg As New AddMessageDelegate(AddressOf AddMessage)Try
sREVDATA = _Serial.ReadLine
Catch ex As Exception
sREVDATA = ex.Message
End TrytxtMessage.Invoke(addmsg, sREVDATA)
End Sub
Private Sub AddMessage(ByVal sStr As String)
txtMessage.Text = txtMessage.Text + sStr + vbCrLfEnd Sub
すべての返信
-
同じスレッドで動作しているからです。
つまり、イベントは発生していますがキューに溜まっています。
ボタンのイベントハンドラの途中で溜まったメッセージはハンドラを抜けた後に処理されることになります。
DoEventsはその仕様を無視して溜まっているメッセージをその時点で処理させるステートメントです。
DoEventsはあなたが期待しているイベントだけでなく溜まっているものすべてが実行されます。
したがって、結果論ではなく十分にその副作用を理解して使用する必要があります。
#普通なら同期で実行される処理が同時並行で実行されるとか。
#例で言うとクリックを2回してハンドラが並行して実行されるとか、また前提の共有変数値が書き変わり条件が崩れるとか。
#DoEventsのあとに変数を書き換える処理があり、他のイベントハンドラで書き換わったであろう値を期待した処理があっても
#書き換えのステップより先にイベントハンドラ内の処理が実行されることだってあるということ。
というわけで、期待した動きになると書かれていますが、それは「たまたま」です。
たまたまというのは、イベントがWriteの結果を受けて発生するという仕様のためと思われます。
今回の場合、後続の処理がイベントなのでどうあがいても非同期です。
オブジェクトの仕様を変更できないので、Aの後のBという状態を自身で作ることになるでしょう。
つまり、きっかけのコマンドを記憶してそれに対する後処理が完了したかどうかを監視するすることになると思います。
というか、非同期とわかっていて同期実行したい理由は何でしょうか?
イベントは発生理由とタイミングが明確なだけで、時間軸の概念は無くまた時間軸に依存性を持たせてはいけません。
#もしそうなら関数の戻り値になっているはず。