i am desperate to get a complex c datatype correctly marshaled for C#. I already read all the other posts regarding that topic and i am running out of ideas although it seems to me to be quite close to the solution.
The main issue is that the c-struct is having a union of two different struct types. On with only basic types and one including arrays, which causes trouble.
I have created an example to showcase the situation. The struct worring me is called dataStreamConfiguration
. The c code looks like this, the struct in question is at the bottom of the example c-code:
#include "stdint.h"
#include "stddef.h"
typedef enum viewCapEnum {
X = 0,
}viewCapEnum;
typedef struct fraction{
uint8_t nominator;
uint8_t denominator;
}fraction;
typedef struct comSize{
fraction A;
fraction B;
}comSize;
typedef enum someEnum{
A = 0,
B,
C,
D
}someEnum;
typedef struct someSize{
fraction X;
fraction Y;
}someSize;
typedef struct featTemplateCap{
someEnum A;
someSize Size;
}featTemplateCap;
typedef struct featTypeCap{
someEnum AB;
someSize CD;
}featTypeCap;
typedef struct viewCap{
uint8_t A;
uint8_t B;
size_t BCount;
viewCapEnum ViewCapEnum[50];
comSize MinComSize;
size_t CapaCount;
featTemplateCap TemplCap[14];
size_t TypeCapaCount;
featTypeCap FeatTypeCapa[14];
uint8_t GCount;
}viewCap;
typedef struct featX{
uint16_t A;
uint16_t B;
int16_t C;
int16_t D;
}featX;
typedef struct pathCap{
uint8_t Count;
uint8_t Size;
featX Feat;
}pathCap;
typedef struct dataStreamConfiguration{
size_t FeatureSelector;
union {
viewCap AsViewCap;
pathCap AsPathCap;
}dataStream;
}dataStreamConfiguration;
The marshalling of datatypes between C and the C# world is working for almoust all but this dataStreamConfiguration struct. So I got the following code, where instead of mapping (somehow) a union to c# both datatypes have been put one after another. So clearly this was not working correctly. It looked like that:
public unsafe struct UInt32Struct {
public UInt32 value;
}
public unsafe struct fraction{
public Byte nominator;
public Byte denominator;
}
public unsafe struct comSize{
public fraction A;
public fraction B;
}
public unsafe struct someSize{
public fraction X;
public fraction Y;
}
public unsafe struct featTemplateCap{
public UInt32 A;
public someSize Size;
}
public unsafe struct featTypeCap{
public UInt32 AB;
public someSize CD;
}
public unsafe struct viewCap{
public Byte A;
public Byte B;
public UInt16 BCount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 50]
public UInt32Struct[] ViewCapEnum;
public comSize MinComSize;
public UInt16 CapaCount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 14]
public featTemplateCap[] TemplCap;
public UInt16 TypeCapaCount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 14]
public featTypeCap FeatTypeCapa[14];
public Byte GCount;
}
public unsafe struct featX{
public UInt16 A;
public UInt16 B;
public Int16 C;
public Int16 D;
}
public unsafe struct pathCap{
public Byte Count;
public Byte Size;
public featX Feat;
}
public unsafe struct dataStreamConfiguration{
public UInt16 FeatureSelector;
public viewCap AsViewCap;
public pathCap AsPathCap;
}
So to get the union to c# I came across the LayoutKind.Explicit and did following:
[StructLayout(LayoutKind.Explicit)]
public unsafe struct dataStreamConfiguration{
[FieldOffset(0)]
public UInt16 FeatureSelector;
[FieldOffset(2)]
public viewCap AsViewCap;
[FieldOffset(2)]
public pathCap AsPathCap;
}
This was not working due to the alignment of the object types, which are incorrectly aligned or overlapped by non-object fields.. I googled a lot. Adjusted the alignment to 4 by [StructLayout(LayoutKind.Explicit, Pack=4)]
. However, 4,8,16,32, whatever alignment i have choosen, I got the same error during runtime - incorrectly aligned or overlapped issue.
Next thing I did - I felt quite lucky about - was to unroll all the arrays in C# datatype for all the arrays in viewCap
struct. As I have read that this might cause alignment issues. Well, It didn't work. And I found that the memory has been modified, so I could not find the values I have seen in C appearing now in C#. Most of the values in C# are 0. Ok.
To get rid of this memory modification stuff I put in C# to all other structs [StructLayout(LayoutKind.Sequential)]
to keep the order of elements as they are in C. Sadly it didn't help much, I could not find the values of the c-struct in c# either. However, it was finally working, when I got rid of the union and deleted either AsViewCap
or AsPathCap
(my weak moment of blind rage). Ok, but that was not the solution.
Last help was having a try with IntPtr
, so i have created a new struct called dataStreamConfigurationPtr
:
public unsafe struct dataStreamConfigurationPtr{
public UInt16 FeatureSelector;
public void* Ptr;
}
[StructLayout(LayoutKind.Sequential)]
public unsafe struct dataStreamConfiguration{
public UInt16 FeatureSelector;
public viewCap AsViewCap;
public pathCap AsPathCap;
}
Instead of having an overlapping memory with StructLayout.Explicit
I used an void*
to point to the unmanaged memory location. For this I used the old struct definition to get the memory and instead having a union I took the first version where both types are laid out one over another. The idea was to use it like that:
MyFunction(dataStreamConfigurationPtr X, int Status){
//Create obj and appropraite space for data
dataStreamConfiguration DataStream = new dataStreamConfiguration();
DataStream.FeatureSelector = X.FeatureSelector;
unsafe{
IntPtr Ptr = new IntPtr(&X.Ptr);
DataStream.AsViewCap = Marshal.PtrToStructure<viewCap>(Ptr);
DataSteram.AsPathCap = Marshal.PtrToStructure<pathCap>(Ptr);
}
WCFCallback(DataStream, Status);
}
Now the IntPtr is pointing to the right memory, however, this works only for the first item of the structs. So for viewCap
the first item A
has its correct data whereas item B, BCount,.. all the other item seem to have at least misalligned values or accidental values.. I am quite desperate what to do now, i feel i am so close to a solution but have no idea how to get the other data of the struct from c to c#.
Any suggestions and comments are highly welcome!
Best regards, Tobias