Question

From a P/Invoked native function, I get an IntPtr which points to a sockaddr structure. How can I convert it to an IPAddress?

Thanks!

Was it helpful?

Solution 2

Since you can't directly Marshal a sockaddr type into an IPAddress type, you'd have to make a managed struct out of it first to marshal it into:

[StructLayout(LayoutKind.Sequential)]
internal struct sockaddr_in
{
    internal EnumLib.ADDRESS_FAMILIES sin_family;
    internal ushort sin_port;
    internal in_addr sin_addr;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] internal byte[] sin_zero;

    internal static sockaddr_in FromString(string host, int port)
    {
    var sockaddr = new sockaddr_in();
    var lpAddressLength = Marshal.SizeOf(sockaddr);
    WSAStringToAddress(host + ":" + port, EnumLib.ADDRESS_FAMILIES.AF_INET, IntPtr.Zero,
                      ref sockaddr, ref lpAddressLength);
    return sockaddr;
    }
}

You could then use Marshal.PtrToStructure to obtain a sockaddr_in type from your IntPtr like so:

(sockaddr_in) Marshal.PtrToStructure(address, typeof(sockaddr_in));

After that it should be fairly simple to create a new IPAddress object from the data you've just retrieved.

You can find the ADDRESS_FAMILIES enum that is contained in EnumLib in the above example here: http://pastie.org/8103489

There are originally more members of the struct over at pinvoke.net, just take a look here if you are interested in them but I reckon you won't need them at all in this specific situation.

OTHER TIPS

Following @aevitas' answer, I came up with the following solution:

public enum SockAddrFamily
{
  Inet = 2,
  Inet6 = 23
}

[StructLayout(LayoutKind.Sequential)]
public struct SockAddr
{
  public ushort Family;
  [MarshalAs(UnmanagedType.ByValArray, SizeConst = 14)]
  public byte[] Data;
};

[StructLayout(LayoutKind.Sequential)]
public struct SockAddrIn 
{
  public ushort Family;
  public ushort Port;
  public uint Addr;
  [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
  public byte[] Zero;
}

[StructLayout(LayoutKind.Sequential)]
public struct SockAddrIn6 
{
  public ushort Family;
  public ushort Port;
  public uint FlowInfo;
  [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] 
  public byte[] Addr;
  public uint ScopeId;
};

public IPAddress ConvertSockAddrPtrToIPAddress(IntPtr sockAddrPtr)
{
  SockAddr sockAddr = (SockAddr)Marshal.PtrToStructure(sockAddrPtr, typeof(SockAddr));
  switch ((SockAddrFamily)sockAddr.Family)
  {
    case SockAddrFamily.Inet:
    {
      SockAddrIn sockAddrIn = (SockAddrIn)Marshal.PtrToStructure(sockAddrPtr, typeof(SockAddrIn));
      return new IPAddress(sockAddrIn.Addr);
    }
    case SockAddrFamily.Inet6:
    {
      SockAddrIn6 sockAddrIn6 = (SockAddrIn6)Marshal.PtrToStructure(sockAddrPtr, typeof(SockAddrIn6));
      return new IPAddress(sockAddrIn6.Addr);
    }
    default:
      throw new Exception(string.Format("Non-IP address family: {0}", sockAddr.Family));
  }
}

I successfully tested it with both IPv4 and IPv6 addresses.

You have to create corresponding structure in C#. The using following code (p is you IntPtr):

(MyStruct)System.Runtime.InteropServices.Marshal.PtrToStructure(p, typeof(MyStruct));

You can "read" it to C#. Rest of code is easy, because you will have IP adress in sa_data

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top