You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过dart:ffi调用C++ .so动态库内对象的成员函数?

核心错误原因

你查找符号的逻辑从根上就错了:

  • 动态链接库(.so)导出的可供查找的符号,全部是符合C ABI的全局函数,不存在对象名.方法名格式的符号。g.sayHello是Java/Kotlin层面向对象语法的调用写法,编译后根本不会以这个名字存在于.so的符号表里,自然会报undefined symbol。
  • 你定义的函数签名和实际方法完全不匹配:sayHello接收一个字符串入参、返回字符串,你写的typedef是无参函数,就算找到符号调用也会崩溃。
解决步骤

1. 先确认.so内真实导出的符号名

把对应ABI架构的.so文件拉到本地,执行如下命令查看所有和greet/sayHello相关的导出符号:

nm -D libGreetings.so | grep -i -E "(sayhello|greet)"

命令输出的函数名才是你可以传入lookupFunction的合法符号名。

注意:如果你的.so是仅做了JNI适配的版本,查出来的符号会是Java_<包名>_RustGreetings_sayHello格式,这类是专门给Java虚拟机调用的JNI函数,强依赖JNI环境,不能直接用Dart FFI调用,要么重新编译Rust库增加C ABI导出,要么走Flutter的MethodChannel调安卓原生层逻辑,不要直接lookup这类符号。

2. 修正函数签名定义

按照入参、出参都是字符串的实际逻辑,修正类型定义:

// C侧函数签名:入参为Utf8字符串指针,返回Utf8字符串指针
typedef SayHelloC = Pointer<Utf8> Function(Pointer<Utf8> content);
// Dart侧对应函数签名
typedef SayHelloDart = Pointer<Utf8> Function(Pointer<Utf8> content);

3. 正确加载、查找、调用函数

如果你的.so已经导出了C ABI兼容的全局函数(Rust侧一般会加#[no_mangle] pub extern "C"标记导出,比如导出的函数名是say_hello,替换成你第一步查出来的实际符号名即可),按如下逻辑调用:

// 动态库加载逻辑保持原有逻辑即可
final lib = Platform.isAndroid
    ? DynamicLibrary.open(libGreetings)
    : DynamicLibrary.process();

// 用真实导出的符号名查找函数
final sayHello = lib.lookupFunction<SayHelloC, SayHelloDart>('say_hello');

// 执行调用,注意原生内存的申请和释放
final input = "world".toNativeUtf8();
try {
  final resPtr = sayHello(input);
  final result = resPtr.toDartString();
  print("调用结果:$result");
  // 提示:如果返回的字符串是Rust侧在堆上分配的,需要调用Rust侧导出的释放函数回收内存,否则会造成内存泄漏
} catch (e) {
  print("调用出错:${e.toString()}");
} finally {
  // 释放入参占用的原生内存
  calloc.free(input);
}
常见坑点
  • Rust代码如果没加#[no_mangle]和extern "C"标记,编译后会被符号混淆,你查不到可读的函数名,必须重新编译库加上标记。
  • 不要尝试直接调用JNI格式的导出函数,这类函数要求第一个参数是JNI环境指针、第二个参数是类/对象实例,直接从Dart传参不符合调用约定,会直接触发内存访问错误崩溃。

内容的提问来源于stack exchange,提问作者C ivit Vaghani

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 22:46:03