I want to pass an array of UTF8 encoded strings into native code. I did it for one single string (as it's just a byte array), but now I want to pass an array of strings.
Because I don't know the size of array, I should pass reference to it. So I write a struct that should contain array pointer and its length:
[StructLayout(LayoutKind.Sequential)]
struct StringRef
{
[MarshalAs(UnmanagedType.LPArray)]
public byte[] Bytes;
public uint Length;
}
But when I run it it fails:
Cannot marshal field 'Bytes' of type 'StringRef': Invalid managed/unmanaged type combination (Array fields must be paired with ByValArray or SafeArray).
Here is complete code sample.
Managed code:
public class RustInterop
{
[StructLayout(LayoutKind.Sequential)]
struct StringRef
{
[MarshalAs(UnmanagedType.LPArray)]
public byte[] Bytes;
public uint Length;
}
[DllImport("rust.dll")]
private static extern int println(StringRef[] array, uint count);
public static int Println(params string[] strings)
{
var array = strings.Select(str => Encoding.UTF8.GetBytes(str)).Select(bytes => new StringRef
{
Bytes = bytes,
Length = (uint)bytes.Length
}).ToArray();
return println(array, (uint) array.Length);
}
}
Unmanaged code:
#[repr(C)]
pub struct StringRef {
bytes: *const u8,
length: usize
}
#[no_mangle]
pub extern fn println(array: *const StringRef, count: usize) -> i32 {
let array_slice = unsafe { std::slice::from_raw_parts(array, count) };
for str in array_slice {
let slice = unsafe { std::slice::from_raw_parts(str.bytes, str.length) };
let str = std::str::from_utf8(slice);
match str {
Ok(str) => {
println!("{}", str);
},
Err(_) => return -1
};
}
1
}