none
非同期なイベントを同期させる? RRS feed

  • 質問

  • 非同期なイベントのため、困っています。
    イベントを同期させる方法や終了を待つ方法はありますか?
    ご指導のほど、よろしくお願いします。

    開発環境は VisualStudio2005 vb.net です。

    以下のようなプログラムでクラスモジュールを呼び出しており、そのクラスモジュール内でイベントを発生させています。
    正常にイベントが発生して終了するのですが、イベントが終了する前に戻り値がFalseで処理が終了してしまいます。
     具体的な状況:クリックすると同時にメッセージボックスがFalseで表示され、その後にイベントが発生している(イベントは正常終了)


    Public Class frmMainMenu
        Private Sub btnComm_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Comm.Click
            Dim clsAAA As clsABC
            Dim result As Boolean = clsAAA.AAARead("connect.txt")
            MessageBox.Show(result.ToString)
        End Sub
    End Class

    Public Class clsABC

        Public WithEvents comAAA As Comm    ' イベントの発生を定義
        Public RtnValue As Boolean          ' 戻り値

        Public Function AAARead(ByVal x As String) As Boolean

            RtnValue = False

            comAAA = New Comm
            comAAA.Connect = x

            comAAA.Excute = True ← これを実行することで以下のイベントが発生する

            AAARead = RtnValue

        End Function

        Private Sub AAARead_Start(ByVal eventSender As Object, ByVal eventArgs As AAALib.CommEventArgs) Handles AAARead.Start

            ' エラーが返ってきたら、終了する
            If eventArgs.result <> 0 Then
                RtnValue = False
                Exit Sub
            End If

        End Sub

        Private Sub AAARead_End(ByVal eventSender As Object) Handles AAARead.End

            ' 正常終了の値を返す
            RtnValue = True

        End Sub

    End Class

    2009年11月18日 10:34

すべての返信

  • 不可能ではないですが、とても勧められないので代案を。
    Public Class Abc
        Public Event ReadCompleted(ByVal succeeded As Boolean)
        Public Sub StartRead(ByVal name As String)
            Dim com As New CommClient()
            AddHandler com.Start, AddressOf Me.com_Start
            AddHandler com.End, AddressOf Me.com_End
            com.Connect = name
            com.Execute = True
        End Sub
        Private Sub com_Start(ByVal sender as Object, ByVal e as CommEventArgs)
            If e.result <> 0 Then
                RaiseEvent ReadCompleted(False)
            End If
        End Sub
        Private Sub com_End(ByVal sender As Object)
            RaiseEvent ReadCompleted(True)
        End Sub
    End Class
    
    ' 利用側
    Sub Hoge_Click(...) Handles Hoge.Clck
        Dim abc As New Abc()
        AddHandler abc.ReadCompleted, AddressOf Me.ShowComResult
        abc.StartRead("connect.txt")
    End Sub
    Private Sub ShowComResult(ByVal succeeded As Boolean)
        MessageBox.Show(secceeded.ToString())
    End Sub
    
    2009年11月18日 11:06
  • どうしても待ちたいのであればWaitHandleでブロックするとできますが、Commクラスが非同期で処理されているならHongliangさんの書いているようにしたほうが素直な処理だと思います。
    Public Class clsABC
        Public WithEvents comAAA As Comm    ' イベントの発生を定義
        Public RtnValue As Boolean          ' 戻り値
    
        Private waitHandle As System.Threading.ManualResetEvent
    
        Public Function AAARead(ByVal x As String) As Boolean
    
            waitHandle = New System.Threading.ManualResetEvent(False)
    
            RtnValue = False
    
            comAAA = New CommClient()
            comAAA.Connect = x
    
            comAAA.Excute = True '← これを実行することで以下のイベントが発生する
    
            'AAARead_Endが呼ばれて、waitHandleがシグナル状態になるまで待機する
            waitHandle.WaitOne()
            'いらなくなったのでwaitHandleを処分
            waitHandle.Close()
            waitHandle = Nothing
            AAARead = RtnValue
        End Function
    
        Private Sub AAARead_Start(ByVal eventSender As Object, ByVal eventArgs As AAALib.CommEventArgs) Handles AAARead.Start
            ' エラーが返ってきたら、終了する
            If eventArgs.result <> 0 Then
                RtnValue = False
                'エラーだとAAARead_Endが呼ばれない場合は以下の処理をここに入れる
                'If (waitHandle IsNot Nothing) Then
                '    '非同期処理が終わったのでAAAReadの処理を再開させる
                '    waitHandle.Set()
                'End If
                Exit Sub
            End If
        End Sub
    
        Private Sub AAARead_End(ByVal eventSender As Object) Handles AAARead.End
    
            ' 正常終了の値を返す
            RtnValue = True
    
            If (waitHandle IsNot Nothing) Then
                '非同期処理が終わったのでAAAReadの処理を再開させる
                waitHandle.Set()
            End If
        End Sub
    End Class
    2009年11月18日 11:21
  • さっそく回答していただき、ありがとうございます。

    Hongliangさんの方法で試してみましたが、イベントが起こらなくなってしまいました。
    AddHandler com. の . を入力した後に、候補は出てきます。

    ですが、イベントが起こってくれないのです。
    Me. の後も候補が出てくるので、入力ミスは無いと思います。

    説明不足でしたが、 Dim com As New Comm の Comm は参照の追加で追加したDLLです。

    引き続き、ご指導お願いします。
    2009年11月19日 4:10
  • Comm が GC に解放されてるんでしょうかね。
    1. Abc の com をローカル変数からメンバ変数に変更
    2. Form に ArrayList をローカル変数に配置、初期化
    3. Click イベントで作成した Abc オブジェクトを上記の ArrayList に Add
    4. ShowComResult メソッドで ArrayList から Remove(sender)
    とすることで GC の誤動作は防げますが……。

    取り敢えず、ComStart メソッドや ComEnd メソッドに Debug.Print 文などを仕込んで、これらが呼び出されているかどうかを確認してみてください。
    2009年11月19日 11:40
  • 説明不足で申し訳ありません。

    イベントが起こらないのは、class 側です。
    AddHandler
    com.Start, AddressOf Me.com_Start
    AddHandler com.End, AddressOf Me.com_End

    com.Execute = True を実行しても com のイベントが起こりません。

    Public WithEvents com As comm の時は、イベントが起こっていました。

    とりあえず、Debug.Print を仕込んでみましたが、呼び出されていないようです。

    引き続き、よろしくお願いいたします。
    2009年11月20日 2:13