我想从C#调用DhcpGetClientInfo API,但我有一个关于将此C结构转换为C#的问题:
typedef struct _DHCP_CLIENT_SEARCH_INFO {
DHCP_SEARCH_INFO_TYPE SearchType;
union {
DHCP_IP_ADDRESS ClientIpAddress;
DHCP_CLIENT_UID ClientHardwareAddress;
LPWSTR ClientName;
} SearchInfo;
} DHCP_SEARCH_INFO, *LPDHCP_SEARCH_INFO;
我认为正确的转换是这样的:
[StructLayout(LayoutKind.Explicit, Size=12)]
public struct DHCP_SEARCH_INFO
{
[FieldOffset(0)]
public DHCP_SEARCH_INFO_TYPE SearchType;
[FieldOffset(4)]
public DHCP_IP_ADDRESS ClientIpAddress;
[FieldOffset(4)]
public DHCP_BINARY_DATA ClientHardwareAddress;
[FieldOffset(4), MarshalAs(UnmanagedType.LPWStr)]
public string ClientName;
};
但是这会产生System.TypeLoadException:附加信息:无法从程序集'ConsoleApplication3,Version = 1.0.0.0,Culture = neutral,PublicKeyToken = null'加载类型'Dhcpsapi.DHCP_SEARCH_INFO',因为它包含偏移处的对象字段4由非对象字段错误对齐或重叠。
如果你想编译,这是其他类型的转换:
public enum DHCP_SEARCH_INFO_TYPE : uint
{
DhcpClientIpAddress = 0,
DhcpClientHardwareAddress = 1,
DhcpClientName = 2
};
[StructLayout(LayoutKind.Sequential)]
public struct DHCP_BINARY_DATA
{
public uint DataLength;
public IntPtr Data;
};
[StructLayout(LayoutKind.Sequential)]
public struct DHCP_IP_ADDRESS
{
public UInt32 IPAddress;
}
修改
我在C中验证了sizeof和offsets:
#pragma comment(lib,"Dhcpsapi.lib")
int _tmain(int argc, _TCHAR* argv[])
{
DHCP_SEARCH_INFO si;
printf("sizeof(DHCP_SEARCH_INFO)=%d\n", sizeof(DHCP_SEARCH_INFO));
printf("ClientIpAddress offset=%d\n", (PBYTE)&si.SearchInfo.ClientIpAddress - (PBYTE)&si);
printf("ClientHardwareAddress offset=%d\n", (PBYTE)&si.SearchInfo.ClientHardwareAddress - (PBYTE)&si);
printf("ClientName offset=%d\n", (PBYTE)&si.SearchInfo.ClientName - (PBYTE)&si);
return 0;
}
输出是:
sizeof(DHCP_SEARCH_INFO)=12
ClientIpAddress offset=4
ClientHardwareAddress offset=4
ClientName offset=4
修改 根据Camford的回答,我将结构声明如下。使用sizeof也应该使x64正确。
[StructLayout(LayoutKind.Explicit, Size=12)]
public struct DHCP_SEARCH_INFO
{
[FieldOffset(0)]
public DHCP_SEARCH_INFO_TYPE SearchType;
[FieldOffset(sizeof(DHCP_SEARCH_INFO_TYPE))]
public DHCP_IP_ADDRESS ClientIpAddress;
[FieldOffset(sizeof(DHCP_SEARCH_INFO_TYPE))]
public IntPtr ClientName;
[FieldOffset(sizeof(DHCP_SEARCH_INFO_TYPE))]
public DHCP_BINARY_DATA ClientHardwareAddress;
};
答案 0 :(得分:7)
就我所知,你模拟联盟的方式是正确的。您获得的异常可能与结构中的string
对象有关。我试图在测试项目中构建您的代码。使用结构中的字符串,我会得到与您相同的异常。使用IntPtr替换字符串,我没有任何例外。无论对DhcpGetClientInfo
的呼叫是否有效,我都不知道。您可以使用Marshal.StringToHGlobalUni
获取字符串的IntPtr。
[StructLayout(LayoutKind.Explicit)]
public struct SearchInfo
{
[FieldOffset(0)]
public DHCP_IP_ADDRESS ClientIpAddress;
[FieldOffset(0)]
public DHCP_BINARY_DATA ClientHardwareAddress;
[FieldOffset(0)]
public IntPtr ClientName; //LPWSTR
}
[StructLayout(LayoutKind.Sequential)]
public struct DHCP_SEARCH_INFO
{
public DHCP_SEARCH_INFO_TYPE SearchType;
public SearchInfo SearchInfo;
}
编辑:我想这意味着在C#中模拟联合对C ++中的联合有类似的要求。在C ++中,您只能在联合中使用POD类型。在C#中,您可能只有结构类型。
更新:感谢DavidHeffernan指出了一种更好的方法来布置内部结合的结构。你可以在下面阅读他的解释。
答案 1 :(得分:0)
我认为最好的办法是将get / set方法添加到...猜测...获取/设置变量中的正确位。我相信你也必须把它包起来
类似的东西:
[StructLayout(LayoutKind.Sequential)]
public struct DHCP_SEARCH_INFO
{
public DHCP_SEARCH_INFO_TYPE SearchType;
public ulong Complex;
};
public struct DHCP_SEARCH_INFO_WRAP
{
public DHCP_SEARCH_INFO_TYPE SearchType;
private ulong Complex;
public DHCP_IP_ADDRESS ClientIpAddress
{
get
{
return BitConverter.ToInt32(BitConverter.GetBytes(Complex),0);
}
set
{
byte[] orig = BitConverter.GetBytes(Complex);
byte[] chng = BitConverter.GetBytes(value);
Array.Copy(chng,0,orig,0,4);
}
}
public DHCP_SEARCH_INFO_WRAP(ref DHCP_SEARCH_INFO var)
{
this.SearchType = var.SearchType;
this.Complex = var.Complex;
}
};
我在这里直接写了这个并没有测试它。
编辑: Camford让我觉得你可以在C#中拥有工会,我错了。但是因为看起来你只能模仿我支持我的解决方案的基本类型