トップ回答者
RemoveHandlerでのイベントとイベント ハンドラの関連付けの解除ができていないようです。

質問
-
最近VBを始めたばかりの全くの素人ですがどうかよろしくお願いします。
VBを使ってマイコンとのシリアル通信をしようと考えています。マイコンから受け取った値を逐一ラベルに表示しようとしたのですが、
RemoveHandlerがうまくいっていないようです。
コード:
Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
If SerialPort1.IsOpen = False Then
SerialPort1.Open()
End IfIf Button1.Text = "開始" Then
AddHandler SerialPort1.DataReceived, AddressOf datareceived_serial
Button1.Text = "停止"Else
RemoveHandler SerialPort1.DataReceived, AddressOf datareceived_serial
Button1.Text = "開始"End If
End Sub
Delegate Sub Labell()
Public Sub datareceived_serial()
Invoke(New Labell(AddressOf labelout))
End Sub
Sub labelout()
Label1.Text = SerialPort1.ReadLine
End Sub
End Classシリアル通信の受信はうまくいくのですが、通信を終了しようとボタンをクリックするとフリーズしてしまいます。
「 RemoveHandler SerialPort1.DataReceived, AddressOf datareceived_serial」の「AddressOf datareceived_serial」に緑色の波線が引かれ次のような警告がエラー一覧に現れます。
「AddressOf' へのメソッド引数には、イベントのデリゲート型への厳密でない変換が必要です。したがって、'AddressOf' 式はこのコンテキストでは無効です。'AddressOf' 式を変数に割り当て、その変数を使用してハンドラーとしてメソッドを追加および削除してください。」
一体どうすればよいのでしょうか?
- 編集済み Mizuhox 2012年7月11日 16:42
回答
-
そのConnectページには設計による(By Design)と書かれているので、AddHandlerできてもRemoveHandlerできないのは正しいのだと思います。厳密でないデリゲート変換にもAddHandlerについてしか言及されていません。RemoveHandlerについて言及されていないのは不親切ですが!
内部的な事情を想像すると、SerialPort1.DataReceivedイベントが必要としている関数(の引数・戻り値)とdatareceived_serialとでは一致していません。そのため、AddHandler / AddressOfは引数・戻り値を一致させるためにラッパー関数を自動的に生成します。
AddHandlerはそれで問題ないのですが、RemoveHandlerでも新たなラッパーを生成し、それをSerialPort1.DataReceivedイベントから取り除こうとします。取り除くことには成功しますが、それはAddHandler時に生成された関数とは別なので、そちらは残ったまま。
結果として、イベントが発生するたびにdatareceived_serialが呼ばれ続ける、という構造になっているかと。回避策についてはtrapemiyaさんが書かれている通り、ラッパー関数を生成しなくても済むように正しい引数・戻り値とすることです。
すべての返信
-
お使いのVisual Studioのバージョン、およびお使いの.NET Frameworkのバージョンは何でしょうか?
今回の現象ですが、バグかもしれませんね。Relaxed Delegate Conversion and RemoveHandler
http://connect.microsoft.com/VisualStudio/feedback/details/341021/relaxed-delegate-conversion-and-removehandler上のページの回避策にも書いてありますが、厳密でないデリゲートを使わず、引数をきちん指定するとうまく動くのではないかと思います。以下のようになるのかな?
Private Sub dataReceived_serial(ByVal sender As Object, ByVal e As SerialDataReceivedEventArgs)
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
-
そのConnectページには設計による(By Design)と書かれているので、AddHandlerできてもRemoveHandlerできないのは正しいのだと思います。厳密でないデリゲート変換にもAddHandlerについてしか言及されていません。RemoveHandlerについて言及されていないのは不親切ですが!
内部的な事情を想像すると、SerialPort1.DataReceivedイベントが必要としている関数(の引数・戻り値)とdatareceived_serialとでは一致していません。そのため、AddHandler / AddressOfは引数・戻り値を一致させるためにラッパー関数を自動的に生成します。
AddHandlerはそれで問題ないのですが、RemoveHandlerでも新たなラッパーを生成し、それをSerialPort1.DataReceivedイベントから取り除こうとします。取り除くことには成功しますが、それはAddHandler時に生成された関数とは別なので、そちらは残ったまま。
結果として、イベントが発生するたびにdatareceived_serialが呼ばれ続ける、という構造になっているかと。回避策についてはtrapemiyaさんが書かれている通り、ラッパー関数を生成しなくても済むように正しい引数・戻り値とすることです。
-
datareceived_serialはどこに書かれていますか? Imports System.IO.Portsが無いだけかもしれません。
(参考)
SerialDataReceivedEventArgs クラス
http://msdn.microsoft.com/ja-jp/library/system.io.ports.serialdatareceivedeventargs(v=vs.100).aspx★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
- 回答の候補に設定 山本春海 2012年8月2日 8:52
-
以下のようになっています
################################################
Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
If SerialPort1.IsOpen = False Then
SerialPort1.Open()
End If
If Button1.Text = "開始" Then
AddHandler SerialPort1.DataReceived, AddressOf datareceived_serial
Button1.Text = "停止"
Else
RemoveHandler SerialPort1.DataReceived, AddressOf datareceived_serial
Button1.Text = "開始"
End If
End Sub
Delegate Sub Labell()
Private Sub datareceived_serial(ByVal sender As Object, ByVal e As SerialDataReceivedEventArgs)
Invoke(New Labell(AddressOf labelout))
End Sub
Sub labelout()
Label1.Text = SerialPort1.ReadLine
End Sub
End Class################################################
「Imports System.IO.Ports」というのは「Public Class Form1」の前に付け加える、ということでよいのでしょうか?
- 編集済み Mizuhox 2012年7月12日 9:35
-
Imports System.IO.Ports
Imports System.IO.Ports.SerialDataReceivedEventArgsの二つを記述し、エラーは消えたのですが、まだフリーズしてしまいます。
ただ、フリーズしたプログラムをタスクマネージャで強制終了したあと次のようなエラーが出てきました。
RemoveHandlerが原因だと思っていたのですが、この行をコメントアウトしてみたところフリーズはしなくなりました。
これは一体どういうことなのでしょうか?また、当初の質問の内容と違ってきてしまっているようなのですが、新しくスレッドを立てるべきでしょうか?
- 編集済み Mizuhox 2012年7月13日 4:51