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

如何使用dart:ffi调用返回函数指针的C++函数?

用dart:ffi调用带函数指针的C++函数指南

1. 导入必要的dart:ffi库

在Dart文件开头导入相关依赖:

import 'dart:ffi';
import 'package:ffi/ffi.dart'; // 提供字符串转换、内存管理的扩展方法

2. 定义C类型对应的FFI Typedef

将C++中的函数指针和导出函数映射为Dart可识别的类型:

2.1 映射unmanaged_callback_ptr

对应C++中void* (*unmanaged_callback_ptr)(const char*, const char*):

// 原生函数指针的底层定义
typedef UnmanagedCallbackNative = Pointer<Void> Function(Pointer<Utf8>, Pointer<Utf8>);
// Dart侧可调用的函数类型
typedef UnmanagedCallback = Pointer<Void> Function(Pointer<Utf8>, Pointer<Utf8>);

2.2 映射managed_direct_method_ptr

对应C++中char* (*managed_direct_method_ptr)(const char*, const char*, unmanaged_callback_ptr):

// 原生函数指针的底层定义
typedef ManagedDirectMethodNative = Pointer<Utf8> Function(
  Pointer<Utf8>,
  Pointer<Utf8>,
  Pointer<NativeFunction<UnmanagedCallbackNative>>,
);
// Dart侧可调用的函数类型
typedef ManagedDirectMethod = Pointer<Utf8> Function(
  Pointer<Utf8>,
  Pointer<Utf8>,
  UnmanagedCallback,
);

2.3 映射C++导出函数

分别映射CreateManagedDelegate和InvokeManagedFunction:

// 映射CreateManagedDelegate函数
typedef CreateManagedDelegateNative = Pointer<NativeFunction<ManagedDirectMethodNative>> Function(
  Pointer<Utf8>,
  Pointer<Utf8>,
  Pointer<Utf8>,
);
typedef CreateManagedDelegate = Pointer<NativeFunction<ManagedDirectMethodNative>> Function(
  Pointer<Utf8>,
  Pointer<Utf8>,
  Pointer<Utf8>,
);

// 映射InvokeManagedFunction函数
typedef InvokeManagedFunctionNative = Pointer<Utf8> Function(
  Pointer<Utf8>,
  Pointer<Utf8>,
  Pointer<NativeFunction<UnmanagedCallbackNative>>,
);
typedef InvokeManagedFunction = Pointer<Utf8> Function(
  Pointer<Utf8>,
  Pointer<Utf8>,
  UnmanagedCallback,
);

3. 加载动态库并绑定函数

根据平台加载对应的C++动态库(Windows为.dll,Linux为.so,macOS为.dylib):

// 替换为你的动态库路径
final DynamicLibrary lib = DynamicLibrary.open('libcpplib.so');

// 绑定CreateManagedDelegate
final CreateManagedDelegate createManagedDelegate = lib.lookupFunction<CreateManagedDelegateNative, CreateManagedDelegate>(
  'CreateManagedDelegate',
);

// 绑定InvokeManagedFunction
final InvokeManagedFunction invokeManagedFunction = lib.lookupFunction<InvokeManagedFunctionNative, InvokeManagedFunction>(
  'InvokeManagedFunction',
);

4. 处理CreateManagedDelegate返回的函数指针

调用C++函数获取原生函数指针后,转换为Dart可调用的函数:

// 将Dart字符串转换为C兼容的Utf8指针
final Pointer<Utf8> libName = 'your_lib_name'.toNativeUtf8();
final Pointer<Utf8> typeName = 'your_type_name'.toNativeUtf8();
final Pointer<Utf8> methodName = 'your_method_name'.toNativeUtf8();

// 调用C++函数获取原生函数指针
final Pointer<NativeFunction<ManagedDirectMethodNative>> nativeMethodPtr = createManagedDelegate(
  libName,
  typeName,
  methodName,
);

// 转换为Dart可直接调用的函数
final ManagedDirectMethod managedMethod = nativeMethodPtr.asFunction<ManagedDirectMethod>();

// 释放临时内存,避免泄漏
calloc.free(libName);
calloc.free(typeName);
calloc.free(methodName);

5. 定义并传递回调函数

回调函数必须是顶级函数或静态函数,防止被Dart GC回收导致崩溃:

// 符合UnmanagedCallback类型的回调函数
Pointer<Void> myUnmanagedCallback(Pointer<Utf8> arg1, Pointer<Utf8> arg2) {
  // 将C字符串转换为Dart字符串
  final String strArg1 = arg1.toDartString();
  final String strArg2 = arg2.toDartString();
  
  // 业务逻辑处理
  print('回调收到参数:$strArg1, $strArg2');
  
  // 返回void*类型,无需返回值时返回nullptr
  return nullptr;
}

调用获取到的managedMethod并传入回调:

// 准备调用参数
final Pointer<Utf8> arg1 = 'param1'.toNativeUtf8();
final Pointer<Utf8> arg2 = 'param2'.toNativeUtf8();

// 调用托管方法
final Pointer<Utf8> resultPtr = managedMethod(arg1, arg2, myUnmanagedCallback);

// 处理返回结果
final String result = resultPtr.toDartString();
print('调用结果:$result');

// 释放内存
calloc.free(arg1);
calloc.free(arg2);
calloc.free(resultPtr); // 需确认C侧是否由Dart负责释放该内存

6. 调用InvokeManagedFunction示例

直接调用该函数并传递回调:

final Pointer<Utf8> funcName = 'target_func'.toNativeUtf8();
final Pointer<Utf8> funcArgs = 'func_args'.toNativeUtf8();

final Pointer<Utf8> invokeResult = invokeManagedFunction(funcName, funcArgs, myUnmanagedCallback);

print('Invoke结果:${invokeResult.toDartString()}');

// 释放内存
calloc.free(funcName);
calloc.free(funcArgs);
calloc.free(invokeResult);

关键注意事项

  • 内存管理:所有通过toNativeUtf8()创建的指针必须用calloc.free()释放;C++返回的指针需明确内存归属,避免泄漏。
  • 回调存活:回调函数需保持存活(如存为全局变量),禁止使用匿名函数或实例方法,防止被GC回收。
  • 平台适配:动态库文件名需根据运行平台调整,可通过Platform.isWindows等条件分支处理。

内容的提问来源于stack exchange,提问作者BND

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 05:16:38