File: net\System\Net\NetworkInformation\ping.cs
Project: ndp\fx\src\System.csproj (System)

namespace System.Net.NetworkInformation {
 
    using System.Net;
    using System.Net.Sockets;
    using System;
    using System.Runtime.InteropServices;
    using System.Threading;
    using System.Threading.Tasks;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.Security.Permissions;
 
    public delegate void PingCompletedEventHandler (object sender, PingCompletedEventArgs e);
 
 
    public class PingCompletedEventArgs: System.ComponentModel.AsyncCompletedEventArgs {
        PingReply reply;
 
        internal PingCompletedEventArgs (PingReply reply, Exception error, bool cancelled, object userToken):base(error,cancelled,userToken) {
            this.reply = reply;
        }
        public PingReply Reply{get {return reply;}}
    }
 
    public class Ping:Component
    {
        const int MaxUdpPacket = 0xFFFF + 256; // Marshal.SizeOf(typeof(Icmp6EchoReply)) * 2 + ip header info;  
        const int MaxBufferSize = 65500; //artificial constraint due to win32 api limitations.
        const int DefaultTimeout = 5000; //5 seconds same as ping.exe
        const int DefaultSendBufferSize = 32;  //same as ping.exe
        
        byte[] defaultSendBuffer = null;
        bool ipv6 = false;
        bool cancelled = false;
        bool disposeRequested = false;
        object lockObject = new object();
 
        //used for icmpsendecho apis
        internal ManualResetEvent pingEvent = null;
        private RegisteredWaitHandle registeredWait = null;
        SafeLocalFree requestBuffer = null;
        SafeLocalFree replyBuffer = null;
        int sendSize = 0;  //needed to determine what reply size is for ipv6 in callback
        
        SafeCloseIcmpHandle handlePingV4 = null;
        SafeCloseIcmpHandle handlePingV6 = null;
 
        //new async event support
        AsyncOperation asyncOp = null;
        SendOrPostCallback onPingCompletedDelegate;
        public event PingCompletedEventHandler PingCompleted;
 
        // For blocking in SendAsyncCancel()
        ManualResetEvent asyncFinished = null;
        bool InAsyncCall {
            get {
                if (asyncFinished == null)
                    return false;
                // Never blocks, just checks if a thread would block.
                return !asyncFinished.WaitOne(0);
            }
            set {
                if (asyncFinished == null)
                    asyncFinished = new ManualResetEvent(!value);
                else if (value)
                    asyncFinished.Reset(); // Block
                else
                    asyncFinished.Set(); // Clear
            }
        }
 
        // Thread safety
        private const int Free = 0;
        private const int InProgress = 1;
        private new const int Disposed = 2;
        private int status = Free;
 
        private void CheckStart(bool async)
        {
            if (disposeRequested)
            {
                throw new ObjectDisposedException(GetType().FullName);
            }
 
            int currentStatus = Interlocked.CompareExchange(ref status, InProgress, Free);
            if (currentStatus == InProgress)
            {
                throw new InvalidOperationException(SR.GetString(SR.net_inasync));
            }
            else if (currentStatus == Disposed)
            {
                throw new ObjectDisposedException(GetType().FullName);
            }
 
            if (async)
            {
                InAsyncCall = true;
            }
        }
 
        private void Finish(bool async)
        {
            Debug.Assert(status == InProgress, "Invalid status: " + status);
            status = Free;
            if (async)
            {
                InAsyncCall = false;
            }
            if (disposeRequested)
            {
                InternalDispose();
            }
        }
 
        protected void OnPingCompleted(PingCompletedEventArgs e) 
        {
            if (PingCompleted != null) {
                PingCompleted (this,e);
            }
        }
 
        void PingCompletedWaitCallback (object operationState) {
            OnPingCompleted((PingCompletedEventArgs)operationState);
        }
 
        public Ping () {
            onPingCompletedDelegate = new SendOrPostCallback (PingCompletedWaitCallback);
        }
 
        //cancel pending async requests, close the handles
        private void InternalDispose () {
            disposeRequested = true;
 
            if (Interlocked.CompareExchange(ref status, Disposed, Free) != Free)
            {
                // Already disposed, or Finish will call Dispose again once Free
                return;
            }
 
            if (handlePingV4 != null) {
                handlePingV4.Close ();
                handlePingV4 = null;
            }
 
            if (handlePingV6 != null) {
                handlePingV6.Close ();
                handlePingV6 = null;
            }
 
            UnregisterWaitHandle();
 
            if (pingEvent != null) {
                pingEvent.Close();
                pingEvent = null;
            }
 
            if (replyBuffer != null) {
                replyBuffer.Close();
                replyBuffer = null;
            }
 
            if (asyncFinished != null) {
                asyncFinished.Close();
                asyncFinished = null;
            }
        }
 
        private void UnregisterWaitHandle()
        {
            lock (lockObject)
            {
                if (registeredWait != null)
                {
                    registeredWait.Unregister(null); 
                    // If Unregister returns false, it is sufficient to nullify registeredWait
                    // and let its own finilizer clean up later.
                    registeredWait = null;
                }
            }
        }
 
        protected override void Dispose(Boolean disposing)
        {
            if (disposing) { // Only on explicit dispose.  Otherwise, the GC can cleanup everything else.
                InternalDispose();
            }
            base.Dispose(disposing);
        }
 
        //cancels pending async calls
        public void SendAsyncCancel() {
            lock (lockObject) {
                if (!InAsyncCall) {
                    return;
                }
 
                cancelled = true;
            }
            // Because there is no actual native cancel, 
            // we just have to block until the current operation is completed.
            asyncFinished.WaitOne();
        }
 
 
        //private callback invoked when icmpsendecho apis succeed
        private static void PingCallback (object state, bool signaled) 
        {
            Ping ping = (Ping)state;
            PingCompletedEventArgs eventArgs = null;
            bool cancelled = false;
            AsyncOperation asyncOp = null;
            SendOrPostCallback onPingCompletedDelegate = null;
 
            try
            {
                lock (ping.lockObject) {
                    cancelled = ping.cancelled;
                    asyncOp = ping.asyncOp;
                    onPingCompletedDelegate = ping.onPingCompletedDelegate;
 
                    if (!cancelled) {
                        //parse reply buffer
                        SafeLocalFree buffer = ping.replyBuffer;
                        
                        //marshals and constructs new reply
                        PingReply reply;
 
                        if (ping.ipv6) {
                            Icmp6EchoReply icmp6Reply = (Icmp6EchoReply)Marshal.PtrToStructure (buffer.DangerousGetHandle (), typeof(Icmp6EchoReply));
                            reply = new PingReply (icmp6Reply,buffer.DangerousGetHandle(),ping.sendSize);
                        }
                        else {
                            IcmpEchoReply icmpReply = (IcmpEchoReply)Marshal.PtrToStructure (buffer.DangerousGetHandle (), typeof(IcmpEchoReply));
                            reply = new PingReply (icmpReply);
                        }
                        
                        eventArgs = new PingCompletedEventArgs (reply, null, false, asyncOp.UserSuppliedState);
                    } else { //cancelled
                        eventArgs = new PingCompletedEventArgs (null, null, true, asyncOp.UserSuppliedState);
                    }
                }
            }
            // in case of failure, create a failed event arg
            catch (Exception e) {
                PingException pe = new PingException(SR.GetString(SR.net_ping), e);
                eventArgs = new PingCompletedEventArgs (null,pe, false, asyncOp.UserSuppliedState);
            }
            finally {
                ping.FreeUnmanagedStructures ();
                ping.UnregisterWaitHandle();
                ping.Finish(true);
            }
            
            asyncOp.PostOperationCompleted (onPingCompletedDelegate, eventArgs);
        }
        
        public PingReply Send (string hostNameOrAddress) {
            return Send (hostNameOrAddress, DefaultTimeout, DefaultSendBuffer, null);
        }
 
 
        public PingReply Send (string hostNameOrAddress, int timeout) {
            return Send (hostNameOrAddress, timeout, DefaultSendBuffer, null);
        }
 
 
        public PingReply Send (IPAddress address) {
            return Send (address, DefaultTimeout, DefaultSendBuffer, null);
        }
 
        public PingReply Send (IPAddress address, int timeout) {
            return Send (address, timeout, DefaultSendBuffer, null);
        }
 
        public PingReply Send (string hostNameOrAddress, int timeout, byte[] buffer) {
            return Send (hostNameOrAddress, timeout, buffer, null);
        }
 
        public PingReply Send (IPAddress address, int timeout, byte[] buffer) {
            return Send (address, timeout, buffer, null);
        }
 
        public PingReply Send (string hostNameOrAddress, int timeout, byte[] buffer, PingOptions options) {
            if (ValidationHelper.IsBlankString(hostNameOrAddress)) {
                throw new ArgumentNullException ("hostNameOrAddress");
            }
 
            IPAddress address; 
            if (!IPAddress.TryParse(hostNameOrAddress, out address)) {
                try {
                    address = Dns.GetHostAddresses(hostNameOrAddress)[0];
                }
                catch (ArgumentException) 
                {
                    throw;
                }
                catch (Exception ex) {
                    throw new PingException(SR.GetString(SR.net_ping), ex);
                }
            }
            return Send(address, timeout, buffer, options);
        }
 
 
        public PingReply Send (IPAddress address, int timeout, byte[] buffer, PingOptions options) {
            if (buffer == null) {
                throw new ArgumentNullException ("buffer");
            }
 
            if (buffer.Length > MaxBufferSize ) {
                throw new ArgumentException(SR.GetString(SR.net_invalidPingBufferSize), "buffer");
            }
 
            if (timeout < 0) {
                throw new ArgumentOutOfRangeException ("timeout");
            }
 
            if (address == null) {
                throw new ArgumentNullException ("address");
            }
 
            TestIsIpSupported(address); // Address family is installed?
 
            if (address.Equals(IPAddress.Any) || address.Equals(IPAddress.IPv6Any))
            {
                throw new ArgumentException(SR.GetString(SR.net_invalid_ip_addr), "address");
            }
            
            //
            // FxCop: need to snapshot the address here, so we're sure that it's not changed between the permission
            // and the operation, and to be sure that IPAddress.ToString() is called and not some override that
            // always returns "localhost" or something.
            //
            IPAddress addressSnapshot;
            if (address.AddressFamily == AddressFamily.InterNetwork) 
            {
                addressSnapshot = new IPAddress(address.GetAddressBytes());
            }
            else
            {
                addressSnapshot = new IPAddress(address.GetAddressBytes(), address.ScopeId);
            }
 
            (new NetworkInformationPermission(NetworkInformationAccess.Ping)).Demand();
 
            CheckStart(false);
            try {
                return InternalSend (addressSnapshot, buffer, timeout, options, false);
            }
            catch (Exception e) {
                throw new PingException(SR.GetString(SR.net_ping), e);
            }
            finally {
                Finish(false);
            }
        }
 
 
 
        [HostProtection(ExternalThreading=true)]
        public void SendAsync (string hostNameOrAddress, object userToken) {
            SendAsync (hostNameOrAddress, DefaultTimeout, DefaultSendBuffer, userToken);
        }
 
 
        [HostProtection(ExternalThreading=true)]
        public void SendAsync (string hostNameOrAddress, int timeout, object userToken) {
            SendAsync (hostNameOrAddress, timeout, DefaultSendBuffer, userToken);
        }
 
 
        [HostProtection(ExternalThreading=true)]
        public void SendAsync (IPAddress address, object userToken) {
            SendAsync (address, DefaultTimeout, DefaultSendBuffer, userToken);
        }
 
 
        [HostProtection(ExternalThreading=true)]
        public void SendAsync (IPAddress address, int timeout, object userToken) {
            SendAsync (address, timeout, DefaultSendBuffer, userToken);
        }
 
 
        [HostProtection(ExternalThreading=true)]
        public void SendAsync (string hostNameOrAddress, int timeout, byte[] buffer, object userToken) {
            SendAsync (hostNameOrAddress, timeout, buffer, null, userToken);
        }
 
 
        [HostProtection(ExternalThreading=true)]
        public void SendAsync (IPAddress address, int timeout, byte[] buffer, object userToken) {
            SendAsync (address, timeout, buffer, null, userToken);
        }
 
 
        [HostProtection(ExternalThreading=true)]
        public void SendAsync (string hostNameOrAddress, int timeout, byte[] buffer, PingOptions options, object userToken) {
            if (ValidationHelper.IsBlankString(hostNameOrAddress)) {
                throw new ArgumentNullException ("hostNameOrAddress");
            }
 
            if (buffer == null) {
                throw new ArgumentNullException ("buffer");
            }
 
            if (buffer.Length > MaxBufferSize ) {
                throw new ArgumentException(SR.GetString(SR.net_invalidPingBufferSize), "buffer");
            }
 
            if (timeout < 0) {
                throw new ArgumentOutOfRangeException ("timeout");
            }
 
            IPAddress address;
            if (IPAddress.TryParse(hostNameOrAddress, out address))
            {
                SendAsync(address, timeout, buffer, options, userToken);
                return;
            }
 
            CheckStart(true);
            try {
                cancelled = false;
                asyncOp = AsyncOperationManager.CreateOperation (userToken);
                AsyncStateObject state = new AsyncStateObject(hostNameOrAddress,buffer,timeout,options,userToken);
                ThreadPool.QueueUserWorkItem(new WaitCallback(ContinueAsyncSend), state);
            }
            catch (Exception e) {
                Finish(true);
                throw new PingException(SR.GetString(SR.net_ping), e);
            }
        }
 
 
 
        [HostProtection(ExternalThreading=true)]
        public void SendAsync (IPAddress address, int timeout, byte[] buffer, PingOptions options, object userToken) {
            if (buffer == null) {
                throw new ArgumentNullException ("buffer");
            }
 
            if (buffer.Length > MaxBufferSize ) {
                throw new ArgumentException(SR.GetString(SR.net_invalidPingBufferSize), "buffer");
            }
 
            if (timeout < 0) {
                throw new ArgumentOutOfRangeException ("timeout");
            }
 
            if (address == null) {
                throw new ArgumentNullException ("address");
            }
 
            TestIsIpSupported(address); // Address family is installed?
 
            if (address.Equals(IPAddress.Any) || address.Equals(IPAddress.IPv6Any))
            {
                throw new ArgumentException(SR.GetString(SR.net_invalid_ip_addr), "address");
            }
 
            //
            // FxCop: need to snapshot the address here, so we're sure that it's not changed between the permission
            // and the operation, and to be sure that IPAddress.ToString() is called and not some override that
            // always returns "localhost" or something.
            //
 
            IPAddress addressSnapshot;
            if (address.AddressFamily == AddressFamily.InterNetwork) 
            {
                addressSnapshot = new IPAddress(address.GetAddressBytes());
            }
            else
            {
                addressSnapshot = new IPAddress(address.GetAddressBytes(), address.ScopeId);
            }
 
            (new NetworkInformationPermission(NetworkInformationAccess.Ping)).Demand();
 
            CheckStart(true);
            try{
                cancelled = false;
                asyncOp = AsyncOperationManager.CreateOperation (userToken);
                InternalSend (addressSnapshot, buffer, timeout, options, true);
            }
            catch(Exception e){
                Finish(true);
                throw new PingException(SR.GetString(SR.net_ping), e);
            }
        }
 
 
        //************* Task-based async public methods *************************
        [HostProtection(ExternalThreading = true)]
        public Task<PingReply> SendPingAsync(IPAddress address)
        {
            return SendPingAsyncCore(tcs => SendAsync(address, tcs));
        }
 
        [HostProtection(ExternalThreading = true)]
        public Task<PingReply> SendPingAsync(string hostNameOrAddress)
        {
            return SendPingAsyncCore(tcs => SendAsync(hostNameOrAddress, tcs));
        }
 
        [HostProtection(ExternalThreading = true)]
        public Task<PingReply> SendPingAsync(IPAddress address, int timeout)
        {
            return SendPingAsyncCore(tcs => SendAsync(address, timeout, tcs));
        }
 
        [HostProtection(ExternalThreading = true)]
        public Task<PingReply> SendPingAsync(string hostNameOrAddress, int timeout)
        {
            return SendPingAsyncCore(tcs => SendAsync(hostNameOrAddress, timeout, tcs));
        }
 
        [HostProtection(ExternalThreading = true)]
        public Task<PingReply> SendPingAsync(IPAddress address, int timeout, byte[] buffer)
        {
            return SendPingAsyncCore(tcs => SendAsync(address, timeout, buffer, tcs));
        }
 
        [HostProtection(ExternalThreading = true)]
        public Task<PingReply> SendPingAsync(string hostNameOrAddress, int timeout, byte[] buffer)
        {
            return SendPingAsyncCore(tcs => SendAsync(hostNameOrAddress, timeout, buffer, tcs));
        }
 
        [HostProtection(ExternalThreading = true)]
        public Task<PingReply> SendPingAsync(IPAddress address, int timeout, byte[] buffer, PingOptions options)
        {
            return SendPingAsyncCore(tcs => SendAsync(address, timeout, buffer, options, tcs));
        }
 
        [HostProtection(ExternalThreading = true)]
        public Task<PingReply> SendPingAsync(string hostNameOrAddress, int timeout, byte[] buffer, PingOptions options)
        {
            return SendPingAsyncCore(tcs => SendAsync(hostNameOrAddress, timeout, buffer, options, tcs));
        }
 
        private Task<PingReply> SendPingAsyncCore(Action<TaskCompletionSource<PingReply>> sendAsync)
        {
            // Create a TaskCompletionSource to represent the operation
            var tcs = new TaskCompletionSource<PingReply>();
 
            // Register a handler that will transfer completion results to the TCS Task
            PingCompletedEventHandler handler = null;
            handler = (sender, e) => HandleCompletion(tcs, e, handler);
            this.PingCompleted += handler;
 
            // Start the async operation.
            try { sendAsync(tcs); }
            catch
            {
                this.PingCompleted -= handler;
                throw;
            }
 
            // Return the task to represent the asynchronous operation
            return tcs.Task;
        }
 
        private void HandleCompletion(TaskCompletionSource<PingReply> tcs, PingCompletedEventArgs e, PingCompletedEventHandler handler)
        {
            if (e.UserState == tcs)
            {
                try { this.PingCompleted -= handler; }
                finally
                {
                    if (e.Error != null) tcs.TrySetException(e.Error);
                    else if (e.Cancelled) tcs.TrySetCanceled();
                    else tcs.TrySetResult(e.Reply);
                }
            }
        }
 
 
        internal class AsyncStateObject{
            internal AsyncStateObject(string hostName, byte[] buffer, int timeout, PingOptions options, object userToken)
            {
                this.hostName = hostName;
                this.buffer = buffer;
                this.timeout = timeout;
                this.options = options;
                this.userToken = userToken;
            }
 
            internal byte[] buffer;
            internal string hostName;
            internal int timeout;
            internal PingOptions options;
            internal object userToken;
        }
 
        private void ContinueAsyncSend(object state) {
            //
            // FxCop: need to snapshot the address here, so we're sure that it's not changed between the permission
            // and the operation, and to be sure that IPAddress.ToString() is called and not some override that
            // always returns "localhost" or something.
            //
 
            Debug.Assert(asyncOp != null, "Null AsyncOp?");
 
            AsyncStateObject stateObject = (AsyncStateObject) state;
 
            try {
                IPAddress addressSnapshot = Dns.GetHostAddresses(stateObject.hostName)[0];
 
                (new NetworkInformationPermission(NetworkInformationAccess.Ping)).Demand();
                InternalSend (addressSnapshot, stateObject.buffer, stateObject.timeout, stateObject.options, true);
            }
 
            catch(Exception e){
                PingException pe = new PingException(SR.GetString(SR.net_ping), e);
                PingCompletedEventArgs eventArgs = new PingCompletedEventArgs (null, pe, false, asyncOp.UserSuppliedState);
                Finish(true);
                asyncOp.PostOperationCompleted(onPingCompletedDelegate, eventArgs);
            }
        }
 
 
 
        // internal method responsible for sending echo request on win2k and higher
 
        private PingReply InternalSend (IPAddress address, byte[] buffer, int timeout, PingOptions options, bool async) {
                        
            ipv6 =  (address.AddressFamily == AddressFamily.InterNetworkV6)?true:false;
            sendSize = buffer.Length;
 
            //get and cache correct handle
            if (!ipv6 && handlePingV4 == null) {
                handlePingV4 = UnsafeNetInfoNativeMethods.IcmpCreateFile ();
                if (handlePingV4.IsInvalid) {
                    handlePingV4 = null;
                    throw new Win32Exception(); // Gets last error
                }
            }
            else if (ipv6 && handlePingV6 == null) {
                handlePingV6 = UnsafeNetInfoNativeMethods.Icmp6CreateFile();
                if (handlePingV6.IsInvalid) {
                    handlePingV6 = null;
                    throw new Win32Exception(); // Gets last error
                }
            }
 
 
            //setup the options
            IPOptions ipOptions = new IPOptions (options);
 
            //setup the reply buffer
            if (replyBuffer == null) {
                replyBuffer = SafeLocalFree.LocalAlloc (MaxUdpPacket);
            }
 
            //queue the event
            int error;
 
            try
            {
                if (async) {
                    if (pingEvent == null)
                        pingEvent = new ManualResetEvent (false);
                    else
                        pingEvent.Reset();
                
                    registeredWait = ThreadPool.RegisterWaitForSingleObject (pingEvent, new WaitOrTimerCallback (PingCallback), this, -1, true);
                }
 
                //Copy user dfata into the native world
                SetUnmanagedStructures (buffer);
 
                if (!ipv6) {
                    if (async) {
                        error = (int)UnsafeNetInfoNativeMethods.IcmpSendEcho2 (handlePingV4, pingEvent.SafeWaitHandle, IntPtr.Zero, IntPtr.Zero, (uint)address.m_Address, requestBuffer, (ushort)buffer.Length, ref ipOptions, replyBuffer, MaxUdpPacket, (uint)timeout);
                    }
                    else{
                        error = (int)UnsafeNetInfoNativeMethods.IcmpSendEcho2 (handlePingV4, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, (uint)address.m_Address, requestBuffer, (ushort)buffer.Length, ref ipOptions, replyBuffer, MaxUdpPacket, (uint)timeout);
                    }
                }
                else {                                                   
                    IPEndPoint ep = new IPEndPoint (address, 0);
                    SocketAddress remoteAddr = ep.Serialize ();
                    byte[] sourceAddr = new byte[28];
                    if(async){
                        error = (int)UnsafeNetInfoNativeMethods.Icmp6SendEcho2 (handlePingV6, pingEvent.SafeWaitHandle, IntPtr.Zero, IntPtr.Zero, sourceAddr, remoteAddr.m_Buffer, requestBuffer, (ushort)buffer.Length, ref ipOptions, replyBuffer, MaxUdpPacket, (uint)timeout);
                    }
                    else{
                        error = (int)UnsafeNetInfoNativeMethods.Icmp6SendEcho2 (handlePingV6, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, sourceAddr, remoteAddr.m_Buffer, requestBuffer, (ushort)buffer.Length, ref ipOptions, replyBuffer, MaxUdpPacket, (uint)timeout);
                    }
                }
            }
            catch
            {
                UnregisterWaitHandle();
                throw;
            }
 
            //need this if something is bogus.
            if (error == 0) {
                error = (int)Marshal.GetLastWin32Error();
                
                // Only skip Async IO Pending error value
                if (async && error == UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING)
                    return null; // Expected async return value
                
                // Cleanup
                FreeUnmanagedStructures();
                UnregisterWaitHandle();
 
                if (async // No IPStatus async errors
                    || error < (int)IPStatus.DestinationNetworkUnreachable // Min
                    || error > (int)IPStatus.DestinationScopeMismatch) // Max // Out of IPStatus range
                    throw new Win32Exception(error);
 
                return new PingReply((IPStatus)error); // Synchronous IPStatus errors 
            }
 
            if (async) {
                return null;
            }
 
            FreeUnmanagedStructures ();
 
            //return the reply
            PingReply reply;
            if (ipv6) {
                Icmp6EchoReply icmp6Reply = (Icmp6EchoReply)Marshal.PtrToStructure(replyBuffer.DangerousGetHandle(), typeof(Icmp6EchoReply));
                reply = new PingReply(icmp6Reply, replyBuffer.DangerousGetHandle(), sendSize);
            }
            else {
                IcmpEchoReply icmpReply = (IcmpEchoReply)Marshal.PtrToStructure(replyBuffer.DangerousGetHandle(), typeof(IcmpEchoReply));
                reply = new PingReply(icmpReply);
            }
 
            // IcmpEchoReply still has an unsafe IntPtr reference into replybuffer
            // and replybuffer was being freed prematurely by the GC, causing AccessViolationExceptions.
            GC.KeepAlive(replyBuffer);
 
            return reply;
        }
        
        // Tests if the current machine supports the given ip protocol family
        private void TestIsIpSupported(IPAddress ip) {
            // Catches if IPv4 has been uninstalled on Vista+
            if (ip.AddressFamily == AddressFamily.InterNetwork && !Socket.OSSupportsIPv4)
                throw new NotSupportedException(SR.GetString(SR.net_ipv4_not_installed));
            // Catches if IPv6 is not installed on XP
            else if ((ip.AddressFamily == AddressFamily.InterNetworkV6 && !Socket.OSSupportsIPv6))
                throw new NotSupportedException(SR.GetString(SR.net_ipv6_not_installed));
        }
 
        // copies sendbuffer into unmanaged memory for async icmpsendecho apis
        private unsafe void SetUnmanagedStructures (byte[] buffer) {
            requestBuffer = SafeLocalFree.LocalAlloc(buffer.Length);
            byte* dst = (byte*)requestBuffer.DangerousGetHandle();
            for (int i = 0; i < buffer.Length; ++i)
            {
                dst[i] = buffer[i];
            }
        }
 
        // release the unmanaged memory after ping completion
        void FreeUnmanagedStructures () {
            if (requestBuffer != null) {
                requestBuffer.Close();
                requestBuffer = null;
            }
        }
 
 
        // creates a default send buffer if a buffer wasn't specified.  This follows the
        // ping.exe model
        private byte[] DefaultSendBuffer {
            get {
                if (defaultSendBuffer == null) {
                    defaultSendBuffer = new byte[DefaultSendBufferSize];
                    for (int i=0;i<DefaultSendBufferSize;i++)
                        defaultSendBuffer[i] = (byte)((int)'a'+ i % 23);
                }
                return defaultSendBuffer;
            }
        }
 
        
    }
 
}