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

Flutter FFI调用C++ DLL遇回调错误:无法在Isolate外调用原生回调

问题解决:Flutter FFI 跨线程Native回调错误

问题根源

你遇到的cannot invoke a native callback outside of an isolate错误,是因为Flutter的Pointer.fromFunction生成的回调绑定到创建它的Isolate线程,而你的DLL在独立线程中触发回调,脱离了原Isolate上下文,导致权限限制报错。

解决方案

核心是使用NativeCallable创建可跨线程调用的回调,并通过Isolate的SendPort将Native回调的消息转发到Dart Isolate处理,同时修正函数签名和内存管理问题。

步骤1:修正C++代码的语法错误

你的C++代码中export "C"是错误写法,应该改为extern "C":

extern "C" __declspec(dllexport) void StartServerSession(const char* Name, const char* js, int& sessionID, int& ERESULT, void(eventhandler)(const char*, int)) {}
extern "C" __declspec(dllexport) void InitializeServer(void(eventhandler)(const char*, int)) { 
    // 你的初始化逻辑
}

步骤2:修改Dart FFI代码

以下是完整的修正代码,关键修改已标注:

import 'dart:isolate';
import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'package:flutter/material.dart';

final DynamicLibrary dllPath = DynamicLibrary.open('path to dll');

// 修正InitializeServer的函数签名:回调参数是void(*)(const char*, int)
typedef InitializeFunc = Void Function(Pointer<Void> eventHandler);
typedef InitializeFun = void Function(Pointer<Void> eventHandler);

// 修正StartServerSession的函数签名:int&对应Pointer<Int32>,不是双重指针
typedef StartServerSessionC = Void Function(
    Pointer<Utf8> name,
    Pointer<Utf8> js,
    Pointer<Int32> sessionID,
    Pointer<Int32> eResult,
    Pointer<Void> eventHandler,
);
typedef StartServerSession = void Function(
    Pointer<Utf8> name,
    Pointer<Utf8> js,
    Pointer<Int32> sessionID,
    Pointer<Int32> eResult,
    Pointer<Void> eventHandler,
);

// 处理Native回调消息的Dart函数(在Isolate中执行)
void handleNativeResponse(String response) {
  print('Response: $response');
}

final initializeServer = dllPath.lookupFunction<InitializeFunc, InitializeFun>('InitializeServer');
final startServerSession = dllPath.lookupFunction<StartServerSessionC, StartServerSession>('StartServerSession');

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('FFI Demo')),
        body: Center(
          child: ElevatedButton(
            onPressed: () async {
              await callStartServerIsolate();
            },
            child: const Text('Call DLL Function'),
          ),
        ),
      ),
    );
  }
}

// Isolate入口函数
void startServerFunction(SendPort sendPort) async {
  // 创建ReceivePort接收Native回调的消息
  final receivePort = ReceivePort();
  sendPort.send(receivePort.sendPort);

  // 监听Native回调消息,转发到Dart处理函数
  receivePort.listen((message) {
    if (message is String) {
      handleNativeResponse(message);
    }
  });

  // 分配内存
  final name = 'path to second dll'.toNativeUtf8();
  final js = '{"key": "value"}'.toNativeUtf8(); // 替换为你的JSON值
  final sessionID = calloc<Int32>();
  final eResult = calloc<Int32>();

  // 创建可跨线程的Native回调:将Native传递的参数转为Dart字符串,通过SendPort发送
  final nativeCallable = NativeCallable<Void Function(Pointer<Utf8>, Int32)>.isolate(
    (responsePtr, length) {
      final response = responsePtr.toDartString(length: length);
      receivePort.sendPort.send(response);
    },
    exceptionalReturn: nullptr,
  );

  try {
    // 初始化DLL,传入跨线程回调
    initializeServer(nativeCallable.nativeFunction.cast<Void>());
    // 启动会话
    startServerSession(
      name,
      js,
      sessionID,
      eResult,
      nativeCallable.nativeFunction.cast<Void>(),
    );

    print('Session ID: ${sessionID.value}, Result: ${eResult.value}');
    sendPort.send("task Complete");
  } finally {
    // 释放资源:必须释放,避免内存泄漏
    calloc.free(name);
    calloc.free(js);
    calloc.free(sessionID);
    calloc.free(eResult);
    nativeCallable.close(); // 关闭NativeCallable
    receivePort.close();
  }
}

Future<void> callStartServerIsolate() async {
  final receivePort = ReceivePort();
  final sendPort = receivePort.sendPort;

  // 启动Isolate
  await Isolate.spawn(startServerFunction, sendPort);

  // 监听Isolate返回的消息
  await for (final message in receivePort) {
    if (message is SendPort) {
      // 保存Native回调的消息端口(可选,根据需求处理)
    } else if (message == "task Complete") {
      receivePort.close();
      break;
    }
  }
}

关键修改说明

  1. 函数签名修正:C++的int&参数对应Dart的Pointer<Int32>,而非Pointer<Pointer<Int32>>,之前的类型转换会导致内存访问错误。
  2. 使用NativeCallable:NativeCallable.isolate()创建的回调允许在任意Native线程调用,回调内部通过SendPort将消息发送到Dart Isolate,避免直接在Native线程执行Dart代码。
  3. 内存管理:添加了calloc.free()和nativeCallable.close(),避免内存泄漏。
  4. 消息转发:Native回调不直接执行print,而是将字符串通过SendPort发送到Isolate,由Dart线程处理UI/日志操作。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 23:18:09