8

我想从 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: Additional information: Could not load type 'Dhcpsapi.DHCP_SEARCH_INFO' from assembly 'ConsoleApplication3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' 因为它在偏移量 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 和偏移量:

#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;
};
4

2 回答 2

9

据我所知,您模拟联合的方式是正确的。您得到的异常可能与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 指出了一种更好的方式来布局内部带有联合的结构。你可以在下面阅读他的解释。

于 2012-10-11T12:02:48.037 回答
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# 中使用联合,我错了。但似乎你只能模仿我支持我的解决方案的原始类型

于 2012-10-11T11:51:27.067 回答