C
C#2y ago
rick_o_max

✅ Interoperability between incompatible types

Hi! I'm trying to pass a class back-and-forth from C# to CPP. This class contains some members that I can't and don't need to translate to CPP types, but when I add these members to my interop classes, the application exits with a fatal error.
18 Replies
rick_o_max
rick_o_maxOP2y ago
This is my CPP class:
typedef struct TModel
{
char* Name;
bool Used;
Vector3 LocalPosition;
Quaternion LocalRotation;
Vector3 LocalScale;
bool Visibility;
//void* Parent;
//void* Children;
//void* Bones;
//bool IsBone;
//void* GeometryGroup;
//void* BindPoses;
//void* MaterialIndices;
//void* UserProperties;
} Model;
typedef struct TModel
{
char* Name;
bool Used;
Vector3 LocalPosition;
Quaternion LocalRotation;
Vector3 LocalScale;
bool Visibility;
//void* Parent;
//void* Children;
//void* Bones;
//bool IsBone;
//void* GeometryGroup;
//void* BindPoses;
//void* MaterialIndices;
//void* UserProperties;
} Model;
This is my C# class:
[StructLayout(LayoutKind.Sequential, CharSet = Interoperability.DefaultCharSet)]
public class Model// : IModel
{
[field: MarshalAs(UnmanagedType.LPStr)]
public string Name { get; set; }
[field: MarshalAs(UnmanagedType.U1)]
public bool Used { get; set; }
public Vector3 LocalPosition { get; set; }
public Quaternion LocalRotation { get; set; }
public Vector3 LocalScale { get; set; }
[field: MarshalAs(UnmanagedType.U1)]
public bool Visibility { get; set; }
//public IModel Parent { get; set; }
//[field: MarshalAs(UnmanagedType.LPStruct)]
//public IList<IModel> Children { get; set; }
//[field: MarshalAs(UnmanagedType.LPStruct)]
//public IList<IModel> Bones { get; set; }
//[field: MarshalAs(UnmanagedType.U1)]
//public bool IsBone { get; set; }
//public IGeometryGroup GeometryGroup { get; set; }
//[field: MarshalAs(UnmanagedType.LPStruct)]
//public Matrix4x4[] BindPoses { get; set; }
//[field: MarshalAs(UnmanagedType.LPStruct)]
//public IList<int> MaterialIndices { get; set; }
//[field: MarshalAs(UnmanagedType.LPStruct)]
//public Dictionary<string, object> UserProperties { get; set; }
}
[StructLayout(LayoutKind.Sequential, CharSet = Interoperability.DefaultCharSet)]
public class Model// : IModel
{
[field: MarshalAs(UnmanagedType.LPStr)]
public string Name { get; set; }
[field: MarshalAs(UnmanagedType.U1)]
public bool Used { get; set; }
public Vector3 LocalPosition { get; set; }
public Quaternion LocalRotation { get; set; }
public Vector3 LocalScale { get; set; }
[field: MarshalAs(UnmanagedType.U1)]
public bool Visibility { get; set; }
//public IModel Parent { get; set; }
//[field: MarshalAs(UnmanagedType.LPStruct)]
//public IList<IModel> Children { get; set; }
//[field: MarshalAs(UnmanagedType.LPStruct)]
//public IList<IModel> Bones { get; set; }
//[field: MarshalAs(UnmanagedType.U1)]
//public bool IsBone { get; set; }
//public IGeometryGroup GeometryGroup { get; set; }
//[field: MarshalAs(UnmanagedType.LPStruct)]
//public Matrix4x4[] BindPoses { get; set; }
//[field: MarshalAs(UnmanagedType.LPStruct)]
//public IList<int> MaterialIndices { get; set; }
//[field: MarshalAs(UnmanagedType.LPStruct)]
//public Dictionary<string, object> UserProperties { get; set; }
}
If I use the classes as they are, it works fine, but if I uncomment the lines on C# and CPP, the application crashes. It might have something to do with the way these references are passed to CPP. I don't need their types in CPP, I just need to keep a reference to them.
WhiteBlackGoose
IModel is a managed type, since it's (probably) an interface
rick_o_max
rick_o_maxOP2y ago
I just realized I haven't added the LPStruct to the Parent member, let me see if that makes any difference
WhiteBlackGoose
I have no idea if it will help
rick_o_max
rick_o_maxOP2y ago
No, it doesn't Yes, it is a managed type But can't I keep a reference to it as a void* when passing the struct to cpp?
WhiteBlackGoose
doesn't make sense to me, since GC is moving
rick_o_max
rick_o_maxOP2y ago
I don't need CPP to touch these commented out fields, but I have to keep them "alive" while passing the struct/class back and forth hmm I'll probably have to wrap the fields I can interop inside another class
WhiteBlackGoose
if you need to keep them alive, just use GC.KeepAlive call it after your native method although it doesn't make sense to me bc if you don't want to touch them but want to keep them alive... it's either that C++ does touch them, or you just have no reference to it afterwards, but then why keep alive?
rick_o_max
rick_o_maxOP2y ago
I don't want to create the structs on CPP heap, so I'm creating everything on C# and passing the objects to CPP. CPP can change the fields, but won't create anything (besides strings allocations) CPP calls a C# callback C# creates the objects and send the pointer back to CPP CPP modifies the objects But well, thinking better I'm still not 100% sure about the best way to handle that
WhiteBlackGoose
honestly, idk either I usually do it all manually to ensure it works as I expect but it's probably a bad advice
rick_o_max
rick_o_maxOP2y ago
Wdym with manually? Looks like I can pass the entire struct as pointer with Marshal.StructureToPtr That requires creating a buffer, tho
WhiteBlackGoose
manually as in, with structs, pointers, and 0 managed objects whatsoever
rick_o_max
rick_o_maxOP2y ago
The only problem is I have to use some managed classes, and part of the classes holding these other classes must come from CPP I know I can make it field by field, by calling CPP methods and creating some system to track the classes by id or something like that But that is a lot of work
Anton
Anton2y ago
check out the actual layout of your class layout sequential would put reference types on top anyway if I remember correctly you need layout explicit for it to actually apply from the docs: For non-blittable types, it controls the layout when the class or structure is marshaled to unmanaged code, but does not control the layout in managed memory. Use the attribute with LayoutKind.Explicit to control the precise position of each data member. This affects both managed and unmanaged layout, for both blittable and non-blittable types. by non-blittable they mean types with fields of reference type or show us how you marshal the object
rick_o_max
rick_o_maxOP2y ago
Interesting Is there a way to set a type blitting as a pointer only? Bc the only thing I need, is keep the same structure from the C# class in CPP I don't need CPP to touch some fields There are classes I can't interop anyway, like Dictionary instances from my C# classes If there is no way to do that, I'll have to create simpler structures to work on CPP, and wrap these in the final C# classes which contains all the properties I need The point is that, I don't want to create huge wrappers around my classes or using Swig to do that Can't use CLI either So my idea is letting C# handle the objects creation while CPP only fires C# callbacks Well, I guess I don't need to read any specific data form the C# classes in CPP anyway So using callbacks might work fine So, looks like it should work But Idk if what I'm doing is "Ilegal", like I'm passing a struct containing all my callbacks to CPP But when CPP calls these callbacks, program crashes It used to work fine when these callbacks were outside the struct But I use the struct to pass all callbacks at once I'm passing a CallbackData struct by val to CPP, it seems to get the values right, but throws an error when calling any callback method Nvm, values are messed up somehow...hmm
Anton
Anton2y ago
generally use properties if possible, then you don't have to care about the layout. You just get a pointer to some memory on the cpp side, and then use properties to work with that that's generally how library abstraction works in C too they pass around an opaque pointer (handle), and have a bunch of operations defined for it here you'd just have the operations be methods, which is arguably easier to work with Unity does it the other way — they define the layout on the cpp side, and have wrappers with properties on the C# side
rick_o_max
rick_o_maxOP2y ago
I ended up doing everything differently Since I just care about the final objects created in C# I pass a series of callbacks to C So it can pass the data C# needs to create and update the structures I'm not sure about C to C# callback performance, tho But it is working as expected
Accord
Accord2y ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.
Want results from more Discord servers?
Add your server