[RESOLVED] Threading / Delegates
I'm getting myself really confused.
The idea is to Ping all the addresses in a given Class C sub-net and analyse the responses. Doing this synchronously takes a long time so I thought I'd use the Threading capability of VB2008 to start a new thread to Ping each Address, using Delegates to start the thread and process the results.
Code:
Public Class Form1
Private Delegate Sub DoResult(ByVal pr As PingReply)
Private Delegate Sub StartPing(ByVal IPCNumber As Integer)
Private IPSubNet As String
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
IPSubNet = "192.168.1"
For IPCounter As Integer = 0 To 254
Dim myPing As StartPing = AddressOf DoPing
Dim PingParam As Integer = IPCounter
myPing.Invoke(PingParam)
Next
End Sub
Public Sub ShowResult(ByVal pr As PingReply)
Debug.Print("Address: " + pr.Address.ToString + " Status: " + pr.Status.ToString)
End Sub
Public Sub DoPing(ByVal ipn As Integer)
Dim IPToPingC As String = IPSubNet + ipn.ToString
Dim PingOp As New Ping
Dim Pingresult As PingReply = PingOp.Send(IPToPingC)
Dim ADResult As New DoResult(AddressOf ShowResult)
ADResult.Invoke(Pingresult)
End Sub
End Class
The above is failing with a Null Exception at ' Debug.Print("Address: " + pr.Address.ToString + " Status: " + pr.Status.ToString)'
Having used the Debugger to look at PingResult in DoPing it's Null, I don't understand why? At the point of failure, IPToPingC has a vaule of "192.168.1.10" and nothing is being output to the Debug Window (so what happened to "192.168.1.0" through to "192.168.1.9" is anyones guess) - and I've only just started using VB2008.
Never having used Threading I think I'm a little (lot) out of my depth, any assistance would be gratefully received.
EDIT: Now realised that I should perhaps be using DynamicInvoke for starting DoPing and using Object type as the parameter rather than Invoke but it then fails with :
A first chance exception of type 'System.MissingMemberException' occurred in Microsoft.VisualBasic.dll
A first chance exception of type 'System.Reflection.TargetInvocationException' occurred in mscorlib.dll
EDIT2: Just realised what the error message means - I have left the final period off the IPSubNet assignment. It now doesn't fail, but I'm getting wierd results
Code:
Address: 192.168.1.104 Status: DestinationHostUnreachable
Address: 192.168.1.1 Status: Success
Address: 192.168.1.104 Status: DestinationHostUnreachable
Address: 192.168.1.104 Status: DestinationHostUnreachable
Address: 192.168.1.104 Status: DestinationHostUnreachable
Address: 192.168.1.104 Status: DestinationHostUnreachable
192.168.1.1 is the gateway and 192.168.1.104 is the address of the PC running this code. Obviously there's some sort of re-synchronisation problem or the code is just not doing what I think it should.
Current stae of the code:
Code:
Imports System.Net.NetworkInformation
Imports System.Threading
Public Class Form1
Private Delegate Sub DoResult(ByVal pr As PingReply)
Private Delegate Sub StartPing(ByVal IPAdd As Integer)
Private IPSubNet As String
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
IPSubNet = "192.168.1."
For IPCounter As Integer = 0 To 254
Dim myPing As StartPing = AddressOf DoPing
Dim PingParam As Integer = IPCounter
myPing.DynamicInvoke(PingParam)
Next
End Sub
Public Sub ShowResult(ByVal pr As PingReply)
Debug.Print("Address: " + pr.Address.ToString + " Status: " + pr.Status.ToString)
End Sub
Public Sub DoPing(ByVal ipn As Integer)
Dim IPToPingC As String = IPSubNet + ipn.ToString
Dim PingOp As New Ping
Dim Pingresult As PingReply = PingOp.Send(IPToPingC)
Dim ADResult As New DoResult(AddressOf ShowResult)
ADResult.Invoke(Pingresult)
End Sub
End Class
Re: Threading / Delegates
You're making this more difficult than it needs to be. The Ping class already handles the multi-threading for you via its SendAsync method. You simply call SendAsync in a loop and then have a single method to handle the PingCompleted event for each call.
I've never used it so I don't know whether PingCompleted is raised on the UI thread or the background thread. I'd guess the background thread but you can determine that by testing. If it is the background thread then you simply need to use the ISynchronizeInvoke implementation of your form or else a SynchronizationContext.
Re: Threading / Delegates
Thanks for the response.
I tried using SendAsync but it appears that you can't issue a second SendAsync request until the previous one has completed. I'll do a littile more research.
I've also just realised that if the Host is unreachable there will be no address in the result. I'm getting better results by testing the status and only printing when it's zero (ie success). All I have to do now is to put something in to tell me when it's all finished.
Re: Threading / Delegates
Quote:
Originally Posted by
Doogle
I tried using SendAsync but it appears that you can't issue a second SendAsync request until the previous one has completed. I'll do a littile more research.
I would assume that that would be the case for a single Ping object but then you simply create multiple Ping objects.
Quote:
Originally Posted by
Doogle
I've also just realised that if the Host is unreachable there will be no address in the result. I'm getting better results by testing the status and only printing when it's zero (ie success). All I have to do now is to put something in to tell me when it's all finished.
SendAsync allows you to pass in an arbitrary object that you will get back in the event handler, which can contain any information that you may need after the fact. That's a standard part of .NET asynchronous patterns. Such parameters and properties usually have names like 'state' or 'userState'.
Re: Threading / Delegates
Well, thanks again. I've re-done it using SendAsync and as you rightly pointed out it's much simpler
Code:
Imports System.Net.NetworkInformation
Imports System.Net.Dns
Imports System.Net
Public Class Form1
Private PingCount As Integer = 0
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim SubNet As String = "192.168.1."
For IPAddressCount As Integer = 0 To 254
Dim MyPing As NetworkInformation.Ping = New NetworkInformation.Ping
AddHandler MyPing.PingCompleted, AddressOf PingComplete
Dim IPAddress As String = SubNet + IPAddressCount.ToString
MyPing.SendAsync(IPAddress, 2000)
Next
End Sub
Private Sub PingComplete(ByVal sender As Object, ByVal e As PingCompletedEventArgs)
PingCount = PingCount + 1
Dim Reply As PingReply = e.Reply
If Reply.Status = 0 Then
Dim DNSEntry As IPHostEntry = GetHostEntry(e.Reply.Address.ToString)
Debug.Print(e.Reply.Address.ToString + " " + DNSEntry.HostName)
End If
If PingCount = 254 Then MsgBox("Finished")
End Sub
End Class
EDIT: Although I still must get my head round the Threading thing.!!
Re: Threading / Delegates
One thing you should add to that code is a bit of cleanup. In the PingCompleted event handler you should use RemoveHandler to detach the event handler and then Dispose the Ping object.
Re: Threading / Delegates
I assume I do that after the whole thing has finished, e.g.
Code:
If PingCount = LastPing Then
MsgBox("Finished")
RemoveHandler MyPing.PingCompleted, AddressOf PingComplete
MyPing = Nothing
End If
(I had to define MyPing at the Form level rather than in the Button's Click event)
Re: Threading / Delegates
No, not after the whole thing has finished. You are creating one Ping object for every iteration of your loop so you have to cleanup every single one of those objects. There will be one event raised for every object so you have to cleanup in the event handler every time. As with all event handlers, the 'sender' parameter is the object that raised the event, i.e. the Ping object. That's how you access the object, not a member variable:
vb.net Code:
Dim myPing = DirectCast(sender, Ping)
RemoveHandler myPing.PingCompleted, AddressOf PingComplete
myPing.Dispose()
Re: Threading / Delegates
Ah, I'm beginning to see it clearly now ! Thanks.