Search code examples
c#iosobjective-cpointersmarshalling

Marshalling variable-sized string C array into C# (Unity iOS)


I need to return a collection of strings from Objective-C to C# and can't wrap my head around how to do this properly. When I print the returned pointer on managed side its value is 0.

Objective-c

FOUNDATION_EXPORT int _GetAllKeys(char** pointer)
{
    NSArray* array = @[@"key1", @"key2"]; // dummy strings
    
    int size = (int)array.count;

    pointer = malloc(size * sizeof(char *));
    
    for (int i = 0; i < size; i++)
    {
        pointer[i] = MakeStringCopy(array[i]);
    }
    return size;
}

C#

[DllImport("__Internal")]
private static extern int _GetAllKeys(out IntPtr buffer);

internal static IEnumerable<string> GetAllKeys()
{
    int count = _GetAllKeys(out var buffer);
    List<string> keys = new List<string>();
    for (int i = 0; i < count; i++)
    {
    keys.Add(Marshal.PtrToStringUTF8(IntPtr.Add(buffer, IntPtr.Size))); //eventually when i make this work the strings will be returned as UTF8 but for now its not even making it to this line, so ignore the encoding.
    }

    return keys;
}

---------------- SOLUTION ----------------

Objective-c

FOUNDATION_EXPORT int _GetAllKeys(char*** pointer)
{
    NSArray* array = @[@"key1", @"key2"];  
    int size = (int)array.count;
    
    *pointer = malloc(size * sizeof(char *));

    for (int i = 0; i < size; i++)
    {
        (*pointer)[i] = MakeStringCopy(array[i]);
    }
    
    return size;
}

C#

[DllImport("__Internal")]
private static extern int _GetAllKeys(out IntPtr buffer);

internal static IEnumerable<string> GetAllKeys()
{
    int count = _GetAllKeys(out var buffer);
    List<string> keys = new List<string>();
    for (int i = 0; i < count; i++)
    {
        IntPtr ptr = Marshal.ReadIntPtr(buffer);
        keys.Add(Marshal.PtrToStringAuto(ptr));
        buffer = IntPtr.Add(buffer, IntPtr.Size);
    }

    return keys;
}

Solution

  • You are trying to return a char**, so the C function parameter needs to be a char***:

    FOUNDATION_EXPORT int _GetAllKeys(char*** pointer)
    {
       NSArray* array = @[@"key1", @"key2"]; // dummy strings
       int size = (int)array.count;
    
       *pointer = malloc(size * sizeof(char *));
    
       for (int i = 0; i < size; i++)
       {
           (*pointer)[i] = MakeStringCopy(array[i]);
       }
       return size;
    }
    

    The C# code looks fine, except that IntPtr.Add returns a new IntPtr, so it has to be:

    keys.Add(Marshal.PtrToStringUTF8(buffer));
    buffer = IntPtr.Add(buffer, IntPtr.Size);