Click here to Skip to main content
14,239,615 members
Rate this:
Please Sign up or sign in to vote.
See more:
Hello guys,

First of all: sorry for my broken english...

And now to my question:
I'm looking for an equivalent for the function IcmpSendEchoEx2.

I am new to the pinvoke thematics and so I tried it with the following method:
It is a modified version of the pinvoke.net article: icmpsendecho (icmp)

public class IcmpPing
 {
     [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
     private struct ICMP_OPTIONS
     {
         public byte Ttl;
         public byte Tos;
         public byte Flags;
         public byte OptionsSize;
         public IntPtr OptionsData;
     }

     [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
     private struct ICMP_ECHO_REPLY
     {
         public int Address;
         public int Status;
         public int RoundTripTime;
         public short DataSize;
         public short Reserved;
         public IntPtr DataPtr;
         public ICMP_OPTIONS Options;
         [MarshalAs(UnmanagedType.ByValTStr, SizeConst=250)]
         public string Data;
     }

     [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)]
     private static extern int IcmpSendEcho2Ex(IntPtr icmpHandle, int sourceAddress, int destinationAddress, string requestData, short requestSize, ref ICMP_OPTIONS requestOptions, ref ICMP_ECHO_REPLY replyBuffer, int replySize, int timeout);

     public bool Ping(IPAddress src,IPAddress dest)
     {
         IntPtr icmpHandle = IcmpCreateFile();
         ICMP_OPTIONS icmpOptions = new ICMP_OPTIONS();
         icmpOptions.Ttl = 255;
         ICMP_ECHO_REPLY icmpReply = new ICMP_ECHO_REPLY();
         string sData = "x";

         int iReplies = IcmpSendEcho2Ex(icmpHandle, BitConverter.ToInt32(src.GetAddressBytes(), 0), BitConverter.ToInt32(dest.GetAddressBytes(), 0), sData, (short)sData.Length, ref icmpOptions, ref icmpReply, Marshal.SizeOf(icmpReply), 30);
         IcmpCloseHandle(icmpHandle);
         if (icmpReply.Status == 0)
             return true;
         return false;
     }
 }


When I try to send the ping i get an AccessViolationException, so i removed the
"_In_opt_ PIP_OPTION_INFORMATION RequestOptions" field.

After I removed the field, the function executed successfully but no ping was sended at all and the return value was (int)0.


I really appreciate your help and engagement!
Posted
Updated 29-Oct-14 6:23am
v4
Comments
ZurdoDev 13-Oct-14 8:32am
   
The title says "Solved." If this is solved please post something as a solution so that this no longer shows as unanswered.
Christian Sack 13-Oct-14 14:00pm
   
I am currently working on the solution but the main question is solved.
If it is finished, I will post it in a few days.

1 solution

Rate this:
Please Sign up or sign in to vote.

Solution 1

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();

    //Defining pinvoke args
    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) //0x02
    };
    var fullReplyBufferSize = Interop.ReplyMarshalLength + sendbuffer.Length; //Size of Reply struct and the transmitted buffer length.



    var allocSpace = Marshal.AllocHGlobal(fullReplyBufferSize); // unmanaged allocation of reply size. TODO Maybe should be allocated on stack
    try
    {
        DateTime start = DateTime.Now;
        var nativeCode = Interop.IcmpSendEcho2Ex(
            Interop.IcmpHandle, //_In_      HANDLE IcmpHandle,
            default(IntPtr), //_In_opt_  HANDLE Event,
            default(IntPtr), //_In_opt_  PIO_APC_ROUTINE ApcRoutine,
            default(IntPtr), //_In_opt_  PVOID ApcContext
            source, //_In_      IPAddr SourceAddress,
            destination, //_In_      IPAddr DestinationAddress,
            sendbuffer, //_In_      LPVOID RequestData,
            (short) sendbuffer.Length, //_In_      WORD RequestSize,
            ref options, //_In_opt_  PIP_OPTION_INFORMATION RequestOptions,
            allocSpace, //_Out_     LPVOID ReplyBuffer,
            fullReplyBufferSize, //_In_      DWORD ReplySize,
            timeout //_In_      DWORD Timeout
            );
        TimeSpan duration = DateTime.Now - start;
        var reply = (Interop.Reply) Marshal.PtrToStructure(allocSpace, typeof (Interop.Reply)); // Parse the beginning of reply memory to reply struct

        byte[] replyBuffer = null;
        if (sendbuffer.Length != 0)
        {
            replyBuffer = new byte[sendbuffer.Length];
            Marshal.Copy(allocSpace + Interop.ReplyMarshalLength, replyBuffer, 0, sendbuffer.Length); //copy the rest of the reply memory to managed byte[]
        }

        if (nativeCode == 0) //Means that native method is faulted.
            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); //free allocated space
    }
}

/// <summary>Interoperability Helper
///     <see cref="http://msdn.microsoft.com/en-us/library/windows/desktop/bb309069(v=vs.85).aspx" />
/// </summary>
private static class Interop
{
    private static IntPtr? icmpHandle;
    private static int? _replyStructLength;

    /// <summary>Returns the application legal icmp handle. Should be close by IcmpCloseHandle
    ///     <see cref="http://msdn.microsoft.com/en-us/library/windows/desktop/aa366045(v=vs.85).aspx" />
    /// </summary>
    public static IntPtr IcmpHandle
    {
        get
        {
            if (icmpHandle == null)
            {
                icmpHandle = IcmpCreateFile();
                //TODO Close Icmp Handle appropiate
            }

            return icmpHandle.GetValueOrDefault();
        }
    }
    /// <summary>Returns the the marshaled size of the reply struct.</summary>
    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;
    }


    /// <summary>Native result from <code>IcmpSendEcho2Ex</code>.</summary>
    public uint NativeCode
    {
        get { return _nativeCode; }
    }
    public IPStatus Status
    {
        get { return _status; }
    }
    /// <summary>The source address of the reply.</summary>
    public IPAddress IpAddress
    {
        get { return _ipAddress; }
    }
    public byte[] Buffer
    {
        get { return _buffer; }
    }
    public TimeSpan RoundTripTime
    {
        get { return _roundTripTime; }
    }
    /// <summary>Resolves the <code>Win32Exception</code> from native code</summary>
    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;
    }
}
   

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)




CodeProject, 503-250 Ferrand Drive Toronto Ontario, M3C 3G8 Canada +1 416-849-8900 x 100