C#调用Rust dll测试
C#调用Rust dll,难点在于字符串传递,其他类型比较自然。可以给函数传递json字符串,在传出json字符串,两端通过json序列化、反序列化,可以方便处理参数数据。也可以传递不带字符串的结构体(结构体内含字符串的情况没有验证)没什么难点,直接上码:Rust:use std::thread;use libc::{c_char, uint32_t};use std::ffi::{CStr, C
C#调用Rust dll,重点在于字符串传递,其他类型比较自然。可以给函数传递json字符串,在传出json字符串,两端通过json序列化、反序列化,可以方便处理参数数据。也可以传递不带字符串的结构体(结构体内含字符串的情况没有验证)。
小的结构体实例序列化反序列化速度很快,C#端序列化后,调用10000次Rust端函数,执行反序列化后修改对象成员,再次序列化,仅耗时36ms。开发中使用字符串传递参数和传出处理结果,完全可行。
没什么难点,直接上码:
Rust:
use std::thread;
use libc::{c_char, uint32_t};
use std::ffi::{CStr, CString};
use std::str;
extern crate serde;
extern crate serde_json;
#[macro_use] //引入serde_derive中的宏
extern crate serde_derive;
#[derive(Debug, Serialize, Deserialize)]
struct Address{
street: String,
city: String,
}
#[derive(Debug, Serialize, Deserialize)]
struct Person {
name: String,
age: u8,
address: Address,
phones: Vec<String>,
}
impl Person{
fn default() -> Self {
Person{
name: "zhangsan".to_string(),
age: 18u8,
address: Address{
street: "East nanjing road".to_string(),
city: "shanghai".to_string(),
},
phones: vec!["12345678".to_string(), "23456789".to_string()],
}
}
}
#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Kind {
Ok,
Done,
BufferTooSmall,
ArgumentNull,
InternalError,
}
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DbResult {
kind: Kind,
id: u32,
}
#[no_mangle]
pub extern fn GetDbResult() -> DbResult{
DbResult{
kind: Kind::Done,
id: 100u32,
}
}
#[no_mangle]
pub extern fn process(){
let handles: Vec<_> = (0..10).map(|_|{
thread::spawn(||{
let mut x = 0;
for _ in(0..5_000_000){
x+=1
}
x
})
}).collect();
for h in handles{
println!("thread finished with count={}",
h.join().map_err(|_| "Could not join a thread!").unwrap());
}
println!("done!");
}
fn mkstr(s: *const c_char) -> String {
let c_str = unsafe {
assert!(!s.is_null());
CStr::from_ptr(s)
};
let r_str = c_str.to_str().expect("Could not successfully convert string form foreign code!");
String::from(r_str)
}
#[no_mangle]
pub extern fn free_string(s: *mut c_char){
unsafe{
if s.is_null(){return}
CString::from_raw(s)
};
}
#[no_mangle]
pub extern fn result(istr: *const c_char) -> *mut c_char{
let s = mkstr(istr);
let values: Person = serde_json::from_str(&s).unwrap();
println!("Hello,{},welcome to rust world,you name change to {}-rust!",values.name,values.name);
let values2 = Person{name:values.name + "-rust", ..values};
let json = serde_json::to_string_pretty(&values2).unwrap();
let cex = CString::new(json).expect("Failed to create CString!");
cex.into_raw() //转移了所有权,rust不再释放内容,需要C端显式释放
}
Cargo.toxml
[package]
name = "rust_code"
version = "0.1.0"
authors = ["henreash <nmhys@126.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
libc = "0.2.48"
serde = "*"
serde_derive = "*"
serde_json = "*"
[lib]
name="rust_code"
crate-type=["dylib"]
C#代码:
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace SharpProcessor
{
public partial class Form1 : Form
{
[DllImport("rust_code.dll")]
internal static extern void process();
[DllImport("rust_code.dll")]
internal static extern void free_string(IntPtr str);
[DllImport("rust_code.dll")]
internal static extern StringHandle result(string inputs);
[DllImport("rust_code.dll")]
internal static extern DbResult GetDbResult();
public Form1()
{
InitializeComponent();
}
private Person GetTestData()
{
return new Person {
Name = "zhangsan",
Age = 18,
Addresss = new Address {
Street = "East nanjing road",
City = "Shanghai",
},
Phones = new List<string> {
"12345678",
"23456789",
}
};
}
private void button1_Click(object sender, EventArgs e)
{
//process();
//var str = "hello world, i come form c sharp";
var str = JsonConvert.SerializeObject(GetTestData());
using (var str2 = result(str))
{
MessageBox.Show(str2.AsString());
}
var dbResult = GetDbResult();
MessageBox.Show($"{dbResult._result} {dbResult._id}");
}
}
internal class StringHandle : SafeHandle
{
public StringHandle() : base(IntPtr.Zero, true) { }
public override bool IsInvalid
{
get { return false; }
}
public string AsString()
{
int len = 0;
while (Marshal.ReadByte(handle, len) != 0) { ++len; }
byte[] buffer = new byte[len];
Marshal.Copy(handle, buffer, 0, buffer.Length);
return Encoding.UTF8.GetString(buffer);
}
protected override bool ReleaseHandle()
{
Form1.free_string(handle);
return true;
}
}
public class Person
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("age")]
public byte Age { get; set; }
[JsonProperty("address")]
public Address Addresss { get; set; } = new Address();
[JsonProperty("phones")]
public List<string> Phones { get; set; } = new List<string>();
}
public class Address
{
[JsonProperty("street")]
public string Street { get; set; }
[JsonProperty("city")]
public string City { get; set; }
}
[StructLayout(LayoutKind.Sequential)]
public struct DbResult
{
public enum Kind : uint
{
Ok,
Done,
BufferTooSmall,
ArgumentNull,
InternalError
}
public readonly Kind _result;
public readonly uint _id;
public bool IsSuccess()
{
return _result == Kind.Ok || _result == Kind.Done;
}
public bool IsDone()
{
return _result == Kind.Done;
}
public bool IsBufferTooSmall()
{
return _result == Kind.BufferTooSmall;
}
}
}
运行结果:


耗时测试(首次执行耗时稍长,后面在执行,稳定在36ms左右):
private void button1_Click(object sender, EventArgs e)
{
var stop = new Stopwatch();
stop.Start();
var str = JsonConvert.SerializeObject(GetTestData());
for (var i = 0; i < 10000; i++)
{
using (var str2 = result(str))
{
}
}
stop.Stop();
MessageBox.Show(stop.ElapsedMilliseconds.ToString());
}
Rust ffi和外部系统传递数据:
1、获取C端数组:
使用slice::from_raw_parts获取切片,在做进一步处理。
pub extern "C" fn sum_of_array(array: *const u32, len: usize) -> u32 {
let array = unsafe {
assert!(!array.is_null());
slice::from_raw_parts(array, len)
};
array.iter().sum()
}
2、获取C端字符串:
使用CStr::from_ptr(raw_string)方法
pub extern fn result(istr: *const c_char){
let c_str = unsafe {
assert!(!s.is_null());
CStr::from_ptr(s)
};
let r_str = c_str.to_str().expect("Could not successfully convert string form foreign code!");
let input = String::from(r_str);
//c_str.to_string_lossy().into_owned();//简化写法
println!("{}", input);
}
3、给C返回字符串:
CString::new(“Hello, world!”).as_ptr() //将RustCString指针类型转化为C的原始指针类型
CString::new(“Hello world!”).into_raw() //将RustCString指针类型转化为C的原始指针类型,同时转移所有权,Rust不释放内存
CString::from_raw(s) //C端用完时Rust端来释放内存
use std::ffi::CString;
use std::os::raw::c_char;
extern {
fn my_printer(s: *const c_char);
}
let c_to_print = CString::new("Hello, world!").unwrap();
unsafe {
my_printer(c_to_print.as_ptr()); // 使用 as_ptr 将CString转化成char指针传给c函数
}
上面代码不牵涉释放,C用完c_to_print的内存,c_to_print才离开作用域释放。但如果使用into_raw,则需要from_raw配对调用。
开放原子旋武开源社区(简称“旋武社区”)是由开放原子开源基金会孵化及运营的技术社区,致力于在中国推广和发展Rust编程语言生态,推动Rust在操作系统、终端设备、安全技术、基础软件等关键领域的产业落地,构建安全、可靠、高效的软件基础设施。
更多推荐



所有评论(0)