2013-07-02 34 views
3

從P /調用的本地函數中,我得到一個指向sockaddr結構的IntPtr。我如何將它轉換爲IPAddressC# - 將指向sockaddr結構的IntPtr轉換爲IPAddress

謝謝!

+0

[p/invoke C函數返回指向結構體的指針]的可能的重複(http://stackoverflow.com/questions/779444/p-invoke-c-function-that-returns-pointer-toa-a- struct) – spender

+1

@spender首先,IPAddress不是一個結構。其次,sockaddr可能包含IPv4或IPv6地址,因此轉換並不像您提到的問題那樣簡單。所以這絕對不是它的重複。 – kol

回答

2

IP地址既然你不能直接Marshal一個sockaddr類型爲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.PtrToStructure獲得sockaddr_in類型從IntPtr像這樣:

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

之後,根據您剛纔檢索的數據創建新的IPAddress對象應該相當簡單。

您可以找到包含在EnumLib在上面的例子在這裏ADDRESS_FAMILIES枚舉:http://pastie.org/8103489

有該結構在最初更多的成員在pinvoke.net,只是take a look here如果你有興趣在他們,但我估計在這種特定情況下你根本不需要它們。

+0

你能否清理一下你的答案?例如,沒有EnumLib的定義,並且不需要調用WSAAddressToString來進行轉換。如果它包含將IntPtr轉換爲IPAddress所需的代碼,那麼對於IPv4和IPv6情況,我都會很樂意接受您的答案。感謝您的幫助! – kol

+0

@kol編輯我的答案爲簡潔並從結構中刪除了兩個多餘的屬性。如果你想完全查看它們,我還在pinvoke上添加了一個原始代碼片段的鏈接。您可能還想在'in_addr'成員上查找http://msdn.microsoft.com/en-us/library/windows/desktop/ms738571(v=vs.85).aspx,專門用於IPv4和IPv6互操作性。 – aevitas

+1

感謝您的幫助,我添加了「最終」轉換器代碼作爲一個單獨的答案。 – kol

0

您必須在C#中創建相應的結構。在使用下面的代碼(p是你IntPtr):

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

可以 「讀」 它的C#。代碼休息是很容易的,因爲你將不得不在sa_data

+0

對不起,這不是我的問題的答案。 IPAddress不是一個結構體,並且sockaddr可能包含IPv4或IPv6地址,因此轉換並不像您所建議的那樣簡單。 – kol

+0

您的項目中定義了哪種結構? IntPtr是指向內存的指針,但是您必須知道您正嘗試讀取的內存大小。 –

+0

鏈接到sockaddr的定義:http://msdn.microsoft.com/en-us/library/windows/desktop/ms740496.aspx – kol

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地址進行了測試。