Rem*_*mko 8 c c# pinvoke winapi
我想从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;
Run Code Online (Sandbox Code Playgroud)
我认为正确的转换是这样的:
[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;
};
Run Code Online (Sandbox Code Playgroud)
但是这会产生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;
}
Run Code Online (Sandbox Code Playgroud)
编辑:
我在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;
}
Run Code Online (Sandbox Code Playgroud)
输出是:
sizeof(DHCP_SEARCH_INFO)=12
ClientIpAddress offset=4
ClientHardwareAddress offset=4
ClientName offset=4
Run Code Online (Sandbox Code Playgroud)
编辑: 根据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;
};
Run Code Online (Sandbox Code Playgroud)
就我所知,你模拟联合的方式是正确的.您获得的异常可能与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;
}
Run Code Online (Sandbox Code Playgroud)
编辑:我想这意味着在C#中模拟联合对C++中的联合有类似的要求.在C++中,您只能在联合中使用POD类型.在C#中,您可能只有结构类型.
更新:感谢DavidHeffernan指出了一种更好的方法来布置内部结合的结构.你可以在下面阅读他的解释.