Question Problem with Listbox

digitaldrew

Well-known member
Joined
Nov 10, 2012
Messages
167
Programming Experience
Beginner
Trying to write a small program that will take each website in a listbox and ping them.

Everything seams to be working fine, however it seams like the loop goes so fast the txtLog doesn't have time to update itself. I've gone in and added a thread.sleep to put a pause in there but it doesn't seam to fix the issue. The program will pause, but the txtLog still doesn't update. However, if I stick a messagebox in the code then it will give me the messagebox that I requested, and then update the text log appropriately before continuing to the next website..

Here is the start button code. Basically, they click Start and it finds out which radio button they have selected so it knows how many times to ping each address. Then it starts the loop where it should check each item..
VB.NET:
    Private Sub btnStartPing_Click(sender As System.Object, e As System.EventArgs) Handles btnStartPing.Click

        If lstIps.Items.Count <= 0 Then
            MsgBox("Please Add at Least One IP or Website!")
            Exit Sub
        ElseIf chkOnePing.Checked = True Then
            pingNumber = 1
        ElseIf chkFourPings.Checked = True Then
            pingNumber = 4
        ElseIf chkCustom.Checked = True Then
            Try
                Integer.Parse(txtCustomPing.Text)
                pingNumber = Me.txtCustomPing.Text
            Catch ex As Exception
                MessageBox.Show("Custom Value Must be a Number!")
            End Try
        Else
            MsgBox("Please Select How Many Pings to Send!")
            Exit Sub
        End If

        Do
            For i As Integer = 0 To lstIps.Items.Count - 1
                lstIps.SelectedIndex = i
                selectedPing = lstIps.SelectedItem.ToString()
                Call pingIt()
                'Thread.Sleep(2000)
            Next
            pingNumber = pingNumber - 1
        Loop Until pingNumber <= 0
        MsgBox("Pinging Completed!")

    End Sub

When it calls the pingIt sub that is where it's actually performing the ping..Here is the code for that
VB.NET:
    Public Sub pingIt()
        Dim pingSender As New Ping()
        Dim options As New PingOptions()
        Dim roundtriptime As String

        ' Use the default Ttl value which is 128,
        ' but change the fragmentation behavior.
        options.DontFragment = True

        ' Create a buffer of 32 bytes of data to be transmitted.
        Dim data As String = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        Dim buffer() As Byte = Encoding.ASCII.GetBytes(data)
        Dim timeout As Integer = 120

        'MsgBox(selectedPing)
        Dim reply As PingReply = pingSender.Send(selectedPing, timeout, buffer, options)
        lblTotalPingsSent.Text = lblTotalPingsSent.Text + 1

        If reply.Status = IPStatus.Success Then
            MsgBox("Success")
            roundtriptime = GetMs(reply.RoundtripTime)
            txtLog.AppendText("Ping Successful to: " & selectedPing & " with Roundtrip Time: " & roundtriptime & " at: " & TimeOfDay & vbCrLf)
            lblTotalPingsReceived.Text = lblTotalPingsReceived.Text + 1
            Exit Sub
        Else
            txtLog.AppendText("Ping UnSuccessful to: " & selectedPing & " at: " & Now & vbCrLf)
            lblTotalPingsLost.Text = lblTotalPingsLost.Text + 1
            Exit Sub
        End If
    End Sub

As you see, I have the MsgBox(selectedPing) commented out in there. If I uncomment that then I'll get the correct msgbox with the string and everything after that will perform just fine. However, if I uncomment the msgbox then everything still goes as it should - but the txtLog doesn't update.

Any idea why this happening? What can I do so that it actually updates appropriately?
 
Hi,

The reason for this is that the project is busy running your code and therefore the controls on the UI thread are not being redrawn while your code is bury. The way to get round this is to force a redrawing of the TextBox control using, in your case, txtLog.Refresh(). Place this method in your PingIt subroutine after your If statement and this will then perform as you are expecting.

Some other pointers for you if I may:-

1) In your If statement in your PingIt subroutine you have "Exit Sub" after each true or false statement. This is unnecessary since the if statement is the last statement in your routine an therefore the only thing that will occur after your if statement is an exit of the subroutine.

2) Next, look at this loop that you have:-

VB.NET:
For i As Integer = 0 To lstIPs.Items.Count - 1
  lstIPs.SelectedIndex = i
  selectedPing = lstIPs.SelectedItem.ToString
  Call pingIt()
  'Thread.Sleep(2000)
Next

I am guessing that you want to loop through each IP address in the list and Ping it? Even though this loops through the items in the list it does not Ping each IP address since you have used lstIPs.SelectedItem.ToString. You are Pinging the same selected address every time? Should it not be:-

VB.NET:
For i As Integer = 0 To lstIPs.Items.Count - 1
  lstIPs.SelectedIndex = i
  'selectedPing = lstIPs.SelectedItem.ToString
  selectedPing = lstIPs.Items(i).ToString
  Call pingIt()
  'Thread.Sleep(2000)
Next

This will Ping each IP address in the list.

Hope that helps.

Cheers,

Ian
 
Hey Ian,
Thanks allot for your response and your kind pointers. You were right - the txtLog.Refresh fixed the issue. Also, thanks for the details about the loop. I had forgotten to remove that exit sub in my if statements I'm not sure why those were there.

I am noticing one other thing..When I run this code
VB.NET:
        Do
            For i As Integer = 0 To lstIps.Items.Count - 1
                lstIps.SelectedIndex = i
                'selectedPing = lstIPs.SelectedItem.ToString
                'selectedPing = lstIps.Items(i).ToString
                'Call pingIt()
                Thread.Sleep(2000)
            Next
            pingNumber = pingNumber - 1
        Loop Until pingNumber <= 0
        MsgBox("Pinging Completed!")

It goes through fine and gives me the "pinging complete" msgbox at the end..

once I uncomment selectedPing = lstIps.Items(i).ToString and Call pingIt() then the loop does not actually run until the pingNumber reaches 0..instead it goes through the listbox one time and ends up throwing an exception on the list website in the listbox.

Without calling the pingIt sub and performing the ping it will go through the list just fine selecting them and counting down until the number of pings they have selected to send is reached..However, once I actually try to run it with the ping it do/loop seams to have zero impact..

Thanks again!
 
Hi,

Sorry, but you are going to have to explain that one in a bit more detail if the following does not help:-

Admittedly, that Do / While loop should be a For loop since you are running a fixed number of iterations based on the value of pingNumber, but that aside, you are running a Do / While loop which means regardless of the value of pingNumber the Do / While loop will ALWAYS run at least once. Therefore the For loop within the Do loop will always run once.

The fact that you comment or uncomment the selectedPing = lstIps.Items(i).ToString and the Call PingIt() subroutine, will have no bearing on the function of the loops and so long as you have set the value of pingNumber correctly the Do / While loop will work fine.

I suggest that you add a Breakpoint at the start of the Do / While loop and check the value of your variables at runtime to make sure everything is set correctly. Just tested your loops here with an arbitrary value of 5 and the Do / While loop executed 5 times as expected??

Hope that helps.

Cheers,

Ian
 
Thanks for your response Ian and the suggestion on the loop. As you can see in the code below I run it without calling the pingIt sub and it seams to work fine..I get the msgbox for each website and everything looks good.

VB.NET:
        Do Until pingNumber <= 0

            For i As Integer = 0 To lstIps.Items.Count - 1
                lstIps.SelectedIndex = i
                'selectedPing = lstIPs.SelectedItem.ToString
                selectedPing = lstIps.Items(i).ToString
                Thread.Sleep(2000)
                MsgBox(selectedPing)
                'Call pingIt()
            Next
            pingNumber = pingNumber - 1
        Loop
        MsgBox("Pinging Completed!")

Now, once I uncomment the pingIt it will go through the list one time and freeze at the last item, only to give me the exception as you can see in the screenshot.
screenshot.JPG
 
Hi,

OK, so this has got nothing to do with your loops not working, it is to do with the fact that you get an exception from one of the sites you are trying to ping.

The first thing to do then is to handle that exception with a Try / Catch / End Try block. You can then examine the returned error from that site using MsgBox(ex.Message) and MsgBox(ex.InnerException). This will then stop the code from breaking and allow you to do something with the site that returned the error.

Hope that helps.

Cheers,

Ian
 
You should use the Ping.SendAsync method or perform the pings in a secondary thread.
 
@Ian..thanks so much for clearing that up! Once removing the last site from the listbox it indeed worked to perfection! I added the Try / Catch / End Try in there and that fixed the issue!

@John..Thanks for your suggesstion. This was just going to lead into my next question..

If I wanted to try and move this into a background worker what would be the best way to go about it? I'm assuming it would be to have the loop run outside of the worker and only have the worker actually process the ping request (currently pingIt sub)..However I'm not sure how this would work since it's trying to pull the ping url from a string which would throw a cross-threading error.

Thanks again!!
 
Last edited:
Hi,

I think you may have missed JohnH's point. I have not used it myself, but using the Ping.SendAsync method suggested by JohnH implies that the method runs asynchronously which means it is on another thread. I would suggest that you look into that first. Start here:-

Ping.SendAsync Method (System.Net.NetworkInformation)

Hope that helps.

Cheers,

Ian
 
Hi,

I think you may have missed JohnH's point. I have not used it myself, but using the Ping.SendAsync method suggested by JohnH implies that the method runs asynchronously which means it is on another thread. I would suggest that you look into that first. Start here:-

Ping.SendAsync Method (System.Net.NetworkInformation)

Hope that helps.

Cheers,

Ian

Thanks for the link Ian and the suggestion JohnH! Going to check out that link now.
 
Hey Ian or JohnH..Maybe one of you can help me a bit with the async...

I managed to rewrite my loop a little bit and get the async ping in there..only a few issues I've noticed.
A) It doesn't actually select any items in the listbox
B) It seams to go through and only ping the last website in the listbox..The results for the pings all show up at once when it's finished completely and the only website I see listed in the log is the last one in my listbox..Basically, I have 6 websites in the listbox and when the txtLog finally updates it shows 6 pings only to the last website..I've tried changing my loop around to something like For each x as String in lstIps.Items but that didn't seam to help either..

Here's my code
VB.NET:
        Do Until pingNumber <= 0
            For x As Integer = 0 To lstIps.Items.Count - 1
                selectedPing = lstIps.Items(x).ToString
                ' Generate the request
                Dim myPing As New Net.NetworkInformation.Ping()
                ' Add the handler for this request...
                AddHandler myPing.PingCompleted, AddressOf PingRequestCompleted
                myPing.SendAsync(selectedPing, selectedPing)
                Thread.Sleep(1000)
            Next
            pingNumber = pingNumber - 1
        Loop
        MsgBox("Pinging Completed!")

and

VB.NET:
    Public Sub PingRequestCompleted(ByVal sender As Object, ByVal e As Net.NetworkInformation.PingCompletedEventArgs)
        Dim roundtriptime As String = String.Empty

        txtLog.AppendText("Ping Response from: " & selectedPing & " with Roundtrip Time: " & roundtriptime & " at: " & TimeOfDay & vbCrLf)

        txtLog.Refresh()
        lblTotalPingsLost.Refresh()
        lblTotalPingsReceived.Refresh()
        lblTotalPingsSent.Refresh()
    End Sub

and this would be my result..if chase.com was the last of 5 websites listed in my listbox..
 

Attachments

  • result.JPG
    result.JPG
    34.1 KB · Views: 27
Hey Ian, thanks for your response and the link. I looked over that page and his example was very good and definitely helped me (with threading overall, really)...But I know I'm still doing something wrong over here and can't really figure out what it is..

Now in my start button I am simply calling the pingIt sub which is where the loop and async call is. Here is my new code for the "pingIt" sub
VB.NET:
        Do Until pingNumber <= 0
            For x As Integer = 0 To lstIps.Items.Count - 1
                lstIps.SelectedIndex = x
                selectedPing = lstIps.Items(x).ToString
                Dim waiter As New AutoResetEvent(False)

                Dim pingSender As New Ping()

                AddHandler pingSender.PingCompleted, AddressOf PingCompletedCallback
                Dim data As String = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
                Dim buffer() As Byte = Encoding.ASCII.GetBytes(data)
                Dim timeout As Integer = 2000
                Dim options As New PingOptions(64, True)

                'txtLog.AppendText("Time to live: " & selectedPing & options.Ttl)
                'txtLog.AppendText("Don't fragment: " & selectedPing & options.DontFragment)
                MsgBox(selectedPing)
                pingSender.SendAsync(selectedPing, timeout, buffer, options, waiter)

                'waiter.WaitOne()
                Thread.Sleep(2000)
            Next
            pingNumber = pingNumber - 1
        Loop
        MsgBox("Pinging Completed!")

This seams to work smoothly with the msgbox in there..I can see that it's looping through the list and passing the proper URL for pinging. However, I am running into the same issue as before where the txtLog doesn't update until about the third website again. I've looked for where the txtLog.refresh needs to go and I've tried in a few different places but the log still doesn't seam to update until the third website gets pinged. If I comment out the msgbox(selectedPing) line then I am faced with the issue I was posting about earlier..It goes through the entire list and the log won't update until the end..when it will show me 3 pings going to the same last website in my listbox. I'm a little baffled as to why the msgbox shows the proper string coming in and the log even updates properly (after the first three pings), but by simply commenting out that msgbox the log turns around and shows pings going to the same website.

As per the example you sent me..I am finally realizing how threads are to suppose to handle responses (by using the completed event) and here is what I have for my PingCompletedCallback
VB.NET:
    Private Sub PingCompletedCallback(ByVal sender As Object, ByVal e As PingCompletedEventArgs)
        ' If the operation was canceled, display a message to the user.
        If e.Cancelled Then
            txtLog.AppendText("Ping canceled at: " & TimeOfDay & vbCrLf)
            MsgBox("Pinger Successfully Cancelled!")

            ' Let the main thread resume. 
            ' UserToken is the AutoResetEvent object that the main thread 
            ' is waiting for.
            CType(e.UserState, AutoResetEvent).Set()
        End If

        ' If an error occurred, display the exception to the user.
        If e.Error IsNot Nothing Then
            txtLog.AppendText("Ping failed:" & e.Error.ToString() & vbCrLf)
            txtLog.Refresh()

            ' Let the main thread resume. 
            CType(e.UserState, AutoResetEvent).Set()
        End If

        Dim reply As PingReply = e.Reply

        DisplayReply(reply)

        ' Let the main thread resume.
        CType(e.UserState, AutoResetEvent).Set()
        txtLog.Refresh()
    End Sub

VB.NET:
    Public Sub DisplayReply(ByVal reply As PingReply)
        If reply Is Nothing Then
            Return
        End If

        'txtLog.AppendText("Pinged: " & selectedPing & "with Reply status: " & reply.Status & vbCrLf)
        If reply.Status = IPStatus.Success Then
            txtLog.AppendText("Successfully Pinged: " & selectedPing & " with Roundtrip Time: " & reply.RoundtripTime & " and TTL: " & reply.Options.Ttl & vbCrLf)
            'txtLog.AppendText("RoundTrip time: " & selectedPing & reply.RoundtripTime & vbCrLf)
            'txtLog.AppendText("Time to live: " & selectedPing & reply.Options.Ttl & vbCrLf)
            'txtLog.AppendText("Don't fragment: " & selectedPing & reply.Options.DontFragment & vbCrLf)
            'txtLog.AppendText("Buffer size: " & selectedPing & reply.Buffer.Length & vbCrLf)
        End If
        txtLog.Refresh()
    End Sub

I still feel that i'm doing something wrong here and not only with the txtLog issue..Even when using the ping.SendAsync it seams like the program is unclickable while its performing the loop and if the pinger is running in its own thread then shouldn't a stop button be clickable while it's performing the pings?

Thanks again for all your help/advice!
 
digitaldrew said:
Thread.Sleep
Get rid of this (and the AutoResetEvent as well), never block the UI thread.
I've looked for where the txtLog.refresh needs to go
Remove it, you don't need it. As long as the UI thread can process events the control will display when contents change.
 
Thanks for the information JohnH..I had the thread.sleep in there as I was trying to give it a little pause in between each website in the listbox. Also, I did remove the txtlog refreshes and the AutoResetEvent.

However, I'm still noticing the same issues with the last item showing up as being pinged in the log 3 times when I don't have the msgbox in there. I'll have to keep playing with this and see if I can figure out what's causing that.
 
Back
Top