如何在本机代码中传递数组数组
How do I pass array of arrays in native code
我想将一组 UTF8 编码的字符串传递到本机代码中。我为一个字符串执行了此操作(因为它只是一个字节数组),但现在我想传递一个字符串数组。
因为我不知道数组的大小,所以我应该传递对它的引用。所以我写了一个应该包含数组指针及其长度的结构:
[StructLayout(LayoutKind.Sequential)]
struct StringRef
{
[MarshalAs(UnmanagedType.LPArray)]
public byte[] Bytes;
public uint Length;
}
但是当我 运行 它失败了:
Cannot marshal field 'Bytes' of type 'StringRef': Invalid managed/unmanaged type combination (Array fields must be paired with ByValArray or SafeArray).
这是完整的代码示例。
托管代码:
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);
}
}
非托管代码:
#[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
}
你能做的最好的是:
[StructLayout(LayoutKind.Sequential)]
struct StringRef
{
public IntPtr Bytes;
public int Length;
}
[DllImport("CPlusPlusSide.dll")]
private static extern int println(StringRef[] array, int count);
public static int Println(params string[] strings)
{
var utf8s = Array.ConvertAll(strings, x => Encoding.UTF8.GetBytes(x));
var handles = new GCHandle[utf8s.Length];
var refs = new StringRef[utf8s.Length];
try
{
for (int i = 0; i < handles.Length; i++)
{
try
{
}
finally
{
handles[i] = GCHandle.Alloc(utf8s[i], GCHandleType.Pinned);
}
refs[i] = new StringRef
{
Bytes = handles[i].AddrOfPinnedObject(),
Length = (int)utf8s[i].Length
};
}
return println(refs, refs.Length);
}
finally
{
for (int i = 0; i < handles.Length; i++)
{
if (handles[i].IsAllocated)
{
handles[i].Free();
}
}
}
}
请注意,我已经将各种 uint
更改为 int
...最后它们具有相同的 sizeof()
,所以这里没有问题。
使用此 C 代码测试(我没有 Rust 编译器):
typedef struct StringRef
{
char* bytes;
unsigned int length;
} StringRef;
__declspec(dllexport) int __stdcall println(StringRef* array, unsigned int count)
{
for (unsigned int i = 0; i < count; i++)
{
printf("%.*s\n", array[i].length, array[i].bytes);
}
return 0;
}
我想将一组 UTF8 编码的字符串传递到本机代码中。我为一个字符串执行了此操作(因为它只是一个字节数组),但现在我想传递一个字符串数组。
因为我不知道数组的大小,所以我应该传递对它的引用。所以我写了一个应该包含数组指针及其长度的结构:
[StructLayout(LayoutKind.Sequential)]
struct StringRef
{
[MarshalAs(UnmanagedType.LPArray)]
public byte[] Bytes;
public uint Length;
}
但是当我 运行 它失败了:
Cannot marshal field 'Bytes' of type 'StringRef': Invalid managed/unmanaged type combination (Array fields must be paired with ByValArray or SafeArray).
这是完整的代码示例。
托管代码:
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);
}
}
非托管代码:
#[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
}
你能做的最好的是:
[StructLayout(LayoutKind.Sequential)]
struct StringRef
{
public IntPtr Bytes;
public int Length;
}
[DllImport("CPlusPlusSide.dll")]
private static extern int println(StringRef[] array, int count);
public static int Println(params string[] strings)
{
var utf8s = Array.ConvertAll(strings, x => Encoding.UTF8.GetBytes(x));
var handles = new GCHandle[utf8s.Length];
var refs = new StringRef[utf8s.Length];
try
{
for (int i = 0; i < handles.Length; i++)
{
try
{
}
finally
{
handles[i] = GCHandle.Alloc(utf8s[i], GCHandleType.Pinned);
}
refs[i] = new StringRef
{
Bytes = handles[i].AddrOfPinnedObject(),
Length = (int)utf8s[i].Length
};
}
return println(refs, refs.Length);
}
finally
{
for (int i = 0; i < handles.Length; i++)
{
if (handles[i].IsAllocated)
{
handles[i].Free();
}
}
}
}
请注意,我已经将各种 uint
更改为 int
...最后它们具有相同的 sizeof()
,所以这里没有问题。
使用此 C 代码测试(我没有 Rust 编译器):
typedef struct StringRef
{
char* bytes;
unsigned int length;
} StringRef;
__declspec(dllexport) int __stdcall println(StringRef* array, unsigned int count)
{
for (unsigned int i = 0; i < count; i++)
{
printf("%.*s\n", array[i].length, array[i].bytes);
}
return 0;
}