Search code examples
c#androidcpinvokemarshalling

C# Marshalling Structure with Strings to C Library


I am writing a C library that I must interact with from Unity3D (C#) and am running into an issue with writing one of my PInvoke calls.

I have the following c struct

typedef struct TestStruct
{
    const char* Method;
    const char* Url;
} TestStruct;

And C function signature

__declspec(dllexport) void __cdecl TestMethod(TestStruct* args)
{
    // Do stuff with Method and URL
}

In C# I've created my struct like so

[StructLayout(LayoutKind.Sequential)]
public struct TestStruct
{
    public string Method;
    public string Url;
}

And PInvoke signature like so

[DllImport("Test", CallingConvention = CallingConvention.Cdecl)]
private static extern void TestMethod(TestStruct args);

Now when I run this in the Unity editor on Win64 it works just fine. But when I deploy it to my android device (Nexus 6 which I believe is a 32 bit ARM architecture) the Method and Url properties are null in my test struct when they get to the C lib.

Strangely enough, if I change my function signatures to take the raw arguments avoiding the struct entirely, it works just fine.

__declspec(dllexport) void __cdecl TestMethod(const char* Method, const char* Url)
{
    // Do stuff with Method and URL
}

and

[DllImport("Test", CallingConvention = CallingConvention.Cdecl)]
private static extern void TestMethod(string method, string url);

Works just fine. Does anyone have any ideas what I might be doing wrong?


Solution

  • This:

    __declspec(dllexport) void __cdecl TestMethod(TestStruct* args)
    

    is equivalent to:

    [DllImport("Test", CallingConvention = CallingConvention.Cdecl)]
    private static extern void TestMethod(ref TestStruct args);
    

    or out TestStruct, depending on who will write to the TestStruct.

    Important: if the strings are passed C->C#, then C# shouldn't modify them (or .NET will try to free them with the wrong deallocator and bad things will happen). If you use out TestStruct the C-side will have to deallocate the returned const char* I'll have to say I don't know how exactly (because you are using Unity and not .NET under Windows, so the thing gets a little more hairy)