Convert C Union to C# (Incorrectly Aligned) - c#

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

The way you are simulating the union is correct as far as I can tell. The exception you are getting is likely related to thestring 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.

I believe that the best thing to do is to add get/set methods to... guess what... get/set the correct bits in a variable. I believe that you'll have to wrap that too
Something like:
[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;
}
};
I wrote this directly here and didn't test it.
EDIT:
Camford enlightened me that you can have unions in C#, I was wrong. But as it seem that you can only emulate the primitive types I stand by my solution

Related

How to define MAPINAMEID structure in C# in 32/64-bit compatible way?

In C, MAPINAMEID is defined as:
typedef struct _MAPINAMEID
{
LPGUID lpguid;
ULONG ulKind;
union {
LONG lID;
LPWSTR lpwstrName;
} Kind;
} MAPINAMEID, FAR * LPMAPINAMEID;
In C#, my definition is:
[StructLayout(LayoutKind.Explicit)]
private struct MAPINAMEID
{
[FieldOffset(0)]
public IntPtr lpguid;
[FieldOffset(8)]
public uint ulKind;
[FieldOffset(16)]
public int lID;
[FieldOffset(16)]
public IntPtr lpwstrName;
};
Obviously, it works in 64-bit mode only, with 32-bit I need different offset values. Unfortunately, FieldOffset attribute does not allow using computable values (like IntPtr.Size). Is there a platform-independent way to specify offset (or somehow else tell the compiler that I want lID and lpwstrName to share the same offset?
You can declare it like that:
[StructLayout(LayoutKind.Sequential)]
private struct MAPINAMEID
{
public IntPtr lpguid;
public uint ulKind;
public IntPtr lpwstrName; // or lID
};
And use IntPtr 32-bit conversion to switch between lpwstrName and lId when needed (LONG and ULONG are 32-bit).

C# struct behavior based on architecture

I have a question regarding either feeding multiple structs into a method depending on compile architecture,
or about properly laying out the Thread_Basic_Information struct so I can just use a single struct for the same method regardless of x64/x86 (see for reference: https://msdn.microsoft.com/en-us/library/windows/desktop/ms684283(v=vs.85).aspx)
I have confirmed it working fine on x86, and if I manually switch out the struct's it will also work for x64.
However I've recently tried to change my approach from just copy-pasting a ton of functions to trying to recycle my code.
I'm having an extremely difficult time trying to find either precise information on the struct in question which I can use in C# to make it compatible regardless of x86/x64, or feeding 2 different struct's into the same method depending on architecture.
Perhaps StructLayout pack size? however I am not familiar with that attribute.
I'm hoping there might be a way I can generic this, such as if IntPtr.Size == 8 then use 64bit struct, else use 32bit struct. However short of copy-pasting code with a minor rename, I'm hoping there may be a way to do this with generics?
Code:
Method used to create the thread:
public IRemoteThread Create(IntPtr address, bool isStarted = true)
{
//Create the thread
var thr = ThreadHelper.CreateRemoteThread(Process.Handle, address, IntPtr.Zero, ThreadCreationFlags.Suspended)
//Acquire desired information from the thread
var ret = ThreadHelper.NtQueryInformationThread(thr);
// Do other stuff
return result;
}
Support method for querying the thread info we desire:
public static ThreadBasicInformation NtQueryInformationThread(SafeMemoryHandle threadHandle)
{
// Check if the handle is valid
HandleManipulator.ValidateAsArgument(threadHandle, "threadHandle");
// Create a structure to store thread info
var info = new ThreadBasicInformation();
// Get the thread info
var ret = Nt.NtQueryInformationThread(threadHandle, 0, ref info, MarshalType<ThreadBasicInformation>.Size,
IntPtr.Zero);
// If the function succeeded
if (ret == 0)
return info;
// Else, couldn't get the thread info, throws an exception
throw new ApplicationException($"Couldn't get the information from the thread, error code '{ret}'.");
}
32bit struct used in the above method:
[StructLayout(LayoutKind.Sequential)]
public struct ThreadBasicInformation
{
public uint ExitStatus;
public IntPtr TebBaseAdress;
public int ProcessId;
public int ThreadId;
public uint AffinityMask;
public uint Priority;
public uint BasePriority;
}
x64 variant of the same struct
[StructLayout(LayoutKind.Explicit)]
public struct ThreadBasicInformation64
{
[FieldOffset(0)]
public uint ExitStatus;
[FieldOffset(8)]
public IntPtr TebBaseAdress;
[FieldOffset(16)]
public int ProcessId;
[FieldOffset(24)]
public int ThreadId;
[FieldOffset(32)]
public uint AffinityMask;
[FieldOffset(40)]
public uint Priority;
[FieldOffset(44)]
public uint BasePriority;
}
EDIT:
C declaration I found:
typedef LONG KPRIORITY;
typedef struct _CLIENT_ID {
HANDLE UniqueProcess;
HANDLE UniqueThread;
} CLIENT_ID;
typedef CLIENT_ID *PCLIENT_ID;
typedef struct _THREAD_BASIC_INFORMATION
{
NTSTATUS ExitStatus;
PVOID TebBaseAddress;
CLIENT_ID ClientId;
KAFFINITY AffinityMask;
KPRIORITY Priority;
KPRIORITY BasePriority;
} THREAD_BASIC_INFORMATION, *PTHREAD_BASIC_INFORMATION;
The premise of your question is wrong because you have mistranslated the structs. Once you translate them correctly you will find that you can use a single declaration of the struct in your C# code and the compiler will lay it out correctly for all target architectures.
[StructLayout(LayoutKind.Sequential)]
public struct CLIENT_ID
{
public IntPtr UniqueProcess;
public IntPtr UniqueThread;
}
[StructLayout(LayoutKind.Sequential)]
public struct THREAD_BASIC_INFORMATION
{
public int ExitStatus;
public IntPtr TebBaseAdress;
public CLIENT_ID ClientId;
public IntPtr AffinityMask;
public int Priority;
public int BasePriority;
}

Try Call Dll, But the method has a complicated struct type, I can't define it in .Net

The struct define in C++ is:
typedef struct _DEVICE_PATH_LIST_
{
int NumDevice;
TCHAR *devicePath[DEVICE_PATH_LIST_SIZE];
} DEVICE_PATH_LIST;
and the C++ function's definition is:
LandiWin_iGetDevicePathList( int iDeviceType, DEVICE_PATH_LIST * devicePathList);
Then I want to call that method, so I need to define a struct that can represent the DEVICE_PATH_LIST. Here is my definition in C#:
[StructLayout(LayoutKind.Sequential)]
public unsafe struct DevicePathType
{
public int NumDevice;
[MarshalAs(UnmanagedType.ByValArray,SizeConst=32)]
public char*[] devicePath;
}
but it's not right, I also tried all other type but all failed. please help me define a right struct.
The code of C++ like:
typedef struct _DEVICE_PATH_LIST_
{
int NumDevice;
TCHAR *devicePath[DEVICE_PATH_LIST_SIZE];
} DEVICE_PATH_LIST;
The code of C# need to be like:
[StructLayout(LayoutKind.Sequential)]
public struct DevicePathType
{
public int NumDevice;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public IntPtr[] devicePath;
}

Converting c++ struct to c# and using it?

I have a c++ struct as follow:
struct Vehicle
{
u32 something;
Info *info;
u8 something2[ 0x14 ];
Vector3f location;
Template* data;
};
struct Info
{
u32 speed;
std::string name;
BuffCollection** buffCollection;
void* sectionPtr;
};
struct Template
{
u32 templateID;
};
From this question, I figured out the meaning of the u32, u8, and so on, or so I think I did.
Then I tried to make my own C# struct out of it:
[StructLayout(LayoutKind.Sequential)]
public struct Vehicle
{
public uint Something;
public Info Info;
public byte Something2;
public Vector3f Location;
public Template Data;
}
[StructLayout(LayoutKind.Sequential)]
public struct Info
{
public uint Speed;
public string Name;
public byte[] BuffCollection;
public IntPtr SectionPointer;
}
[StructLayout(LayoutKind.Sequential)]
public struct Template
{
public uint TemplateId;
}
public struct Vector3f
{
public float X, Y, Z;
public Vector3f(float x, float y, float z)
{
X = x;
Y = y;
Z = z;
}
}
However, when I try to read the Vehicle:
[DllImport("Core.dll")]
static extern Vehicle GetVehicle();
static void Main()
{
var vehicle = GetVehicle();
Console.WriteLine(vehicle.Info.Name);
Console.ReadKey();
}
I get the following error:
System.Runtime.InteropServices.MarshalDirectiveException: Method's type signature is not PInvoke compatible
From the search I did on it, it lead me to believe that my structure conversion is wrong.
What is wrong with my converted structures?
Regarding structures:
Vehicle.Info is a pointer, so you need to declare it as IntPtr Info, and then use Marshal.PtrToStructure / Marshal.StructureToPtr to read/write its value in managed code;
Vehicle.something2 is a byte array, not a byte, so you need to declare it this way:
[MarshalAs(UnmanagedType.ByValArray, SizeConst=20)]
byte[] something2=new byte[20];
Vehicle.Data - see #1, the same problem
Info.Name - .NET does not provide marshalling for std::string, so you will either need to write your own marshaler (see this: Custom Marshaler for PInvoke with std::string) or change the type to something like char* in your c++ library.
Info.BuffCollection should also be an IntPtr or BuffCollection[] (depending on what the BuffCollection type is about - it's not provided in your question)
Regarding the signature and invocation of GetVehicle(); method:
it is likely that the method returns the pointer to the structure, not the structure itself (just speculating, please double check). If so, you need to declare it as
static extern IntPtr GetVehicle();
and then use Marshal.PtrToStructure to convert it to your structure like this:
var vehiclePtr=GetVehicle();
var vehicle = (Vehicle)Marshal.PtrToStructure(vehiclePtr, typeof(Vehicle));

Unions in C# - incorrectly aligned or overlapped with a non-object field

I am marshaling via PInvoke to a native C dll which expects the following call.
private static extern int externalMethod(IntPtr Data, [MarshalAs(UnmanagedType.U4)] ref int dataLength);
The dataLength parameter is the length of the struct being passed via the IntPtr Data parameter. It throws an exception if the two do not match. The external method uses a C Union joining together four types.
I've managed to recreate unions in C# by using the FieldOffsetAttribute. I am then calculating the length of the C# union and calling the method with the following:
int len = Marshal.SizeOf(data);
IntPtr ptr = Marshal.AllocCoTaskMem(len);
externalMethod(ptr, len);
However, I get the error System.TypeLoadException : ... Could not load type because it contains an object field at offset 0 that is incorrectly aligned or overlapped by a non-object field. with the following code. I believe it is perhaps either one of the strings or the integer array (variable B7)? How would I go about changing this to make it work - do I have to break the integer array into multiple variables?
[StructLayoutAttribute(LayoutKind.Explicit)]
public struct Union{
[FieldOffset(0)]
public A a;
[FieldOffset(0)]
public B b;
[FieldOffset(0)]
public C c;
[FieldOffset(0)]
public D d;
}
[StructLayout(LayoutKind.Sequential)]
public struct A
{
public int A1;
public int A2;
public int A3;
[MarshalAs(UnmanagedType.LPTStr, SizeConst = 17)]
public string A4;
[MarshalAs(UnmanagedType.LPTStr, SizeConst = 4)]
public string A5;
}
[StructLayout(LayoutKind.Sequential)]
public struct B
{
public int B1;
[MarshalAs(UnmanagedType.LPTStr, SizeConst = 2)]
public string B2;
[MarshalAs(UnmanagedType.LPTStr, SizeConst = 4)]
public string B3;
[MarshalAs(UnmanagedType.LPTStr, SizeConst = 6)]
public string B4;
public int B5;
public int B6;
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U4, SizeConst = 255)]
public int[] B7;
}
[StructLayout(LayoutKind.Sequential)]
public struct C
{
public int C1;
public int C2;
public int C3;
public int C4;
[MarshalAs(UnmanagedType.LPTStr, SizeConst = 32)]
public string C5;
public float C6;
public float C7;
public float C8;
public float C9;
public float C10;
public float C11;
public float C12;
public float C13;
public float C14;
}
[StructLayout(LayoutKind.Sequential)]
public struct D
{
public int D1;
[MarshalAs(UnmanagedType.LPTStr, SizeConst = 36)]
public string D2;
}
Just use the A/B/C/D structs directly and skip the union. In your extern calls, simply substitute the correct struct in the method declaration.
extern void UnionMethodExpectingA( A a );
If the unmanaged methods actually accept a union and behave differently based on the type passed, then you can declare different extern methods that all end up calling the same unmanaged entry point.
[DllImport( "unmanaged.dll", EntryPoint="ScaryMethod" )]
extern void ScaryMethodExpectingA( A a );
[DllImport( "unmanaged.dll", EntryPoint="ScaryMethod" )]
extern void ScaryMethodExpectingB( B b );
Updated for "length" parameter. The logic still applies. Just create a "wrapper" method and do the same thing.
void CallScaryMethodExpectingA( A a )
{
ScaryMethodExpectingA( a, Marshal.SizeOf( a ) );
}
It is difficult to answer this question without knowing what you are trying to achieve. An explicitly-layouted struct is a very bad choice for any normal use-case; this only makes sense if you are using the data in native calls (pinvoke), and in those cases you definitely don’t want to use the managed class string. The [MarshalAs] attribute only takes effect during the call invocations, not constantly every time the field is read from or written to by your managed code. It doesn’t allow you to overlap a string pointer with an int because doing so would allow you to set the pointer to a meaningless value and then accessing the string would crash the CLR. The same is true for arrays, so you can’t use char[] either.
If you are the author of the native code that you need to call, then I strongly recommend to write four separate methods instead of a single one that accepts four completely different data structures.
If you cannot change the native code, then you could always declare your four structs A, B, C and D the way you do now and just use them directly, without the union. Just declare four different pinvoke declarations for the same native function (use the EntryPoint property on the [DllImport] attribute).

Categories