Using C#'s Marshal.PtrToStructure on a C++ struct containing two bitsets - c#

I'm trying to use my existing code to map a C++ structure containing std::bitsets to a C# version of it.
My code works great on these simple objects:
// C++ struct
struct log_t
{
uint16 current;
bool complete[64];
};
C# struct
[StructLayout(LayoutKind.Sequential)]
public struct Log
{
public UInt16 current;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 64, ArraySubType = UnmanagedType.Struct)]
public byte[] complete;
}
// Convert the C# struct back to bytes for the C++ app to use
private byte[] GetObjectBytes(object obj)
{
int size = Marshal.SizeOf(obj);
byte[] arr = new byte[size];
IntPtr ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(obj, ptr, true);
Marshal.Copy(ptr, arr, 0, size);
Marshal.FreeHGlobal(ptr);
return arr;
}
How do I map the following C++ structs to C# structs that contains two std::bitsets? I wish to use them in the same manner as above.
struct items_table
{
std::bitset<512> keyList;
std::bitset<512> seenList;
};
struct items
{
std::array<items_table, 7> tables;
};
SOLUTION
I used byte[] arrays in C# to represent the bitsets in C++. I then had to access each bit like this:
private void AddItem(int ItemId, KeyItems keyitems)
{
decimal byteIndex = (ItemId % 512) / 8;
decimal bitInByteIndex = (ItemId % 512) % 8;
keyitems.tables[ItemId / 512].keyList[(int)byteIndex] |= (byte)(1 << (byte)bitInByteIndex);
}

We use interop a lot where I work, and though I haven't had to handle this situation, I suggest you treat your bitsets as byte arrays (byte[] in C#, I believe char[] in C++ unless you're aliasing char). After all 512 bits is just 64 contiguous bytes.
I admit I haven't given you a full explanation of the syntax, my apologies, but I hope this gets you pointed in the right direction.
If your C# end needs to use them as BitArray instances, you could create properties on your struct there and just make them calculated, based on your byte[]s. That does no harm for interop; the marshaller doesn't try to convert them across code boundaries in either direction.

Related

Is there a generic way to write a struct to bytes in Big Endian format?

I've found questions such as this one, which have come close to solving my dilemma. However, I've yet to find a clean approach to solving this issue in a generic manner.
I have a project that has a lot of structs that will be used for binary data transmission. This data needs to be Big Endian and, of course, most .Net architecture is Little Endian. This means that when I convert my structs to bytes, the byte order for my values are reversed.
Is there a fairly straight-forward approach to either forcing my structs to contain data in Big Endian format, or is there a way to generically write these structs to byte arrays (and byte arrays to structs) that output Big Endian data?
Here is some sample code for what I've already done.
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public unsafe struct StructType_1
{
short shortVal;
ulong ulongVal;
int intVal;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public unsafe struct StructType_2
{
long longVal_1;
long longVal_2;
long longVal;
int intVal;
}
...
public static class StructHelper
{
//How can I change the following methods so that the struct data
//is converted to and from BigEndian data?
public static byte[] StructToByteArray<T>(T structVal) where T : struct
{
int size = Marshal.SizeOf(structVal);
byte[] arr = new byte[size];
IntPtr ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(structVal, ptr, true);
Marshal.Copy(ptr, arr, 0, size);
Marshal.FreeHGlobal(ptr);
return arr;
}
public static T StructFromByteArray<T>(byte[] bytes) where T : struct
{
int sz = Marshal.SizeOf(typeof(T));
IntPtr buff = Marshal.AllocHGlobal(sz);
Marshal.Copy(bytes, 0, buff, sz);
T ret = (T)Marshal.PtrToStructure(buff, typeof(T));
Marshal.FreeHGlobal(buff);
return ret;
}
}
If you don't mind reading and writing each field to a stream (which may have performance implications) you could use Jon Skeet's EndianBinaryWriter: https://stackoverflow.com/a/1540269/106159
The code would look something like this:
public unsafe struct StructType_2
{
long longVal_1;
long longVal_2;
long longVal;
int intVal;
}
using (MemoryStream memory = new MemoryStream())
{
using (EndianBinaryWriter writer = new EndianBinaryWriter(EndianBitConverter.Big, stream))
{
writer.Write(longVal_1);
writer.Write(longVal_2);
writer.Write(longVal);
writer.Write(intVal);
}
byte[] buffer = memory.ToArray();
// Use buffer
}
You would use the EndianBinaryReader for data going in the opposite direction.
This does of course have two fairly large drawbacks:
It's fiddly writing code to read and write each field.
It may be too slow to do it this way, depending on performance requirements.
Also have a look at this answers to this similar question: Marshalling a big-endian byte collection into a struct in order to pull out values
Given the example on the BitConverter that shows converting a uint, I would suspect not.

Converting a structure to an intptr

I have a class definition as follows:
[StructLayout(LayoutKind.Sequential)]
public class OUR_MEM_STR
{
public byte[] p;
public int len;
};
This is an equivalent defintion of the C structure below:
typedef struct
{
void *p;
int len;
} OUR_MEM_STR;
I used byte[] instead of IntPtr type for member p becuase of the way it was being used thorughout c# project.
I have defined an object obj with len = 10 and p = new byte[10]
I want to make it an intptr. How do I get the size of the object for that?
IntPtr pObj = Marshal.AllocHGlobal(obj.len + sizeof(int));
Marshal.StructureToPtr(obj, pObj, true);
See what I did there. It seems too hard coded. If I do the below snippet;
IntPtr pObj = Marshal.AllocHGlobal(Marshal.SizeOf(obj));
Doing this returns the wrong size because obj.p returns a size of 4 and not 10. Because of the memory taken by the pointer pointing to the byte array is 4 bytes.
Is there a better way?
The return value is correct, p is a pointer, it takes 4 bytes.
You cannot leave it this way, there are two memory allocations. The marshaller allocated the memory for the array. It created a SAFEARRAY, a COM array type. Pretty unlikely that your C code is going to be happy with that. Declare it like this instead:
[StructLayout(LayoutKind.Sequential)]
public class OUR_MEM_STR {
public IntPtr p;
public int len;
};
And use Marshal.AllocHGlobal(10) to assign p. Don't forget to clean-up again.
Don't pass true to StructureToPtr(), the memory allocated by AllocHGlobal() isn't initialized. That's going to randomly crash your program. You must pass false.

StructLayout to convert byte array to short

Sorry about the horrible title but I honestly have know idea what I want nor what is wrong...
Basically I have a struct (well I have 250+ structs but they all follow the same idea) that looks like this:
[StructLayout(LayoutKind.Explicit)]
public struct GenericPacket
{
[FieldOffset(0)]
public byte[] data;
[FieldOffset(0)]
public short packetid;
}
The issue is that a byte array is a reference type and a short is a value type and it won't allow the fieldoffset to be set to the same memory location...
I would really hate to have to remove all the structs I've written just to do it a different way. So here is my question, how can I use this in a way that works. Basically what I'm going to do is this:
socket.Receive(buff, 0, 0, SocketFlags.None);
GenericPacket packet = new GenericPacket();
packet.data = buff;
Console.WriteLine(packet.packetid);
It refuses to even compile as it doesn't want to generate the struct /:
Before anyone suggests other methods, the reason I am doing this is it needs ultra high speeds... I could use a ByteReader and other methods (eg. BitConverter) but it needs to be a tad faster than that...
I started with Bitwise shifts but I needed a more 'dynamic' way to do it because after I have a packet ID I then read it with another struct, for example:
[StructLayout(LayoutKind.Explicit)]
public struct _03k
{
[FieldOffset(0)]
byte[] data;
[FieldOffset(0)]
short packetid;
[FieldOffset(2)]
short #null;
[FieldOffset(4)]
int major;
[FieldOffset(8)]
int minor;
[FieldOffset(12)]
int build;
}
Instead of having to have a lot of inline "Bitwise shits" I simply want an easy AND very fast way of doing this... Seems I got the fast just not the easy /:
PLEASE HELP! Unsafe code is ok, but prefer a managed version too.
FAIL :(: Just remembered you can convert Value types to Reference types by boxing them (casting to type object). This does however remove the REAL return type and says it just is an object, is there anyway with XML Documentation that you can lie about the return type?
DOESN'T WORK SADLY D:
UPDATE: OK, now I have:
public struct GenericPacket
{
public short packetid;
public static GenericPacket ReadUsingPointer(byte[] data)
{
unsafe
{
fixed (byte* packet = &data[0])
{
return *(GenericPacket*)packet;
}
}
}
}
But its a bit annoying having to call a method everytime to convert it :( Anyone got any more suggestions?
Thanks, JD
You just want to convert the first two bytes of the array to a short, how does that pose any performance problem?
packetid = ((short)data[0] << 8) | data[1];
Or the other way around if you want the other endianess.
EDIT:
So you want to parse multiple fields. Don't reinvent the wheel, then. Use Google's Protocol Buffers, it's very fast and efficient, and I doubt you'd encounter performance issues with it. Here's a .NET port.
I've just found you can use fixed araays in structs:
[StructLayout(LayoutKind.Explicit)]
unsafe struct Union
{
[FieldOffset(0)]
public short x;
[FieldOffset(0)]
public fixed byte y[2];
}
The initialization:
var u = new Union();
byte[] y = new byte[2]; //your original array here
int len = y.Length;
unsafe
{
fixed (byte* s= y)
{
byte* source = s;
byte* dest = u.y;
for (int i = 0; i < len; i++)
{
*dest++ = *source++;
}
}
}
In this particular example you could do this:
[StructLayout(LayoutKind.Explicit)]
struct byte_array
{
[FieldOffset(0)]
public byte byte1;
[FieldOffset(1)]
public byte byte2;
[FieldOffset(0)]
public short packetid;
}
But in general, Buffer.BlockCopy is probably a better solution.
What I finally settled on was a struct with an implicit conversion operator that called the ReadByPointer method.
public struct GenericPacket
{
public short packetid;
public static GenericPacket ReadUsingPointer(byte[] data)
{
unsafe
{
fixed (byte* packet = &data[0])
{
return *(GenericPacket*)packet;
}
}
}
static public implicit operator GenericPacket(byte[] value)
{
return GenericPacket.ReadUsingPointer(value);
}
}
This allows you to do the following:
GenericPacket packet = bufferFromReceive;
Thanks -jD

C#: marshalling a struct that contains arrays

I am doing some C# interop work. I have the following struct:
#pragma pack(push,1)
typedef struct
{
unsigned __int64 Handle;
LinkType_t Type;
LinkState_t State;
unsigned __int64 Settings;
signed __int8 Name[MAX_LINK_NAME];
unsigned __int8 DeviceInfo[MAX_LINK_DEVINFO];
unsigned __int8 Reserved[40];
} LinkInfo_t;
This is my attempt to convert it into a C# struct:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct LinkInfo_t
{
[MarshalAs(UnmanagedType.U8)]
public UInt64 Handle;
[MarshalAs(UnmanagedType.I4)]
public LinkType_t Type;
[MarshalAs(UnmanagedType.I4)]
public LinkState_t State;
[MarshalAs(UnmanagedType.U8)]
public UInt64 Settings;
[MarshalAs(UnmanagedType.LPStr, SizeConst = MAX_LINK_NAME)]
public string Name;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_LINK_DEVINFO, ArraySubType = UnmanagedType.U1)]
public byte[] DeviceInfo;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 40, ArraySubType = UnmanagedType.U1)]
public byte[] Reserved;
}
However, whenever I initialize the struct the Name, DeviceInfo, and Reserved fields are all set to null. How do I fix this?
For the arrays, try to use the fixed keyword:
public fixed byte DeviceInfo[MAX_LINK_DEVINFO];
public fixed byte Reserved[40];
whenever I initialize the struct the
Name, DeviceInfo, and Reserved fields
are all set to null
This is correct, and your definition looks OK to me (BTW, you don't need [MarshalAs] on the primitive fields, the default behaviour is to do what you specified there). Because your array fields are null, the marshaler won't do anything about them when marshaling your struct to unmanaged memory, but it's going to create the strings and arrays when unmarshaling.
What Anton Tykhyy says is correct. I just want to clarify with some examples. Using 'fixed' works, but that forces you to use 'unsafe' as well. I like to avoid using unsafe wherever possible. Using Marshal is a way to get around that.
First, let's say that I have a library that was created in C with the following definitions.
typedef struct {
int messageType;
BYTE payload[60];
} my_message;
/**
* \param[out] msg Where the message will be written to
*/
void receiveMessage(my_message *msg);
/*
* \param[in] msg The message that will be sent
*/
void sendMessage(my_message *msg);
In C#, the following structure would be equivalent to the one in C.
[StructLayout(LayoutKind.Sequential, Size = 64), Serializable]
struct my_message
{
int messageType;
[MarshalAs(UnmanagedType.ByValArray,SizeConst = 60)]
byte[] payload;
public initializeArray()
{
//explicitly initialize the array
payload = new byte[60];
}
}
Since the msg in receiveMessage() is documented as [out], you don't need to do anything special to the array in the structure before passing it to the function. i.e.:
my_message msg = new my_message();
receiveMessage(ref msg);
byte payload10 = msg.payload[10];
Since the msg in sendMessage() is documented as [in], you will need to fill the array before calling the function. Before filling the array, the array needs to be explicitly instantiated before using it. i.e.:
my_message msg = new my_message();
msg.initializeArray();
msg.payload[10] = 255;
sendMessage(ref msg);
Calling initializeArray() should instantiate the array in the previously allocated space created within the struct for this array.

Passing pointers from unmanaged code

I have a C# project that imports a C dll, the dll has this function:
int primary_read_serial(int handle, int *return_code, int *serial, int length);
I want to get access to the serial parameter. I've actually got it to return one character of the serial parameter, but I'm not really sure what I'm doing and would like to understand what is going, and of course get it working.
So, I'm very sure that the dll is being accessed, other functions without pointers work fine. How do I handle pointers? Do I have to marshal it? Maybe I have to have a fixed place to put the data it?
An explanation would be great.
Thanks!
Richard
You will have to use an IntPtr and Marshal that IntPtr into whatever C# structure you want to put it in. In your case you will have to marshal it to an int[].
This is done in several steps:
Allocate an unmanaged handle
Call the function with the unamanged array
Convert the array to managed byte array
Convert byte array to int array
Release unmanaged handle
That code should give you an idea:
// The import declaration
[DllImport("Library.dll")]
public static extern int primary_read_serial(int, ref int, IntPtr, int) ;
// Allocate unmanaged buffer
IntPtr serial_ptr = Marshal.AllocHGlobal(length * sizeof(int));
try
{
// Call unmanaged function
int return_code;
int result = primary_read_serial(handle, ref return_code, serial_ptr, length);
// Safely marshal unmanaged buffer to byte[]
byte[] bytes = new byte[length * sizeof(int)];
Marshal.Copy(serial_ptr, bytes, 0, length);
// Converter to int[]
int[] ints = new int[length];
for (int i = 0; i < length; i++)
{
ints[i] = BitConverter.ToInt32(bytes, i * sizeof(int));
}
}
finally
{
Marshal.FreeHGlobal(serial_ptr);
}
Don't forget the try-finally, or you will risk leaking the unmanaged handle.
If I understand what you're trying to do, this should work for you.
unsafe
{
int length = 1024; // Length of data to read.
fixed (byte* data = new byte[length]) // Pins array to stack so a pointer can be retrieved.
{
int returnCode;
int retValue = primary_read_serial(0, &returnCode, data, length);
// At this point, `data` should contain all the read data.
}
}
JaredPar gives you the easy way to do it however, which is just to change your external function declaration so that C# does the marshalling for you in the background.
Hopefully this gives you an idea of what's happening at a slightly lower level anyway.
When writing your P/invoke declaration of that function, try using keyword ref for the pointer parameters like this:
[DllImport("YouDll.dll", EntryPoint = "primary_read_serial")]
public static extern int primary_read_serial(int, ref int, ref int, int) ;
I'm not sure if you need to specify the parameters' name in C#. And remember, when calling that method, you will also have to use ref in the arguments you're passing by reference.

Categories

Resources