So here is the full working code:
public static PingReply Send(IPAddress srcAddress, IPAddress destAddress, int timeout = 5000, byte[] buffer = null, PingOptions po = null)
{
if (destAddress == null || destAddress.AddressFamily != AddressFamily.InterNetwork || destAddress.Equals(IPAddress.Any))
throw new ArgumentException();
var source = srcAddress == null ? 0 : BitConverter.ToUInt32(srcAddress.GetAddressBytes(), 0);
var destination = BitConverter.ToUInt32(destAddress.GetAddressBytes(), 0);
var sendbuffer = buffer ?? new byte[] {};
var options = new Interop.Option
{
Ttl = (po == null ? (byte) 255 : (byte) po.Ttl),
Flags = (po == null ? (byte) 0 : po.DontFragment ? (byte) 0x02 : (byte) 0)
};
var fullReplyBufferSize = Interop.ReplyMarshalLength + sendbuffer.Length;
var allocSpace = Marshal.AllocHGlobal(fullReplyBufferSize);
try
{
DateTime start = DateTime.Now;
var nativeCode = Interop.IcmpSendEcho2Ex(
Interop.IcmpHandle,
default(IntPtr),
default(IntPtr),
default(IntPtr),
source,
destination,
sendbuffer,
(short) sendbuffer.Length,
ref options,
allocSpace,
fullReplyBufferSize,
timeout
);
TimeSpan duration = DateTime.Now - start;
var reply = (Interop.Reply) Marshal.PtrToStructure(allocSpace, typeof (Interop.Reply));
byte[] replyBuffer = null;
if (sendbuffer.Length != 0)
{
replyBuffer = new byte[sendbuffer.Length];
Marshal.Copy(allocSpace + Interop.ReplyMarshalLength, replyBuffer, 0, sendbuffer.Length);
}
if (nativeCode == 0)
return new PingReply(nativeCode, reply.Status, new IPAddress(reply.Address), duration);
else
return new PingReply(nativeCode, reply.Status, new IPAddress(reply.Address), reply.RoundTripTime, replyBuffer);
}
finally
{
Marshal.FreeHGlobal(allocSpace);
}
}
private static class Interop
{
private static IntPtr? icmpHandle;
private static int? _replyStructLength;
public static IntPtr IcmpHandle
{
get
{
if (icmpHandle == null)
{
icmpHandle = IcmpCreateFile();
}
return icmpHandle.GetValueOrDefault();
}
}
public static int ReplyMarshalLength
{
get
{
if (_replyStructLength == null)
{
_replyStructLength = Marshal.SizeOf(typeof (Reply));
}
return _replyStructLength.GetValueOrDefault();
}
}
[DllImport("Iphlpapi.dll", SetLastError = true)]
private static extern IntPtr IcmpCreateFile();
[DllImport("Iphlpapi.dll", SetLastError = true)]
private static extern bool IcmpCloseHandle(IntPtr handle);
[DllImport("Iphlpapi.dll", SetLastError = true)]
public static extern uint IcmpSendEcho2Ex(IntPtr icmpHandle, IntPtr Event, IntPtr apcroutine, IntPtr apccontext, UInt32 sourceAddress, UInt32 destinationAddress, byte[] requestData, short requestSize, ref Option requestOptions, IntPtr replyBuffer, int replySize, int timeout);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct Option
{
public byte Ttl;
public readonly byte Tos;
public byte Flags;
public readonly byte OptionsSize;
public readonly IntPtr OptionsData;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct Reply
{
public readonly UInt32 Address;
public readonly int Status;
public readonly int RoundTripTime;
public readonly short DataSize;
public readonly short Reserved;
public readonly IntPtr DataPtr;
public readonly Option Options;
}
}
And here is the result class:
[Serializable]
public class PingReply
{
private readonly byte[] _buffer = null;
private readonly IPAddress _ipAddress = null;
private readonly uint _nativeCode = 0;
private readonly TimeSpan _roundTripTime = TimeSpan.Zero;
private readonly IPStatus _status = IPStatus.Unknown;
private Win32Exception _exception;
internal PingReply(uint nativeCode, int replystatus, IPAddress ipAddress, TimeSpan duration)
{
_nativeCode = nativeCode;
_ipAddress = ipAddress;
if (Enum.IsDefined(typeof (IPStatus), replystatus))
_status = (IPStatus) replystatus;
}
internal PingReply(uint nativeCode, int replystatus, IPAddress ipAddress, int roundTripTime, byte[] buffer)
{
_nativeCode = nativeCode;
_ipAddress = ipAddress;
_roundTripTime = TimeSpan.FromMilliseconds(roundTripTime);
_buffer = buffer;
if (Enum.IsDefined(typeof (IPStatus), replystatus))
_status = (IPStatus) replystatus;
}
public uint NativeCode
{
get { return _nativeCode; }
}
public IPStatus Status
{
get { return _status; }
}
public IPAddress IpAddress
{
get { return _ipAddress; }
}
public byte[] Buffer
{
get { return _buffer; }
}
public TimeSpan RoundTripTime
{
get { return _roundTripTime; }
}
public Win32Exception Exception
{
get
{
if (Status != IPStatus.Success)
return _exception ?? (_exception = new Win32Exception((int) NativeCode, Status.ToString()));
else
return null;
}
}
public override string ToString()
{
if (Status == IPStatus.Success)
return Status + " from " + IpAddress + " in " + RoundTripTime + " ms with " + Buffer.Length + " bytes";
else if (Status != IPStatus.Unknown)
return Status + " from " + IpAddress;
else
return Exception.Message + " from " + IpAddress;
}
}