I searched this question but google gave nothing.
Is there any way to marshal array of arrays?
//C
typedef struct SomeStruct
{
float matrix[7][12];
} SomeStruct;
//C#
public struct SomeStruct
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = ???)]
public float[][] matrix;
}
You have to use a linear array in the C# code:
public struct SomeStruct
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 7*12)]
public float[] matrix;
}
You'll need to convert from a 2D index to a linear index for convenience.
int LinearIndex(int i, int j)
{
return i*12 + j;
}
Related
I have to translate a C++ DLL into C# to use it in a project. Here is the part that causes me trouble. This is the C++ code of the header of the DLL :
struct initiate_rb
{
unsigned char Rem_Add;
unsigned char Features_Supported_1_m;
struct add_addr_param Add_Addr_Param;
};
struct add_addr_param
{
unsigned char D_Type;
unsigned char D_Len;
struct
{
unsigned char Network_Address[6];
unsigned short MAC_Address_Len;
unsigned char * MAC_Address;
} S_Addr;
};
I am not sure how to handle this in C#. Here is what I have achieved so far :
[StructLayout(LayoutKind.Sequential, Pack = 1)]
unsafe struct initiate_rb
{
public byte Rem_Add;
public byte Features_Supported_1_m;
public add_addr_param Add_Addr_Param;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
unsafe struct add_addr_param
{
public byte D_Type;
public byte D_Len;
[StructLayout(LayoutKind.Sequential)]
public struct S_Addr
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public byte[] Network_Address;
public ushort MAC_Address_Len;
public byte* MAC_Address;
};
}
I used Pack = 1 because in my DLL header file there is the line #pragma pack (1)
The problem is, this doesn't work when I have to use this struct. It returns me an error code.
So first, concerning this struct translation, am I doing it right ?
Thanks for helping.
I'm not sure about the attributes but I believe the second struct shoudn't be nested in C# because the one in C++ defines a variable of the struct's type just after the closing parentheses.
[StructLayout(LayoutKind.Sequential, Pack = 1)]
unsafe struct dpc2_add_addr_param
{
public byte D_Type;
public byte D_Len;
public S_Addr S_Addr;
}
[StructLayout(LayoutKind.Sequential)]
public struct S_Addr
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public byte[] Network_Address;
public ushort MAC_Address_Len;
public byte* MAC_Address;
};
In fact I resolved this problem. The problem was that I was using a constructor for my initiate_rb struct
unsafe struct initiate_rb
{
public byte Rem_Add;
public byte Features_Supported_1_m;
public add_addr_param Add_Addr_Param;
public initiate_rb(int networkAddressLength) : this()
{
Rem_Add = (byte)0;
Features_Supported_1_m = (byte)0;
add_addr_param = new Add_Add_Param(networkAddressLength);
}
}
and this constructor for add_addr_param :
[StructLayout(LayoutKind.Sequential, Pack = 1)]
unsafe struct add_addr_param
{
public byte D_Type;
public byte D_Len;
[StructLayout(LayoutKind.Sequential)]
public struct S_Addr
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public byte[] Network_Address;
public ushort MAC_Address_Len;
public byte* MAC_Address;
};
public add_addr_param(int networkAddressLength) : this()
{
D_Type = (byte)0;
D_Len = (byte)0;
S_Addr.Network_Address = new byte[6]; //problem with this line
}
}
This was the problem. Since I remove this line, the code was operating correctly. In fact I didn't need constructors since all parameters have to be set to 0.
Thanks for helping!
public class TestSerializer
{
public static byte[] StructureToByteArray(Test[] array)
{
int size = Marshal.SizeOf(array.Length);
byte[] arr = new byte[size];
IntPtr ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(array, ptr, true);//error
Marshal.Copy(ptr, arr, 0, size);
Marshal.FreeHGlobal(ptr);
return arr;
}
When I write the above code I got this error. But my structure is like this :
public struct Test
{
[FieldOffset(0)]
// public string name;
public Byte icode;
[FieldOffset(1)]
public Byte method;
[FieldOffset(2)]
public Byte wav;
[FieldOffset(3)]
public Byte wav2;
[FieldOffset(4)]
public Byte unit;
[FieldOffset(5)]
public Byte temp;
[fieldOffset(6)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public byte[] itemname;
//public float factor;
}
Test[] array = new Test[200];
I want to convert this 'array' to byte array and I also have a doubt in the following line
int size = Marshal.SizeOf(array.Length);
is it possible like writing this
array[1].itemname=asd;
Try adding attribute [StructLayout(LayoutKind.Explicit)] to your struct test.
Marshal.SizeOf(array.Length) resolves to Marshal.SizeOf(200) - and 200 is an int32, for which this reports size of 4. You need to do array.Length * Marshal.SizeOf(typeof(Test))
The problem is that array of type Test is not blittable according to the documentation (while the struct itself is, if you've marked it with [StructLayout(LayoutKind.Explicit)] or LayoutKind.Sequential) and you need to do it yourself in a loop.
public static byte[] StructureToByteArray(Test[] array)
{
int structSize = Marshal.SizeOf(typeof(Test));
int size = array.Length * structSize;
byte[] arr = new byte[size];
IntPtr ptr = Marshal.AllocHGlobal(size);
for (int i = 0; i < array.Length; i++ )
Marshal.StructureToPtr(array[i], ptr+i*structSize, true);//error
Marshal.Copy(ptr, arr, 0, size);
Marshal.FreeHGlobal(ptr);
return arr;
}
Arrays must start on 4-byte boundaries, so the struct should look like this:
[StructLayout(LayoutKind.Explicit)]
public struct Test
{
[FieldOffset(0)]
public byte icode;
[FieldOffset(1)]
public byte method;
[FieldOffset(2)]
public byte wav;
[FieldOffset(3)]
public byte wav2;
[FieldOffset(4)]
public byte unit;
[FieldOffset(5)]
public byte temp;
[FieldOffset(8)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public byte[] itemname;
}
the string assigment can be done via encoding string into byte array and copying into the struct as follows:
Test[] array = new Test[200];
byte[] temp = Encoding.UTF8.GetBytes("asd");
array[0].itemname = new byte[20];
Array.Copy(temp, array[0].itemname, temp.Length);
I had idea about message parsing in serial communication, there are many kind of packets which have different form. but they're all sent by byte array.
so I thought using union to parse each message.
but it's not working well.
following code is the sample code which I'm In Error
[StructLayout(LayoutKind.Explicit, Size=12)]
public struct UnionPacket
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst=12)]
public byte[] data;
[FieldOffset(0)]
public float Time;
[FieldOffset(4)]
public Int16 CoordX;
[FieldOffset(6)]
public Int16 CoordY;
[FieldOffset(8)]
public byte Red;
[FieldOffset(9)]
public byte Green;
[FieldOffset(10)]
public byte Blue;
[FieldOffset(11)]
public byte Alpha;
}
if this were possible, it would be very happy, but it isn't. This code occurs TypeLoadException "... because it contains an object field at offset 0 that is incorrectly aligned or overlapped by a non-object field."
So I changed some code like this
[StructLayout(LayoutKind.Explicit, Size= 12)]
public struct UnionPacket
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)]
[FieldOffset(0)]
public byte[] data;
//[FieldOffset(0)]
//public float Time;
[FieldOffset(4)]
public Int16 CoordX;
[FieldOffset(6)]
public Int16 CoordY;
[FieldOffset(8)]
public byte Red;
[FieldOffset(9)]
public byte Green;
[FieldOffset(10)]
public byte Blue;
[FieldOffset(11)]
public byte Alpha;
}
for test, i just disabled Time field which offset is 0, and this didn't occur Exception. but, if i change other fields, it doesn't change byte array. i think that byte array's real memory location is allocated in other heap, so it cannot be done.
Is there any way to solve this problem in C#? only C++ or C can solve this?
and if i use this with inheritance, is it possible?
P.S. Sorry about my poor english
I'm not 100% sure what you are trying to do, but:
first approach, with array stored inside of a structure is possible, if you use it with fixed/unsafe keywords. I don't know if this is possible for you. So having code like this should work:
[StructLayout(LayoutKind.Explicit, Size = 12)]
public unsafe struct UnionPacket
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)]
[FieldOffset(0)]
public fixed byte data[12];
[FieldOffset(0)]
public float Time;
[FieldOffset(4)]
public Int16 CoordX;
[FieldOffset(6)]
public Int16 CoordY;
[FieldOffset(8)]
public byte Red;
[FieldOffset(9)]
public byte Green;
[FieldOffset(10)]
public byte Blue;
[FieldOffset(11)]
public byte Alpha;
}
Of course, then you have to enable unsafe code blocks in VS and use wrap your structure around with it:
unsafe
{
var u = new UnionPacket();
for (byte i = 0; i < 12; i++)
{
u.data[i] = i;
}
Console.WriteLine(u.Time);
Console.WriteLine(u.CoordX);
Console.WriteLine(u.CoordY);
Console.WriteLine(u.Red);
Console.WriteLine(u.Green);
Console.WriteLine(u.Blue);
Console.WriteLine(u.Alpha);
}
Other approach would be - just forgetting about this array inside struct, and handle parsing using Marshal.Copy:
[StructLayout(LayoutKind.Explicit, Size = 12)]
public struct UnionPacket2
{
[FieldOffset(0)]
public float Time;
[FieldOffset(4)]
public Int16 CoordX;
[FieldOffset(6)]
public Int16 CoordY;
[FieldOffset(8)]
public byte Red;
[FieldOffset(9)]
public byte Green;
[FieldOffset(10)]
public byte Blue;
[FieldOffset(11)]
public byte Alpha;
}
static void Main(string[] args)
{
var len = Marshal.SizeOf(typeof(UnionPacket2));
var buffer = new byte[len];
for (byte i = 0; i < len; i++)
{
buffer[i] = i;
}
var ptr = Marshal.AllocHGlobal(len);
Marshal.Copy(buffer, 0, ptr, len);
var u = (UnionPacket2)Marshal.PtrToStructure(ptr, typeof(UnionPacket2));
Marshal.FreeHGlobal(ptr);
Console.WriteLine(u.Time);
Console.WriteLine(u.CoordX);
Console.WriteLine(u.CoordY);
Console.WriteLine(u.Red);
Console.WriteLine(u.Green);
Console.WriteLine(u.Blue);
Console.WriteLine(u.Alpha);
}
I have a array of structs returned from PInvoke, and it returns the array fine if the struct just contains int or float, but when i try to return an array of char it starts to get messy, i have tried with returning an IntPtr, but that has not been successfull. Any ideas how i can get this working?
C code
struct return_part {
int partid;
int numcomp;
int parttype;
char partname[100];
};
extern int return_parts(return_part ** array, int * arraySizeInElements) {
int partcount = 0;
struct list_part *currentnode;
currentnode = head;
struct section_list *section;
struct return_part *temppart;
while (currentnode != NULL) {
partcount++;
currentnode = currentnode->next;
}
currentnode = head;
*arraySizeInElements = partcount;
int bytesToAlloc = sizeof(return_part) * (*arraySizeInElements);
return_part * a = static_cast<return_part *>(CoTaskMemAlloc(bytesToAlloc));
*array = a;
int q = 0;
while (currentnode != NULL) {
struct return_part tmp;
tmp.partid = currentnode->partid;
tmp.numcomp = currentnode->numcomp;
strcpy(tmp.partname, currentnode->partname);
tmp.parttype = currentnode->parttype;
a[q] = tmp;
q++;
currentnode = currentnode->next;
}
return 0;
}
C# Code
[StructLayout(LayoutKind.Sequential)]
public struct return_part {
public int partid;
public int numcomp;
public int parttype;
public char partname;
};
internal static class UnsafeNativeMethods
{
[DllImport(_dllLocation, CallingConvention = CallingConvention.Cdecl)]
public static extern int return_parts([MarshalAs(UnmanagedType.LPArray,
SizeParamIndex = 1)] out return_part[] array, out int arraySizeInElements);
}
public static ReturnPoint[] getpoints(int partid) {
return_parts[] parts;
int size;
int result = UnsafeNativeMethods.return_parts(out parts, out size)
}
An array of characters, to unmanaged code, is just a string, so you have two options here. Which one you use depends on how you actually need to use the character data when it's back in C#-land:
Marshal it as an array. See this article for how arrays embedded into structures get marshalled (EDIT: this needs to be ByValArray, not LPArray as I originally indicated; Thanks #Hans):
[StructLayout(LayoutKind.Sequential)]
public struct return_part {
public int partid;
public int numcomp;
public int parttype;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=100)]
public char[] partname;
};
Marshal it as a string. See this article for how to marshal strings contained within structures.
[StructLayout(LayoutKind.Sequential)]
public struct return_part {
public int partid;
public int numcomp;
public int parttype;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=100)]
public string partname;
};
On the C# side, your structure has a single character as partname. Try using either:
char[] partname; //-- A character array
byte[] partname; //-- A byte array
string partname; //-- A string
I'd tend to prefer the string if that works. The byte might work because byte in C# lines up more logically with char in C. The character array might also work because the character in C# logically represents an actual single character (intended use), something C doesn't really have (short of unsigned integer types).
I'm doing C++ --> C# interop stuff and I have a bunch of structs that contain each other like Matryoshka dolls. The problem is that one of these 'nestings' takes the form of a fixed length array:
typedef struct tagBIRDREADING
{
BIRDPOSITION position;
BIRDANGLES angles;
BIRDMATRIX matrix;
BIRDQUATERNION quaternion;
WORD wButtons;
}
BIRDREADING;
typedef struct tagBIRDFRAME
{
DWORD dwTime;
BIRDREADING reading[BIRD_MAX_DEVICE_NUM + 1];
}
BIRDFRAME;
Following the hallowed teachings of Eric Gunnerson, I did the following in C#:
[StructLayout(LayoutKind.Sequential, Pack = 0)]
public struct BIRDREADING
{
public BIRDPOSITION position;
public BIRDANGLES angles;
public BIRDMATRIX matrix;
public BIRDQUATERNION quaternion;
public ushort wButtons;
}
[StructLayout(LayoutKind.Sequential, Size = 127)]
public struct BIRDREADINGa
{
public BIRDREADING reading;
}
public struct BIRDFRAME
{
public uint dwTime;
public BIRDREADINGa readings;
}
My question is, how do I access each of the 127 instances of BIRDREADING contained within BIRDREADINGa and therefore BIRDFRAME? Or have I gone terrible wrong?
I think you just want this:
[StructLayout(LayoutKind.Sequential)]
public struct BIRDFRAME
{
public uint dwTime;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=127)]
public BIRDREADING[] readings;
}
To access all those instances without using an array you need to use an unsafe block to grab the address of the "fake array" you set up, and then use pointer arithmetic. It's going to get ugly:
public struct BIRDREADINGa
{
public BIRDREADING reading;
public BIRDREADING GetReading(int index)
{
unsafe
{
fixed(BIRDREADING* r = &reading)
{
return *(r + index);
}
}
}
}