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

Flutter通过Dart FFI调用MATLAB DLL时分配指针崩溃问题

问题:Flutter调用MATLAB生成的DLL时,分配samplePointer导致应用崩溃

我正在开发Flutter应用,需求是读取.mat文件中的1×N矩阵,再调用MATLAB生成的独立DLL处理数据。但目前在分配samplePointer时应用直接崩溃,预期流程是分配指针后传入函数并返回结果,实际在指针分配环节就出问题了。

相关代码如下:

MATLAB 函数代码

function results = singleRVAnalysis(sample, numBins)
    % SINGLE_RVANALYSIS Analyzes a single random variable sample
    %
    % Inputs:
    %   sample - Array of sample data
    %   numBins - Number of bins for histogram (default: 50)
    %
    % Outputs:
    %   results - A structure containing PDF, CDF, mean, variance, and third moment

    if nargin < 2
        numBins = 50; 
    end

    edges = linspace(min(sample), max(sample), numBins + 1);

    counts = histcounts(sample, edges);
    binWidth = diff(edges);
    pdf = counts / (sum(counts) * binWidth(1)); 
    cdf = cumsum(pdf) * binWidth(1); 

    binCenters = edges(1:end-1) + diff(edges) / 2;
    meanValue = sum(binCenters .* pdf * binWidth(1));
    varianceValue = sum(((binCenters - meanValue).^2) .* pdf * binWidth(1));
    thirdMomentValue = sum(((binCenters - meanValue).^3) .* pdf * binWidth(1));

    results = struct;
    results.PDF = pdf;
    results.CDF = cdf;
    results.Mean = meanValue;
    results.Variance = varianceValue;
    results.ThirdMoment = thirdMomentValue;
    results.Edges = edges;
end

Flutter 代码

import 'dart:ffi';
import 'dart:io';
import 'package:ffi/ffi.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:file_picker/file_picker.dart';

const String dllPath = 'lib\\SingleRVAnalysis\\SingleRVAnalysisLib.dll';

final DynamicLibrary singleRVAnalysisLib = DynamicLibrary.open(dllPath);

final class Results extends Struct {
  external Pointer<Double> pdf;
  external Pointer<Double> cdf;
  @Double()
  external double mean;
  @Double()
  external double variance;
  @Double()
  external double thirdMoment;
  external Pointer<Double> edges;
  @Int32()
  external int binCount;
}

typedef SingleRVAnalysisNative = Results Function(
    Pointer<Double>, Int32, Int32);
typedef SingleRVAnalysisDart = Results Function(Pointer<Double>, int, int);

final singleRVAnalysis = singleRVAnalysisLib.lookupFunction<
    SingleRVAnalysisNative, SingleRVAnalysisDart>('mlxSingleRVAnalysis');

enum FileProcessStatus { idle, processing, completed, failed }

class FileProcessCubit extends Cubit<Map<String, dynamic>> {
  FileProcessCubit() : super({'status': FileProcessStatus.idle, 'file': null});

  void pickFile() async {
    try {
      final result = await FilePicker.platform.pickFiles();

      if (result != null && result.files.single.path != null) {
        emit({
          'status': FileProcessStatus.processing,
          'file': result.files.single,
        });
        await processFile(result.files.single);
      } else {
        emit({
          'status': FileProcessStatus.failed,
          'error': 'No file selected',
        });
      }
    } catch (e) {
      emit({
        'status': FileProcessStatus.failed,
        'error': 'File picking error: $e',
      });
    }
  }

  Future<void> processFile(PlatformFile file) async {
    final filePath = file.path;

    if (filePath == null) {
      emit({
        'status': FileProcessStatus.failed,
        'error': 'No file path available',
      });
      return;
    }

    try {
      final content = await File(filePath).readAsBytes();

      if (content.isEmpty) {
        throw Exception('The file is empty.');
      }

      final List<double> sample = content
          .toString()
          .replaceAll(RegExp(r'[^\d\.\-eE, ]'), '')
          .split(RegExp(r'[\,\s]+'))
          .where((e) => e.isNotEmpty)
          .map((e) => double.tryParse(e) ?? 0.0)
          .toList();

      if (sample.isEmpty) {
        throw Exception('Sample array is empty.');
      }

      const int numBins = 50;
      final Pointer<Double> samplePointer = listToPointerDouble(sample);
      final result = singleRVAnalysis(samplePointer, sample.length, numBins);

      try {
        final pdf = result.pdf.asTypedList(numBins).toList();
        final cdf = result.cdf.asTypedList(numBins).toList();
        final mean = result.mean;
        final variance = result.variance;
        final thirdMoment = result.thirdMoment;

        emit({
          'status': FileProcessStatus.completed,
          'mean': mean,
          'variance': variance,
          'thirdMoment': thirdMoment,
          'pdf': pdf,
          'cdf': cdf,
        });
      } catch (e) {
        emit({
          'status': FileProcessStatus.failed,
          'error': 'Error during DLL processing: $e',
        });
      } finally {
        calloc.free(samplePointer);
      }
    } catch (e) {
      emit({
        'status': FileProcessStatus.failed,
        'error': 'Processing failed: $e',
      });
    }
  }

  void reset() {
    emit({'status': FileProcessStatus.idle, 'file': null});
  }
}

Pointer<Double> listToPointerDouble(List<double> sample) {
  final Pointer<Double> pointer =
      calloc.allocate<Double>(sample.length, alignment: 8);

  for (int i = 0; i < sample.length; i++) {
    pointer[i] = sample[i];
  }

  return pointer;
}

排查方向与解决方法

  1. 内存分配计算错误:calloc.allocate<Double>(sample.length)的第一个参数是字节数,不是元素个数。正确写法应为calloc.allocate<Double>(sizeOf<Double>() * sample.length, alignment: 8),每个Double占8字节,之前的写法会导致内存分配不足,触发越界崩溃。
  2. .mat文件读取逻辑错误:当前代码将二进制.mat文件转成字符串解析,完全错误。.mat是二进制格式,需使用mat_io等专门库解析矩阵数据,否则会生成无效的sample列表,引发后续内存问题。
  3. DLL接口类型不匹配:检查MATLAB生成的DLL接口参数、返回结构体的字段顺序、数据类型(如Int32/Int64)是否与Flutter定义的Results完全一致,避免类型不匹配导致的内存异常。
  4. DLL路径问题:Windows下路径可改用/,确保DLL能被正确加载,加载失败会引发后续调用的内存错误。
  5. 内存对齐问题:尝试移除手动指定的alignment:8,使用calloc默认对齐方式,避免与MATLAB DLL的对齐要求冲突。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 11:22:07