问题
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!
回答1:
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.
回答2:
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.
回答3:
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
来源:https://stackoverflow.com/questions/17426939/c-sharp-converting-intptr-pointing-to-sockaddr-structure-to-ipaddress