如何在dart:ffi中正确创建并绑定C函数指针MagickProgressMonitor?
Dart FFI绑定C函数指针问题解答
问题描述
我希望在Dart中绑定以下C函数,该函数接收名为progress_monitor的函数指针:
FFI_PLUGIN_EXPORT void* magickSetProgressMonitor(..., const void* progress_monitor,...);
其中progress_monitor是如下类型的函数指针:
typedef bool(*MagickProgressMonitor)(const char *,const long long, const long long,void *);
我在Dart中尝试创建函数指针的代码如下:
typedef MagickProgressMonitor = bool Function(String text, int offset, int size, List<int>? clientData); extension _MagickProgressMonitorExtension on MagickProgressMonitor { /// Creates a native function pointer from this method. Pointer<Void> toNativeFunctionPointer() { return Pointer.fromFunction<Bool Function(Pointer<Char>, LongLong, LongLong, Pointer<Void>)>(this, false).cast(); } }
但出现如下错误:
The type 'bool Function(String, int, int, List<int>?)' must be a subtype of 'Bool Function(Pointer<Char>, LongLong, LongLong, Pointer<Void>)' for 'fromFunction'.
我有两个问题:
- 使用
fromFunction<???>时应指定什么类型以消除错误? - C函数的最后一个参数是
void*指针(可指向任意类型),我当前尝试用Dart的List<int>和C的unsigned char数组表示,但似乎不正确,有没有更正确的处理方式?
更新: 我计划通过如下方式暴露API方法:
void magickSetProgressMonitor(MagickProgressMonitor progressMonitor, [List<int>? clientData]) { final Pointer<UnsignedChar> clientDataPtr = clientData?.toUnsignedCharArray() ?? nullptr; final Pointer<Void> progressMonitorPtr = progressMonitor.toNativeFunctionPointer(); Pointer<Void> oldMonitorPtr = _bindings.magickSetProgressMonitor(...,progressMonitorPtr,...); ... }
问题解答
1. 修正fromFunction的类型参数
错误根源是Dart侧的函数签名与fromFunction指定的C函数指针签名不兼容。需要拆分函数类型定义,先做原生兼容的签名,再封装成友好的Dart接口:
// 定义与C侧完全匹配的原生函数指针类型 typedef _NativeMagickProgressMonitor = Bool Function( Pointer<Char> text, LongLong offset, LongLong size, Pointer<Void> clientData, ); // 定义对外暴露的Dart友好函数类型 typedef MagickProgressMonitor = bool Function( String text, int offset, int size, Pointer<Void> clientData, ); extension _MagickProgressMonitorExtension on MagickProgressMonitor { Pointer<Void> toNativeFunctionPointer() { // 适配原生签名的回调逻辑 _NativeMagickProgressMonitor nativeCallback = ( Pointer<Char> textPtr, LongLong offset, LongLong size, Pointer<Void> clientData, ) { // 将C的char*转换为Dart String String text = textPtr.toDartString(); // 调用用户传入的Dart函数,自动处理数值类型转换 return this(text, offset.toInt(), size.toInt(), clientData) ? Bool(true) : Bool(false); }; // fromFunction的类型参数指定为_NativeMagickProgressMonitor return Pointer.fromFunction<_NativeMagickProgressMonitor>(nativeCallback, Bool(false)).cast(); } }
这样nativeCallback的签名与_NativeMagickProgressMonitor完全一致,就能通过类型检查。
2. 正确处理void*类型的clientData
C的void*是通用指针,不能直接映射为Dart的List<int>,需根据数据流向分场景处理:
场景1:Dart侧传递数据给C回调
如果是Dart要把自定义数据传给C侧回调,需手动管理原生内存:
void magickSetProgressMonitor(MagickProgressMonitor progressMonitor, [dynamic? clientData]) { Pointer<Void> clientDataPtr = nullptr; // 处理List<int>类型的客户端数据 if (clientData is List<int>) { // 分配对应长度的原生unsigned char内存 clientDataPtr = malloc.allocate<UnsignedChar>(clientData.length); // 把Dart List的数据拷贝到原生指针 for (int i = 0; i < clientData.length; i++) { clientDataPtr.elementAt(i).value = clientData[i]; } } // 处理整数类型的客户端数据 else if (clientData is int) { clientDataPtr = Pointer.fromAddress(clientData); } final Pointer<Void> progressMonitorPtr = progressMonitor.toNativeFunctionPointer(); Pointer<Void> oldMonitorPtr = _bindings.magickSetProgressMonitor(..., progressMonitorPtr, clientDataPtr, ...); // 注意:分配的原生内存需在合适时机释放,避免内存泄漏 // malloc.free(clientDataPtr); }
场景2:回调中处理C侧传入的void*
如果是C侧主动传递数据到Dart回调,需把Pointer<Void>转换成对应类型的指针再读取:
// 示例用户自定义的回调函数 bool myProgressCallback(String text, int offset, int size, Pointer<Void> clientData) { if (!clientData.isNull) { // 将void*转换为unsigned char数组指针 Pointer<UnsignedChar> dataPtr = clientData.cast<UnsignedChar>(); // 读取数据(需提前知道数组长度) int dataLength = 10; // 示例长度,实际需根据业务逻辑获取 List<int> dartData = List.generate(dataLength, (i) => dataPtr.elementAt(i).value); // 处理dartData... } return true; }
内容的提问来源于stack exchange,提问作者Haidar
相关产品推荐
相关产品推荐

