Convert C Union to C# (Incorrectly Aligned)
c, c#, pinvoke, winapi
Solution
The way you are simulating the union is correct as far as I can tell. The exception you are getting is likely related to the`string` object in your struct. I tried to build your code in a test project. With string in the struct, I get the same exception as you do. With an IntPtr replacing the string, I don't get any exceptions. Whether the call to `DhcpGetClientInfo` is going to work or not, I have no idea. You can use `Marshal.StringToHGlobalUni` to get an IntPtr for your string.
[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;
}
Edit: I guess this means that simulating a union in C# has similar requirement to the union in C++. In C++ you can only have POD types in a union. In C# you can probably only have struct types.
Updated: Thanks to DavidHeffernan for pointing out a better way of laying out structs with unions inside. You can read his explanation below.
Problem
I want to call the DhcpGetClientInfo API from C# but I have a question on conversion of this C struct to 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; ``` I think the Correct conversion is this: ``` [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; }; ``` But that gives an System.TypeLoadException: Additional information: Could not load type 'Dhcpsapi.DHCP_SEARCH_INFO' from assembly 'ConsoleApplication3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' because it contains an object field at offset 4 that is incorrectly aligned or overlapped by a non-object field. This is the conversion of the other types in case you want to compile: ``` 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; } ``` EDIT: I verified sizeof and offsets in C: ``` #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; } ``` Output is: ``` sizeof(DHCP_SEARCH_INFO)=12 ClientIpAddress offset=4 ClientHardwareAddress offset=4 ClientName offset=4 ``` EDIT: Based on Camford's answer I declared the struct as below. Using sizeof should make it correct for x64 as well. ``` [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; }; ```