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: 追加情報: アセンブリ 'ConsoleApplication3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' から型 'Dhcpsapi.DHCP_SEARCH_INFO' をロードできませんでした。オブジェクト以外のフィールドが正しく配置されていないか、重なり合っています。

これは、コンパイルする場合の他の型の変換です。

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 と offset を確認しました。

#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

編集: カムフォードの答えに基づいて、構造体を以下のように宣言しました。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 に答える