Go Premium for a chance to win a PS4. Enter to Win

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 897
  • Last Modified:

How to get data from IAsyncResult to a form?

I wrote a tcpClient that speaks to a server over a telnet connection.

The code below sends a packet (in this case, an authentication packet to login), and then does an asynchronus read to the callback.

Public Sub SendPacket(ByRef Packet As AMIPacket)
        Dim myWriteBuffer As NetworkStream = _session.GetStream
        If myWriteBuffer.CanWrite Then
            myWriteBuffer.BeginWrite(Packet.RenderPacket, 0, Packet.RenderPacket.Length, callBack, myWriteBuffer)
        End If
    End Sub

Open in new window


The problem is: the function that executes the asychnous operation parses the return information, which I need to pass to a textbox control on the form so I can log the communications. I get the error:


Cross-thread operation not valid: Control 'txtLog' accessed from a thread other than the thread it was created on.
A first chance exception of type 'System.InvalidOperationException' occurred in System.Windows.Forms.dll


How do I get the parsed data that is returned from the server passed to the form?
0
DrDamnit
Asked:
DrDamnit
1 Solution
 
Miguel OzSoftware EngineerCommented:
You need to interact with your GUI in a thread safe way, check:
http://msdn.microsoft.com/en-us/library/ms171728(v=vs.80).aspx

and look for the SetText method implementation.
0
 
guramritCommented:
Yo've posted incomplete information about code, or different code for different problem.
By the way BeginWrite doesn't return any value, but you can complete async operation using EndWrite(IAsyncResult).
And you can prevent cross thread errors to occur using txtLog.BeginInvoke function. If you need code then post your code where you pass value to a textbox control (txtLog).
0
 
Corey ScheichDeveloperCommented:
You need to invoke a delegate on the thread the control was created on, similar to below.

regards,

Corey

Public Sub Callback(ByVal ar As IAsyncResult)
        If TextBox1.InvokeRequired Then
            'create delegate
            Dim del As New AsyncCallback(AddressOf Callback)
            'invoke delegate on propper thread 
            TextBox1.BeginInvoke(del, New Object() {ar})
            Exit Sub
        Else
            'it is safe to update textbox if InvokeRequired returns false
            TextBox1.Text = ar.CompletedSynchronously
        End If
    End Sub

Open in new window

0
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
DrDamnitAuthor Commented:
The code that is running in a separate thread (that apparently needs to be a delegate or delegated) is:

 
Public Function ProcessInboundPackets(ByVal ar As IAsyncResult) As Boolean
        Dim message(1024) As Byte
        Dim buffer(1024) As Byte
        Dim strBuffer As String
        Dim Response() As String
        Dim myNetworkStream As NetworkStream = CType(ar.AsyncState, NetworkStream)
        Dim i As Integer = 0
        Dim c As Integer = 0

        While myNetworkStream.CanRead
            ReDim buffer(1024)

            'Find the first null element in the message
            For i = 0 To message.Length
                If message(i) = 0 Then c = i
                Exit For
            Next
            'Get the inbound stream
            myNetworkStream.Read(buffer, 0, buffer.Length)

            'Copy bytes into the message until we get 13 10 13 10 or 0
            For i = 0 To buffer.Length - 1
                'Is there is enough room for a crlf crlf
                If i + 3 <= buffer.Length - 1 Then
                    'Is this the termination?
                    If buffer(i) = 13 And buffer(i + 1) = 10 And buffer(i + 2) = 13 And buffer(i + 3) = 10 Then
                        'we have reached the end. Write them to the message, and send the message for processing.
                        message(i + c) = buffer(i)
                        'Send the message for processing
                        strBuffer = "**********" & vbCrLf & System.Text.Encoding.UTF8.GetString(message).Trim & vbCrLf & "**********"
                        RaiseEvent Log(strBuffer)
                        'Check to see if we win.
                        Response = strBuffer.Split(":")
                        If Response(1) = "Authentication accepted" Then
                            Return True
                        Else
                            Return False
                        End If
                        'Reset the message buffer
                        ReDim message(1024)
                    Else
                        'This is not the end fo the message. Keep writing to the message buffer
                        message(i + c) = buffer(i)
                    End If
                End If
            Next

            'ShowPacket(buffer)
        End While

    End Function

Open in new window


This code runs in a class called clsAMI.vb. In order to pass the information back to the form, I am trying to use RaiseEvent() (see line 31), which is obviously failing.

How do I make this a delegate?

PS. The form code that fires for teh RaiseEvent() is this:

 
Private Sub AMI_Log(ByVal sMessage As String) Handles AMI.Log
        Dim strBuffer
        strBuffer = txtLog.Text
        strBuffer = strBuffer & vbCrLf & sMessage & vbCrLf
        Try
            txtLog.Text = strBuffer
        Catch ex As Exception
            Debug.Print(strBuffer)
            Debug.Print(ex.Message)
        End Try

        txtLog.SelectionStart = txtLog.Text.Length

    End Sub

Open in new window

0
 
Corey ScheichDeveloperCommented:
The attached code should do the trick.  You just have to declare a delegate with the same signature as the method you need it to delegate.

Corey
Delegate Sub AMI_LogDel (byval sMessage as string) 

Private Sub AMI_Log(ByVal sMessage As String) Handles AMI.Log
        
        If txtLog.InvokeRequired then
            'create delegate
            Dim del As New AMI_LogDel(AddressOf AMI_Log)
            'invoke delegate on propper thread 
            txtLog.BeginInvoke(del, New Object() {sMessage})
            Exit Sub
        else

        Dim strBuffer
        strBuffer = txtLog.Text
        strBuffer = strBuffer & vbCrLf & sMessage & vbCrLf
        Try
            txtLog.Text = strBuffer
        Catch ex As Exception
            Debug.Print(strBuffer)
            Debug.Print(ex.Message)
        End Try

        txtLog.SelectionStart = txtLog.Text.Length
       end if
    End Sub

Open in new window

0
 
DrDamnitAuthor Commented:
So much simlier than I was making it! Thank You!
0

Featured Post

VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

Tackle projects and never again get stuck behind a technical roadblock.
Join Now