C# - 将指向sockaddr结构的IntPtr转换为IPAddress

时间:2013-07-02 13:29:40

标签: c# type-conversion pinvoke ip-address intptr

从P / Invoked本机函数中,我得到一个指向IntPtr结构的sockaddr。如何将其转换为IPAddress

谢谢!

3 个答案:

答案 0 :(得分:5)

按照@aevitas的回答,我提出了以下解决方案:

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

我使用IPv4和IPv6地址成功测试了它。

答案 1 :(得分:2)

由于您无法直接Marshalsockaddr类型转换为IPAddress类型,因此您必须首先将a managed struct out of it编组为:

[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;
    }
}

然后,您可以使用Marshal.PtrToStructuresockaddr_in获取IntPtr类型,如下所示:

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

之后,从您刚检索的数据中创建一个新的IPAddress对象应该相当简单。

您可以在上面的示例中找到ADDRESS_FAMILIES中包含的EnumLib枚举:http://pastie.org/8103489

最初在pinvoke.net上有更多结构成员,如果你对它们感兴趣,只有take a look here,但我认为在这种特定情况下你根本不需要它们。

答案 2 :(得分:0)

您必须在C#中创建相应的结构。使用以下代码(pIntPtr):

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

您可以将其“读取”为C#。其余代码很简单,因为您将在sa_data

中拥有IP地址