0
votes

I'm working on this program to ping a large number of IP Address simultaneously I tried simply pinging each address one at a time but Once I started pinging 50+ hosts it got insanely long. The problem I'm having is that I am unable to stop the asynchronous thread on the cancel button click and get this error when I try and ping more than 1 host. I've spent 2 days trying to get it figured out and had no luck. The exact error is as follows:

System.InvalidOperationException: An asynchronous call is already in
progress. It must be completed or canceled before you can call this method.
at System.Net.NetworkInformation.Ping.CheckStart(Boolean async)
at System.Net.NetworkInformation.Ping.Send(IPAddress address, Int32 timeout, Byte[] buffer, PingOptions options)
at MultiPing.Form1.backgroundWorker1_DoWork(Object sender, DoWorkEventArgs e) in f:\Dev\tfsMultiPing\Multi Ping\MultiPing\MultiPing\Form1.cs:line 139
    private void pingBtn_Click(object sender, EventArgs e)
    {
        try
        {
            if (inputBox.Text == "")
            {
                MessageBox.Show("Please Enter an IP Address to Ping.",     "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            else
            {
                if (backgroundWorker1.IsBusy != true)
                {
                    backgroundWorker1.RunWorkerAsync();
                }
                else
                {
                    MessageBox.Show("Please Cancel current Ping or wait     for it to be completed");
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
        }
    }



     public void backgroundWorker1_DoWork(object sender,     System.ComponentModel.DoWorkEventArgs e)
    {
        try
        {
            this.Invoke(new MethodInvoker(delegate {     progressBar1.Enabled = true; }));
            int i;

            //Add each line in the input box to the "allLines" string     array
            string[] allLines = inputBox.Lines;
            Ping pingSender = new Ping();

            try
            {
                //Get an object that will block the main thread
                AutoResetEvent waiter = new AutoResetEvent(false);

                //When the PingCompleted even is raised,
                //The PingCompletedCallback method is called.
                pingSender.PingCompleted += new     PingCompletedEventHandler(PingCompletedCallback);

                //Create a buffer of 32 bytes of data to be transmitted.
                string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
                byte[] buffer = Encoding.ASCII.GetBytes(data);

                //Wait 2 seconds for a reply.
                int timeout = 2000;

                //Set Options for transmission:
                //The data can go through 64 gateways or routers
                //before it is destroyed, and the data packet
                //cannot be fragmented
                PingOptions options = new PingOptions(64, true);

                //Check if Cancel Button was clicked
                if (backgroundWorker1.CancellationPending == true)
                {
                    e.Cancel = true;
                    return;
                }
                else
                {
                    //Begin Loop to ping each given IP Address
                    for (i = 0; i < allLines.Length; i++)
                    {
                        //Check if Cancel Button was clicked
                        if (backgroundWorker1.CancellationPending ==     true)
                        {
                            e.Cancel = true;
                            return;
                        }
                        else
                        {
                            //Convert each line from the input box to an     IP address
                            IPAddress address =     IPAddress.Parse(allLines[i]);

                            //Send ping Asynchronously
                            //Use the waiter as the user token.
                            //When the callback complets, it can wake up     this thread.
                            pingSender.SendAsync(address, timeout,     buffer, options, waiter);
                            PingReply reply = pingSender.Send(address,     timeout, buffer, options);
                            waiter.WaitOne();
                            //If a replay is recieved Print "IP Address"     is up in the output box.
                            if (reply.Status == IPStatus.Success)
                            {
                                this.Invoke(new MethodInvoker(delegate {     outputBoxLive.AppendText(address + " is up" + Environment.NewLine); }));
                            }

                            //If no reply is recieved  then print "IP Address" is down in the output box.
                            else if (reply.Status == IPStatus.TimedOut)
                            {
                                this.Invoke(new MethodInvoker(delegate     {     outputBoxDown.AppendText(address + " is down" + Environment.NewLine); }));
                                pingSender.Dispose();
                            }

                            pingSender.Dispose();
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
        }
    }

    public void pingStuff()
    {
    }

    private static void PingCompletedCallback(object sender,     PingCompletedEventArgs e)
    {
        //If the operation was cancelled, Display a message to the user
        if (e.Cancelled)
        {
            MessageBox.Show("Ping Cancelled");

            //Let the main thread resume.
            //User token is the AutoResetEvent object that the main     thread is waiting for
            ((AutoResetEvent)e.UserState).Set();
        }

        //If an error occurred, Display the exception to the user.
        if (e.Error != null)
        {
            MessageBox.Show("Ping Failed: " + e.Error.ToString());

            //Let the main thread resume.
            ((AutoResetEvent)e.UserState).Set();
        }
        PingReply reply = e.Reply;
        DisplayReply(reply);

        //Let the main thread resume.
        ((AutoResetEvent)e.UserState).Set();
    }

    public static void DisplayReply(PingReply reply)
    {
        if (reply == null)
            return;

        Console.WriteLine("ping status: [0]", reply.Status);
        if (reply.Status == IPStatus.Success)
        {
            Console.WriteLine("Address: {0}", reply.Address.ToString());
            Console.WriteLine("RoundTrip time: {0}",     reply.RoundtripTime);
            Console.WriteLine("Time to live: {0}", reply.Options.Ttl);
            Console.WriteLine("Don't fragment: {0}",     reply.Options.DontFragment);
            Console.WriteLine("Buffer size: {0}", reply.Buffer.Length);
        }
    }




   public void button2_Click(object sender, EventArgs e)
    {
        try
        {
            backgroundWorker1.CancelAsync();
        }
        catch (Exception exc)
        {
            Console.WriteLine(exc);
        }
    }
}
}
1
Without reading the code.. you can probably pass a cancelation token to the async method. - Alkasai
Unlike forum sites, we don't use "Thanks", or "Any help appreciated", or signatures on Stack Overflow. See "Should 'Hi', 'thanks,' taglines, and salutations be removed from posts?. - John Saunders
If you call Invoke you need to call EndInvoke from the callback, this ends the call. - Mgetz
@Mgetz: you are thinking of the rule about BeginInvoke(), not Invoke(). It doesn't apply here. And note that while conventionally it's true that one should match pairs of BeginXXX() and EndXXX() methods, in the case of Control.BeginInvoke() and the compiler-generated delegate types' BeginInvoke() methods, Microsoft has for years offered the guidance that one need not call EndInvoke() in those cases (i.e. they are an exception to the rule). - Peter Duniho

1 Answers

0
votes

The immediate issue with your code (i.e. the cause of the exception) is that you are calling both Ping.Send() and Ping.SendAsync() in your loop. Once you have called Ping.SendAsync(), any subsequent call to Send() or SendAsync() is illegal until the first SendAsync() operation has completed.

It's not clear why you even have both. Given that you appear to want to make the call synchronously, the simplest thing is to simply remove the call to SendAsync() and anything related to it (i.e. the waiter object). Alternatively, since you seem to want to be able to interrupt the operation, you may prefer to remove the call to Send(), so that you can use the SendAsyncCancel() method to interrupt an outstanding operation.

Lacking a good, complete code example (this code example doesn't show the full context, including where you get the addresses to ping), it's hard to know exactly what is wrong in your scenario. But I can offer a couple of observations about the code you did post:

  1. You are calling the Ping.Dispose() method in each iteration of your loop. This means only in the first iteration of the loop should you expect to succeed. In the next iteration, you should be getting an ObjectDisposedException, terminating the work!
  2. If you want to cancel an in-progress call to Ping.SendAsync(), you should call the Ping.SendAsyncCancel() method. This will cause the current ping operation to complete with cancellation status. Of course, to do this you will need to expose the pingSender reference to the form's code somehow, so that the button2_Click() method has access to it.