correct marshalling of c++ structure in c# - c#

I have this structure in C++ that I need to be converted to C#, so I can create this structure from a byte[].
struct TRANS_RECORD_DATA {
int size, code;
int ThrowItemCount;
int ItemCount;
int ItemSubStart;
int DataSize;
BYTE Data[sizeof(sRECORD_ITEM) * 200]; // sizeof(sRECORD_ITEM) = 548
};
The C# version:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct TRANS_RECORD_DATA {
public int size, code;
public int ThrowItemCount;
public int ItemCount;
public int ItemSubStart;
public int DataSize;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 548*200)]
public byte[] Data;
};
I am using this generic function to give me the structure from the byte array:
T ByteArrayToStructure<T>(byte[] bytes) where T : struct
{
GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
T stuff = (T) Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof (T));
handle.Free();
return stuff;
}
But it is giving me:
AccessViolation Exception.
I belive I know why but I don't know how to solve it. The byte[] I have, that needs to be mapped into the struct, does not always have the Data member with size of 548*200. This number is a maximum. But seems that that GenericMethod I use, always try to create the struct with that Data always 548*200, and then it will obviously throw an AccessViolation because the data to be mapped has ended already.
For example this code:
var bytes = new byte[26];
var structure = ByteArrayToStructure<TRANS_RECORD_DATA>(bytes);
Should return a TRANS_RECORD_DATA with all those int members with 0 value, and finally that byte[] Data would have only the remaining two bytes. (26 - 24 = 2). But it seems that it tries to create the full 548*200 byte[] all the time, and then cause the access violation.
Is there a way to solve this ?

So as you explaining it, regardless of C definition for Data array being 200*548 bytes long, it is not actually fully allocated or filled by an external unmanaged code you call.
this way your only workaround is NOT to define Data in your C# struct definition:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct TRANS_RECORD_DATA {
public int size, code;
public int ThrowItemCount;
public int ItemCount;
public int ItemSubStart;
public int DataSize;
};
and after reading DataSize re-interpret remainder of the byte array as that data structure.
you can still use series of calls to Marshal.PtrToStructure:
GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
TRANS_RECORD_DATA stuff = (TRANS_RECORD_DATA) Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof (TRANS_RECORD_DATA));
var items = new List<sRECORD_ITEM>(stuff.ItemCount);
for (int i = 0; i < stuff.ItemCount; ++i)
{
var ptr = handle.AddrOfPinnedObject().Add(i*548)
sRECORD_ITEM item = (sRECORD_ITEM)Marshal.PtrToStructure(ptr,typeof(sRECORD_ITEM));
}
I am pretty sure this should do the trick.

Related

Unmarshaling a structure containing a variable-size array of a structure

I'm trying to unmarshall an array of variable length of a structure nested inside another structure as in the following code:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct CardInfoRequest
{
public ulong CardId;
public byte AppListLength;
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct)]
public CardApp[] AppList;
}
[Serializable()]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct CardApp
{
public ulong CardAppId;
public byte SomeInformation;
}
And I am unmarshalling it by doing:
var lDataPointer = Marshal.AllocHGlobal(pSize);
Marshal.Copy(pData, 0, lDataPointer, pSize);
var lResult = Marshal.PtrToStructure(lDataPointer, typeof(CardInfoRequest));
Marshal.FreeHGlobal(lDataPointer);
Where pData is a byte array containing the marshalled structure and pSize is its size in runtime (18 when the array has one item, 27 when the array has two items and so forth...).
However, no matter the size in bytes of the stream, whenever I unmarshall it I am always getting AppList.Length == 1.
Can I unmarshall it correctly? Should I do it byte by byte?
Thanks in advance!
The pinvoke marshaller has no idea what size array it needs to create. A fixed-size buffer cannot work either. You have to do it in two steps, first unmarshal the first 2 members, you now know the array size from AppListLength. Create the array, then unmarshal the array elements one by one in a loop.
So roughly (untested)
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal struct CardInfoRequestHeader
{
public ulong CardId;
public byte AppListLength;
}
public struct CardInfoRequest
{
public CardInfoRequestHeader Header;
public CardApp[] AppList;
}
...
var req = new CardInfoRequest();
req.Header = (CardInfoRequestHeader)Marshal.PtrToStructure(pData, typeof(CardInfoRequestHeader));
req.AppList = new CardApp(req.Header.AppListLength);
pData += Marshal.SizeOf(CardInfoRequestHeader);
for (int ix = 0; ix < req.AppList.Length; ++ix) {
req.AppList = (CardInfo)Marshal.PtrToStructure(pData, typeof(CardInfo));
pData += Marshal.SizeOf(CardInfo);
}
Beware the ulong and Pack = 1 are Red Flags, unmanaged data rarely looks like that. The code snippet does make the hard assumption that Pack=1 is accurate.

Load a C-Dll in C# with struct array as parameter

I try to import functions from my C-Dll. A function has a struct array as a parameter. The struct will be filled in the function.
struct test
{
int test1;
int test2;
};
void FillStruct( struct test stTest[], int size)
{
if(size == 2){
stTest[0].test1 = 5;
stTest[0].test2 = 5;
stTest[1].test1 = 2;
stTest[1].test2 = 2;
}
}
The method FillStruct should be used in C#.
I think I have to create the struct in C#. Must I marshal the struct if I use memcpy in the Fillstruct?
struct Test
{
public int test1;
public int test2;
}
[DllImport("mydll", CallingConvention = Cdecl)]
public static extern void FillStruct( Test[] stTest, int size);
[...]
var test = new Test[n];
FillStruct(test, test.Length);
I don't see the problem here. It does not matter what you do with the memory in your c code: as long as you don't cause buffer overflows, you can read, write an copy all you want. c# arrays are just the type and length of the array, followed by the data. When you use p/invoke with simple structs, a pointer to the first element in the original array will be passed to your c code.

Initialization of a struct in C#?

Some code I'm modifying makes extensive use of structs to communicate with some factory equipment by loading them to or from byte arrays.
Here's a made-up ultra-simplified example of such a struct.
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct K_FOO
{
public byte a_byte; // 1 byte
public BYTE3_TYPE b3; // 3 bytes
public int num; // happens to be 4 bytes
}
BYTE3_TYPE looks like this...
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public class BYTE3_TYPE
{
[System.Runtime.InteropServices.MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public byte[] L = new byte[3];
}
If I just do a
K_FOO origFoo = new K_FOO();
the int and the byte are initialized to 0's, I assume because they are native types, but the byte array b3 is *un*initialized - all nulls. I have to explicitly load it, for example,
BYTE3_TYPE b3 = new BYTE3_TYPE();
origFoo.b3 = b3;
... and I couldn't think of any alternative because structs don't take parameterless constructors but the real structs are huge.
But I noticed something interesting. We have routines to copy these structs to and from byte arrays. For example . . .
public static T ByteArrayToStructure<T>(byte[] buffer) where T : struct
{
int length = buffer.Length;
IntPtr ptr = Marshal.AllocHGlobal(length); // allocate (length) bytes
Marshal.Copy(buffer, 0, ptr, length); // copies into UNmanaged space
T result = (T)Marshal.PtrToStructure(ptr, typeof(T));
Marshal.FreeHGlobal(ptr);
return result;
}
...and if I call it...
K_FOO retFoo = ByteArrayToStructure<K_FOO>(buffer.bytes);
... the resulting struct is returned fully initialized, the bytes in the byte array have all had their space allocated so they could be loaded, apparently in the PtrToStructure() call. This implies .Net "knows" how to initialize such a struct. So is there some way to get .Net to do that for me so I can avoid writing hundreds of lines of explicit initialization code? Thanks in advance!
If you make your BYTE3_TYPE a struct instead of a class, the default constructor (calling K_FOO origFoo = new K_FOO();) will initialize the entire thing to zero correctly.
This is also likely the correct approach if you're trying to match existing specifications and pass this to custom hardware.

Getting Array of struct from IntPtr

I have some struct like this
struct MyStruct
{
public int field1;
public int field2;
public int field3;
}
and I have pointer to array of this struct.
So, I need to get array from this pointer.
I'm tried to using Marshal.PtrToStructure, but i had memory reading error.
This is my methode:
public MyStruct[] GetArrayOfStruct(IntPtr pointerToStruct, int length)
{
var sizeInBytes = Marshal.SizeOf(typeof(TCnt));
MyStruct[] output = new MyStruct[length];
for (int i = 0; i < length; i++)
{
IntPtr p = new IntPtr((pointerToStruct.ToInt32() + i * sizeInBytes));
output[i] = (MyStruct)System.Runtime.InteropServices.Marshal.PtrToStructure(p, typeof(MyStruct));
}
return output;
}
So, what am i doing wrong ?
This function worked for me, assuming that the size of the struct is fixed:
public static void MarshalUnmananagedArray2Struct<T>(IntPtr unmanagedArray, int length, out T[] mangagedArray)
{
var size = Marshal.SizeOf(typeof(T));
mangagedArray = new T[length];
for (int i = 0; i < length; i++)
{
IntPtr ins = new IntPtr(unmanagedArray.ToInt64() + i * size);
mangagedArray[i] = Marshal.PtrToStructure<T>(ins);
}
}
Two problems. You use TCnt instead of MyStruct in the Marshal.SizeOf() call. Your IntPtr arithmetic cannot work on a 64-bit machine, you must use IntPtr.ToInt64() or cast to (long).
Just getting the wrong IntPtr or length is certainly a possibility too of course. Use Debug + Windows + Memory + Memory 1 and put "pointerToStruct" in the Address box for basic verification.
Structs in C and C# are not the same thing. One of the differences is that in C# you have to explicitly demand that your struct should be sequentially laid out. If you didn't write
[StructLayout(LayoutKind.Sequential)] or [StructLayout(LayoutKind.Explicit)] attribute to your structure I don't believe that you can manage it in this way. Microsoft states that PtrToStructure is to be used to convert structures from unmanaged to managed memory
You should test if adding this attributes to your struct helps, If it doesn't yet help try allocating memory with Marshal.AllocHGlobal(IntPtr) and use Marshal.Copy to init your structure and then try using PtrToStructure. If this works then you can't use PtrToStructure with managed memory

converting between struct and byte array

his question is about converting between a struct and a byte array. Many solutions are based around GCHandle.Alloc() and Marshal.StructureToPtr(). The problem is these calls generate garbage. For example, under Windows CE 6 R3 about 400 bytes of garbarge is made with a small structure. If the code below could be made to work the solution could be considered cleaner. It appears the sizeof() happens too late in the compile to work.
public struct Data
{
public double a;
public int b;
public double c;
}
[StructLayout(LayoutKind.Explicit)]
public unsafe struct DataWrapper
{
private static readonly int val = sizeof(Data);
[FieldOffset(0)]
public fixed byte Arr[val]; // "fixed" is to embed array instead of ref
[FieldOffset(0)]
public Data; // based on a C++ union
}
You can in fact use the constant value 1 as the size of the fixed array in DataWrapper. There is no bounds checking when accessing an unsafe fixed array nor is there any mechanism to request its length at runtime. The presence of the Data member at offset 0 is enough to ensure that you can actually read and write sizeof(Data) bytes to/from DataWrapper.Arr.
With that said, a more straight forward solution would be to cast a Data* pointer to byte* as follows:
unsafe {
Data d = new Data();
d.a = 125.5;
d.b = 0x7FEEDDEE;
d.c = 130.0;
byte* bytes = (byte*)&d;
for (int i = 0; i < sizeof(Data); i++) {
Console.WriteLine(i + ": " + bytes[i]);
}
}

Categories