0

How would you marshall this nested array of structure in C#?

C struct:

typedef struct
{
    unsigned int appVersionNumber;
    unsigned int networkId;
    struct
    {
        int code;
        int endDate;
    } profiles[4];
} CardEnvHolder;

My C# attempt:

[StructLayout(LayoutKind.Sequential)]
unsafe struct CardEnvHolder
{
    public uint appVersionNumber;
    public uint networkId;
    public Profiles[] profiles;
}

[StructLayout(LayoutKind.Sequential)]
struct Profiles 
{
    public int code;
    public int endDate;
}

C# Main:

unsafe 
{
    CardEnvHolder envhold = new CardEnvHolder();
    void* ptr = (void*)&envhold; //Error here
    EnvHolderTest(ptr);
    Console.WriteLine(envhold.profil[1].code);
}

Unfortunately I get the error CS0208 "Cannot take the address of, get the size of, or declare a pointer to a managed type ('T')"

As requested,

EnvHolder C function:

void EnvHoldInit(CardEnvHolder* envhold)
{
    envhold->appVersionNumber = 4;
    envhold->networkId = 8;
    envhold->profiles[1].code = 84;
    printf("%d\n", envhold->profiles[1].code);
    envhold->profiles[1].code++;
}

EXPORT void EnvHolderTest(void* envhold)
{
    EnvHoldInit(envhold);
}

EnvHolder C# prototype:

[DllImport("Sandbox.dll", CallingConvention = CallingConvention.Cdecl)]
extern static unsafe void EnvHolderTest([In, Out] void* envhold);
Eize
  • 19
  • 4
  • Are you asking about C# or C — they are radically different languages and should seldom both be tagged in the same question. When they are both relevant, the question will explicitly ask about how to map a C structure into C# or vice versa, or something similar. Marshalling is normally a precursor to sending data over the network, or storing it in a file. What are you trying to do, and which language are you trying to do it in? – Jonathan Leffler Jun 30 '21 at 14:46
  • @JonathanLeffler Yes, I am looking to marshall this C struct in C#, sorry for the misunderstanding – Eize Jun 30 '21 at 14:47
  • 1
    There is nothing non-trivial in declaring this structure in C#. What exactly are having problems with? – GSerg Jun 30 '21 at 14:49
  • So, are you looking to translate that C structure into the C# analogue? Are you looking for C code that will format data from that C structure into a buffer that C# can read into its analogue of the structure? How will you read that structure in C#? Are you looking for the C# code as well as the C code? What have you tried? Where did you run into problems? – Jonathan Leffler Jun 30 '21 at 14:50
  • @GSerg My main problem here is the fact that Profiles is an array of struct, therefore I cannot use Profiles[] nor IntPtr[] as it will transform the whole structure to managed type – Eize Jun 30 '21 at 14:54
  • 1
    Yes, you can. Just add `[MarshalAs(UnmanagedType.ByValArray, SizeConst=4)]` to `public Profiles[] profiles`. That `unsafe` portion also looks unnecessary, please show `EnvHolderTest`. – GSerg Jun 30 '21 at 14:56
  • See: https://stackoverflow.com/questions/30690823/converting-c-struct-to-c-sharp-and-using-it – Craig Estey Jun 30 '21 at 14:57
  • @CraigEstey That concerns pointers to inner structs as opposed to inline inner structs. – GSerg Jun 30 '21 at 14:59
  • @GSerg Unfortunately, `[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public Profiles[] profiles;` Does not fix the error – Eize Jun 30 '21 at 15:02
  • @GSerg EnvHolderTest will just assign some random value to print for testing purposes, the reason of using unsafe & pointers here is that it feels easier to use than manual marshalling for this project – Eize Jun 30 '21 at 15:09
  • 1
    It might feel easier but it's wrong. Just use standard marshalling `EnvHolderTest(ref envhold)` after setting the correct `MarshalAs` – Charlieface Jun 30 '21 at 15:12
  • @Charlieface EnvHolderTest must be called as void* / IntPtr as this parameter will return different structures, my apology for not clarifying this earlier – Eize Jun 30 '21 at 15:23
  • @Eize With the signature of `(CardEnvHolder* envhold)`, the function cannot return a different structure. It can only amend the structure it is given, which is exactly `(ref envhold)`. – GSerg Jun 30 '21 at 15:26
  • @GSerg Another information, for this specific case, the dev is assumed to know which structure will be returned, the declaration of said structure is not fixed and can be any of the returned struct. Sorry for my awful english, I can provide a more detailed example that will make the situation much easier to understand if needed. EDIT: Come to think about it overloading this fonction for all structures that can be returned might do the trick – Eize Jun 30 '21 at 15:31
  • I would suggest you create different `extern` declarations (overloads or different names) for each type, and use standard marshalling. Otherwise it gets very complicated and messy, with `Marshal.SizeOf/AllocHGlobal/PtrToStructure/StructureToPtr/Free` and associated `try/finally` – Charlieface Jun 30 '21 at 15:54
  • Yes that seems to be the way to go, I will give it a try tomorrow and keep this post updated – Eize Jun 30 '21 at 16:37

0 Answers0