Search code examples
c#.netasynchronouspingsendasync

Ping.SendAsync does not hit the call back PingCompletedEventHandler


I'm having some trouble with SendAsync method when passing an invalid IP - 0.0.0.51

Problem is, the Call back method (pingSender_PingCompleted) does not even get invoked. I do not see any errors what so ever.

IPAddress.TypeParse finds this IP as a "valid" IP.

Here is my code; please let me know what I'm not seeing here.

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Program c = new Program();

            try
            {
                c.PingStore("0.0.0.51");
                Console.WriteLine("Pinged without exceptions");
            }
            catch (Exception ex) 
            {
                Console.WriteLine(ex.Message);
            }
        }

        private void PingStore(string ipAddress)
        {
            Ping pingSender = new Ping();

            pingSender.PingCompleted += new PingCompletedEventHandler(pingSender_PingCompleted);

            pingSender.SendAsync(ipAddress, null);
        }

        private void pingSender_PingCompleted(object sender, PingCompletedEventArgs e)
        {
            Console.WriteLine("PingCompleted invoked. continue to be happy");
        }
    }
}

Please note that I can't:

  1. Have any kind of control over what comes through ipAddress

  2. Complicate my code by using Regex


Solution

  • I've tested your two methods in a simple console application, and I'm getting PingException for 0.0.0.51 just as itsme86 commented.

    This is my guess on what is happening: your comment "I'm expecting this method to be called 100s of times simultaneously." plus pingSender_PingCompleted not being invoked implicates that you're invoking the PingStore method on a worker thread (e.g. using ThreadPool). This would be the reason why you are unable to catch the PingException on your main thread while pingSender_PingCompleted is never invoked due to the exception.

    UPDATE

    In your comment you wrote "FYI i'm on .Net 4.0.".

    Are you 100% sure, your app targets v4.0, not v3.5 or earlier?

    I was able to reproduce your problem using your exact code, but when targeting .NET Framework v3.5 (for v4.0 exception is thrown using your code).

    I did some digging in System.dll for both Framework versions and this is what I found:

    • Both Framework versions inside the Ping class in method private PingReply InternalSend(...) use native method num = (int)UnsafeNetInfoNativeMethods.IcmpSendEcho2.
    • In case of error num is set to 0, and I found out that each Framework version handles this situation differently.

    Framework v3.5/2.0:

    if (num == 0)
    {
        num = Marshal.GetLastWin32Error();
        if (num != 0)
        {
            this.FreeUnmanagedStructures();
            return new PingReply((IPStatus)num);
        }
    }
    

    Framewrk v4.0/4.5:

    if (num == 0)
    {
        num = Marshal.GetLastWin32Error();
        if (async && (long)num == 997L)
        {
            return null;
        }
        this.FreeUnmanagedStructures();
        this.UnregisterWaitHandle();
        if (async || num < 11002 || num > 11045)
        {
            /* NOTE! This throws the exception for 0.0.0.51 address that you're not getting */
            throw new Win32Exception(num);
        }
        return new PingReply((IPStatus)num);
    }
    

    As you can see in the code snippets above, in .NET v4.0 you should be able to catch the exception, while in .NET v3.5 all native WIN32 errors are handled silently.