flutter调用Dll

400 阅读7分钟

加载动态库

import 'dart:ffi' as FFI;
final scriptDir = File(Platform.resolvedExecutable).parent.path;
_algorithm_lib = FFI.DynamicLibrary.open(p.join(scriptDir, 'CylinderSDK.dll'));
    

声明dll中导出的函数

定义函数类型

//FFI.Pointer<Utf8> 表示函数返回参数是一个字符串
typedef Test1C = FFI.Pointer<Utf8> Function();
typedef Test1Dart = FFI.Pointer<Utf8> Function();

//接收返回参数为int的情况:
typedef Test2C = FFI.Int32 Function();
typedef Test2Dart = int Function();

//接收返回参数为字节数据的情况:
typedef Test3C = FFI.Pointer<FFI.Uint8> Function();
typedef Test3Dart = FFI.Pointer<FFI.Uint8> Function();

//传参int、字符串、字节数据的情况:
typedef Test4C = FFI.Int32 Function(FFI.Int32,FFI.Pointer<Utf8>,FFI.Pointer<FFI.Uint8>);
typedef Test4Dart = int Function(int,FFI.Pointer<Utf8>,FFI.Pointer<FFI.Uint8>);

初始化可直接调用Dll导出函数的方法

//()里的字符串参数就是dll导出函数的名称
Test1 = _algorithm_lib!.lookupFunction<Test1C, Test1Dart>('Test1');
Test2 = _algorithm_lib!.lookupFunction<Test2C, Test1Dart>('Test2');
Test3 = _algorithm_lib!.lookupFunction<Test3C, Test3Dart>('Test3');
Test4 = _algorithm_lib!.lookupFunction<Test1C, Test1Dart>('Test4');

调用方法

    //调用Test1
    //resolutionsPointer 是一个指针,不能直接使用,需要通过toDartString方法将其转为dart的字符串
    final FFI.Pointer<Utf8> resolutionsPointer = Test1!();
    //这里的resolutionsJSON就是dll中Test1方法返回的字符串值
    final String resolutionsJSON = resolutionsPointer.toDartString();
    
    
    
    //调用Test2
    //这里的code就是dll中Test2方法返回的int类型值
    int code = Test2!();
    
    
    //调用Test3
    //resolutionsPointer 是一个指针,不能直接使用,需要通过asTypedList方法将其转为dart的Uint8List类型
    final FFI.Pointer<FFI.Uint8> resolutionsPointer = Test3!();
    //这里传递的参数是一个整数,表示字节数据的长度,需要预先知道长度
    final Uint8List Data = resolutionsPointer.asTypedList(1000);
    
    
    //调用Test4
    int intValue = 1;
    //toNativeUtf8将dart字符串转为Pointer<Utf8>类型
    final FFI.Pointer<Utf8> stringValue = "test".toNativeUtf8();
    //将data 转为FFI.Pointer<FFI.Uint8>格式
    //先创建对应长度的Pointer<FFI.Uint8>,然后通过setAll将数据写入
    //通过calloc申请的内存需要在适当时间释放,比如函数调用完成后
    Uint8List data = Uint8List(0);
    final FFI.Pointer<FFI.Uint8> dataPointer = calloc.call<FFI.Uint8>(data.length);
    dataPointer.asTypedList(data.length).setAll(0, data);
    int code = Test4!(intValue,stringValue,dataPointer);
    //释放内存
    calloc.free(dataPointer);

上面的示例涵盖了大多数场景

完整示例

我封装了一个类来通用化,下面是完整示例

示例类:

// ignore: file_names
import 'dart:ffi' as FFI;
import 'dart:io';
// ignore: depend_on_referenced_packages
import 'package:ffi/ffi.dart';
import 'dart:convert';
// import 'dart:io' show Platform;
// ignore: depend_on_referenced_packages
import 'package:path/path.dart' as p;
import 'package:flutter/foundation.dart';

typedef SetResolutionC = FFI.Int32 Function(FFI.Int32, FFI.Int32);
typedef SetResolutionDart = int Function(int, int);

typedef GetAvailableResolutionsC = FFI.Pointer<Utf8> Function();
typedef GetAvailableResolutionsDart = FFI.Pointer<Utf8> Function();

//算法识别相关
//相机拍照
typedef CvCamera_Image_TakeC = FFI.Pointer<FFI.Uint8> Function(
    FFI.Int32, FFI.Int32);
typedef CvCamera_Image_TakeDart = FFI.Pointer<FFI.Uint8> Function(int, int);


//扫描相机
typedef CvCamera_Scan_AllC = FFI.Int32 Function();
typedef CvCamera_Scan_AllDart = int Function();


//获取相机所有信息
typedef CvCamera_Scan_GetAllC = FFI.Pointer<Utf8> Function();
typedef CvCamera_Scan_GetAllDart = FFI.Pointer<Utf8> Function();


//打开相机
typedef CvCamera_OpenC = FFI.Int32 Function(FFI.Pointer<Utf8>);
typedef CvCamera_OpenDart = int Function(FFI.Pointer<Utf8>);


//启动相机,开始捕获
typedef CvCamera_StartC = FFI.Int32 Function(FFI.Int32);
typedef CvCamera_StartDart = int Function(int);


//设置相机参数<handel>,<name>,<value>
typedef CvCamera_SetParamC = FFI.Int32 Function(
    FFI.Int32, FFI.Pointer<Utf8>, FFI.Pointer<Utf8>);
typedef CvCamera_SetParamDart = int Function(
    int, FFI.Pointer<Utf8>, FFI.Pointer<Utf8>);


//通过句柄关闭相机<handel>
typedef CvCamera_CloseC = FFI.Int32 Function(FFI.Int32);
typedef CvCamera_CloseDart = int Function(int);


//通过句柄返回相机的描述信息,获取的这段数据记得释放
typedef CvCamera_GetDescC = FFI.Pointer<Utf8> Function(FFI.Int32);
typedef CvCamera_GetDescDart = FFI.Pointer<Utf8> Function(int);


//释放内存数据(字符串)
typedef CvCamera_String_FreeC = FFI.Int32 Function(FFI.Pointer<Utf8>);
typedef CvCamera_String_FreeDart = int Function(FFI.Pointer<Utf8>);


//释放内存数据(字节)
typedef CvCamera_Bytes_FreeC = FFI.Int32 Function(FFI.Pointer<FFI.Uint8>);
typedef CvCamera_Bytes_FreeDart = int Function(FFI.Pointer<FFI.Uint8>);

//初始化日志
typedef CylinderSDK_InitC = FFI.Int32 Function(FFI.Pointer<Utf8>);
typedef CylinderSDK_InitDart = int Function(FFI.Pointer<Utf8>);

typedef CvCamera_Image_ReadFileC = FFI.Pointer<FFI.Uint8> Function(
    FFI.Pointer<Utf8>, FFI.Int32);
typedef CvCamera_Image_ReadFileDart = FFI.Pointer<FFI.Uint8> Function(
    FFI.Pointer<Utf8>, int);

//终止日志
typedef CylinderSDK_TerminateC = FFI.Int32 Function();
typedef CylinderSDK_TerminateDart = int Function();
typedef CvCamera_Image_GrayToRGBC = FFI.Pointer<FFI.Uint8> Function(
    FFI.Pointer<FFI.Uint8>, FFI.Int32, FFI.Int32, FFI.Int32);
typedef CvCamera_Image_GrayToRGBDart = FFI.Pointer<FFI.Uint8> Function(
    FFI.Pointer<FFI.Uint8>, int, int, int);


//获取图片处理结果
//algorithm: 算法代码, 1,2,3,4类似
//data: 图片数据
//width: 图片宽度
//height: 图片高度
//format: 图片格式, 0:RGB, 1:GRAY
//stride: 图片数据的步长
//param: 算法参数
//返回值: 处理结果
typedef CvImage_Process_DetectC = FFI.Pointer<Utf8> Function(
    FFI.Int32,
    FFI.Pointer<FFI.Uint8>,
    FFI.Int32,
    FFI.Int32,
    FFI.Int32,
    FFI.Int32,
    FFI.Pointer<Utf8>);
typedef CvImage_Process_DetectDart = FFI.Pointer<Utf8> Function(
    int, FFI.Pointer<FFI.Uint8>, int, int, int, int, FFI.Pointer<Utf8>);

//保存图片
//data: 图片数据
//width: 图片宽度
//height: 图片高度
//format: 图片格式, 0:RGB, 1:GRAY
//stride: 图片数据的步长
//filename: 文件名
typedef CvImage_Save_FileC = FFI.Int32 Function(FFI.Pointer<FFI.Uint8>,
    FFI.Int32, FFI.Int32, FFI.Int32, FFI.Int32, FFI.Pointer<Utf8>);
typedef CvImage_Save_FileDart = int Function(
    FFI.Pointer<FFI.Uint8>, int, int, int, int, FFI.Pointer<Utf8>);

class RecognitionEngine {
  FFI.DynamicLibrary? _lib;
  FFI.DynamicLibrary? _algorithm_lib;

  CvCamera_Image_TakeDart? CvCamera_Image_Take;
  CvCamera_Scan_AllDart? CvCamera_Scan_All;
  CvCamera_Scan_GetAllDart? CvCamera_Scan_GetAll;
  CvCamera_OpenDart? CvCamera_Open;
  CvCamera_SetParamDart? CvCamera_SetParam;
  CvCamera_CloseDart? CvCamera_Close;
  CvCamera_GetDescDart? CvCamera_GetDesc;
  CvCamera_String_FreeDart? CvCamera_String_Free;
  CvCamera_Bytes_FreeDart? CvCamera_Bytes_Free;
  CvImage_Process_DetectDart? CvImage_Process_Detect;
  CylinderSDK_InitDart? CylinderSDK_Init;
  CylinderSDK_TerminateDart? CylinderSDK_Terminate;
  CvCamera_Image_ReadFileDart? CvCamera_Image_ReadFile;
  CvCamera_Image_GrayToRGBDart? CvCamera_Image_GrayToRGB;
  CvCamera_StartDart? CvCamera_Start;
  CvImage_Save_FileDart? CvImage_Save_File;

  // 构造函数
  RecognitionEngine() {
    loadLibrary();
    initDllFunctions();
  }

  void init() {
    loadLibrary();
    initDllFunctions();
  }

  // 加载动态库
  void loadLibrary() {
    //获取exe所在的路径
    final scriptDir = File(Platform.resolvedExecutable).parent.path;
    _lib = FFI.DynamicLibrary.open(p.join(scriptDir, 'SetResolution.dll'));
    _algorithm_lib =
        FFI.DynamicLibrary.open(p.join(scriptDir, 'CylinderSDK.dll'));
  }

  //初始化函数
  Future<void> initDllFunctions() async {
    //初始化拍照
    CvCamera_Image_Take = _algorithm_lib!
        .lookupFunction<CvCamera_Image_TakeC, CvCamera_Image_TakeDart>(
            'CvCamera_Image_Take');
    //初始化扫描相机
    CvCamera_Scan_All = _algorithm_lib!
        .lookupFunction<CvCamera_Scan_AllC, CvCamera_Scan_AllDart>(
            'CvCamera_Scan_All');
    //初始化获取当前所有相机的信息
    CvCamera_Scan_GetAll = _algorithm_lib!
        .lookupFunction<CvCamera_Scan_GetAllC, CvCamera_Scan_GetAllDart>(
            'CvCamera_Scan_GetAll');
    //初始化通过名字打开相机,返回句柄
    CvCamera_Open = _algorithm_lib!
        .lookupFunction<CvCamera_OpenC, CvCamera_OpenDart>('CvCamera_Open');
    //初始化通过句柄设置相机的参数
    CvCamera_SetParam = _algorithm_lib!
        .lookupFunction<CvCamera_SetParamC, CvCamera_SetParamDart>(
            'CvCamera_SetParam');
    //启动相机,开始捕获
    CvCamera_Start = _algorithm_lib!
        .lookupFunction<CvCamera_StartC, CvCamera_StartDart>('CvCamera_Start');
    //初始化通过句柄关闭相机
    CvCamera_Close = _algorithm_lib!
        .lookupFunction<CvCamera_CloseC, CvCamera_CloseDart>('CvCamera_Close');
    //初始化通过句柄返回相机的描述信息,获取的这段数据记得释放
    CvCamera_GetDesc = _algorithm_lib!
        .lookupFunction<CvCamera_GetDescC, CvCamera_GetDescDart>(
            'CvCamera_GetDesc');
    //初始化释放内存数据(字符串)
    CvCamera_String_Free = _algorithm_lib!
        .lookupFunction<CvCamera_String_FreeC, CvCamera_String_FreeDart>(
            'CvCamera_String_Free');
    //初始化释放内存数据(字节)
    CvCamera_Bytes_Free = _algorithm_lib!
        .lookupFunction<CvCamera_Bytes_FreeC, CvCamera_Bytes_FreeDart>(
            'CvCamera_Bytes_Free');
    //初始化获取图片处理结果
    CvImage_Process_Detect = _algorithm_lib!
        .lookupFunction<CvImage_Process_DetectC, CvImage_Process_DetectDart>(
            'CvImage_Process_Detect');
    //初始化日志
    CylinderSDK_Init = _algorithm_lib!
        .lookupFunction<CylinderSDK_InitC, CylinderSDK_InitDart>(
            'CylinderSDK_Init');
    //终止日志
    CylinderSDK_Terminate = _algorithm_lib!
        .lookupFunction<CylinderSDK_TerminateC, CylinderSDK_TerminateDart>(
            'CylinderSDK_Terminate');

    //读取文件
    CvCamera_Image_ReadFile = _algorithm_lib!
        .lookupFunction<CvCamera_Image_ReadFileC, CvCamera_Image_ReadFileDart>(
            'CvCamera_Image_ReadFile');
    //gray转为rgb
    CvCamera_Image_GrayToRGB = _algorithm_lib!.lookupFunction<
        CvCamera_Image_GrayToRGBC,
        CvCamera_Image_GrayToRGBDart>('CvCamera_Image_GrayToRGB');
    //保存图片
    CvImage_Save_File = _algorithm_lib!.lookupFunction<
        CvImage_Save_FileC,
        CvImage_Save_FileDart>('CvImage_Save_File');
  }

  //相机扫描
  Future<int> Camera_Scan_All() async {
    debugPrint("------------->开始扫描相机");

    int code = CvCamera_Scan_All!();
    debugPrint("------------->相机扫描结果:$code");
    return code;
  }

  //初始化日志
  void initlog() {
    final FFI.Pointer<Utf8> name = "D:/test.txt".toNativeUtf8();
    CylinderSDK_Init!(name);
  }

  //获取当前所有相机信息
  Future<List<Map<String, dynamic>>> Camera_Scan_GetAll() async {
    debugPrint("------------->开始获取当前所有相机信息");

    final FFI.Pointer<Utf8> resolutionsPointer = CvCamera_Scan_GetAll!();
    final String resolutionsJSON = resolutionsPointer.toDartString();
    // Map<String, dynamic> resolutions = json.decode(resolutionsJSON);
    final List<dynamic> decodedResolutions = json.decode(resolutionsJSON);

    List<Map<String, dynamic>> resolutions =
        decodedResolutions.cast<Map<String, dynamic>>();
    debugPrint("------------->相机信息:${jsonEncode(resolutions)}");
    return resolutions;
  }

  //通过名字打开相机
  Future<int> Camera_Open(String name) async {
    debugPrint("------------->开始打开相机:$name");
    final FFI.Pointer<Utf8> namePointer = name.toNativeUtf8();
    int code = CvCamera_Open!(namePointer);
    debugPrint("------------->相机打开返回的句柄:$code");
    return code;
  }

  //通过句柄设置相机的参数
  Future<int> Camera_SetParam(int handle, String name, String param) async {
    debugPrint("------------->开始设置相机参数:$name");

    final FFI.Pointer<Utf8> namePointer = name.toNativeUtf8();
    final FFI.Pointer<Utf8> paramPointer = param.toNativeUtf8();
    int code = CvCamera_SetParam!(handle, namePointer, paramPointer);
    debugPrint("------------->相机设置参数返回值:$code");
    return code;
  }

  //通过句柄启动相机,开始捕获图像
  Future<int> Camera_Start(int handle) async {
    debugPrint("------------->通过句柄启动相机:$handle");
    int code = CvCamera_Start!(handle);
    debugPrint("------------->通过句柄启动相机返回值:$code");
    return code;
  }

  //通过句柄关闭相机
  Future<int> Camera_Close(int handle) async {
    debugPrint("------------->开始关闭相机,句柄:$handle");
    int code = CvCamera_Close!(handle);
    debugPrint("------------->相机关闭返回值:$code");
    return code;
  }

  //通过句柄返回相机的描述信息,获取的这段数据记得释放
  Future<Map<String, dynamic>> Camera_GetDesc(int handle) async {
    debugPrint("------------->开始通过句柄获取相机描述信息:$handle");
    final FFI.Pointer<Utf8> resolutionsPointer = CvCamera_GetDesc!(handle);
    final String resolutionsJSON = resolutionsPointer.toDartString();
    debugPrint("------------->相机描述信息:$resolutionsJSON");

    //释放内存
    int code = CvCamera_String_Free!(resolutionsPointer);
    debugPrint("内存释放结果:$code");
    return jsonDecode(resolutionsJSON);
  }

  //相机拍照
  Future<Uint8List> Camera_Image_Take(int handle, int timeout,
      {required int width, required int height, required int stride}) async {
    debugPrint("------------->开始相机拍照,句柄:$handle");

    final FFI.Pointer<FFI.Uint8> resolutionsPointer =
        CvCamera_Image_Take!(handle, timeout);

    //先判断resolutionsPointer返回的是否是空地址
    // 检查是否返回了有效的指针
    if (resolutionsPointer.address == 0) {
      debugPrint("-------------> 相机拍照返回的指针为空:${resolutionsPointer.address}");
      return Uint8List(0);
    }

    //获取rgb数据
    final Uint8List rgb_data =
        await Camera_Image_GrayToRGB(resolutionsPointer, width, height, stride);

    return rgb_data;
  }

  Future<Uint8List> Camera_Image_GrayToRGB(
      FFI.Pointer<FFI.Uint8> Pointer, int width, int height, int stride) async {
    debugPrint("------------->dataPointer:${Pointer}");
    // //转为dart的字节数组
    // final Uint8List data = Pointer.asTypedList(width * height);
    // //保存到D://  存为yuv
    // // await File('D:/test.yuv').writeAsBytes(data);

    // debugPrint("------------->相机拍照结果长度:${data.length}");
    // final FFI.Pointer<FFI.Uint8> dataPointer =
    //     calloc.call<FFI.Uint8>(data.length);
    debugPrint("------------->开始转换格式");

    final FFI.Pointer<FFI.Uint8> resolutionsPointer =
        CvCamera_Image_GrayToRGB!(Pointer, width, height, stride);
    //转为dart的字节数组
    final Uint8List Data = resolutionsPointer.asTypedList(width * height * 3);
    debugPrint("------------->开始转换格式结果长度:${Data.length}");

    //拷贝data
    final Uint8List dataCopy = Uint8List.fromList(Data);

    // 释放内存
    int flag = CvCamera_Bytes_Free!(resolutionsPointer);
    debugPrint("释放返回值:$flag");
    int flag1 = CvCamera_Bytes_Free!(Pointer);
    debugPrint("释放返回值:$flag1");
    // calloc.free(dataPointer);

    return dataCopy;
  }

  //获取图片处理结果
  Future<Map<String, dynamic>?> Image_Process_Detect(
      int algorithm, 
      Uint8List data,
      int width,
      int height,
      int format,
      int stride,
      String param) async {
    debugPrint("------------->开始获取图片处理结果");
    try {
      //自动释放池
      // Arena arena = Arena();
      // final FFI.Pointer<FFI.Uint8> dataPointer1 =
      //     arena.call<FFI.Uint8>(data.length);
      // dataPointer1.asTypedList(data.length).setAll(0, data);
      // //回收内存
      // arena.releaseAll();

      //将data 转为FFI.Pointer<FFI.Uint8>格式
      final FFI.Pointer<FFI.Uint8> dataPointer =
          calloc.call<FFI.Uint8>(data.length);

      dataPointer.asTypedList(data.length).setAll(0, data);
      final FFI.Pointer<Utf8> paramPointer = param.toNativeUtf8();

      final FFI.Pointer<Utf8> resolutionsPointer = CvImage_Process_Detect!(
          algorithm, dataPointer, width, height, format, stride, paramPointer);
      final String resolutionsJSON = resolutionsPointer.toDartString();
      Map<String, dynamic>? resolutions;
      resolutions = json.decode(resolutionsJSON);
      debugPrint("------------->图片处理结果:${jsonEncode(resolutions)}");

      //释放内存
      calloc.free(dataPointer);
      return resolutions;
    } catch (e) {
      debugPrint("------------->图片处理结果解析失败:$e");
      return null;
    }
  }

  //保存图片
  Future<int> Image_Save_File(Uint8List data, int width, int height, int format,
      int stride, String filename) async {
    debugPrint("------------->开始保存图片");

    //将data 转为FFI.Pointer<FFI.Uint8>格式
    final FFI.Pointer<FFI.Uint8> dataPointer =
        calloc.call<FFI.Uint8>(data.length);

    dataPointer.asTypedList(data.length).setAll(0, data);
    final FFI.Pointer<Utf8> filenamePointer = filename.toNativeUtf8();

    int code = CvImage_Save_File!(
        dataPointer, width, height, format, stride, filenamePointer);

    debugPrint("------------->保存图片结果:$code");
    //释放内存
    calloc.free(dataPointer);
    return code;
  }

  //通过图片路径获取源数据
  Future<Uint8List> Camera_Image_ReadFile(
      String filename, int format, int size) async {
    // debugPrint("------------->开始读取本地图片,文件:$filename");
    final FFI.Pointer<Utf8> filenamePointer = filename.toNativeUtf8();
    final FFI.Pointer<FFI.Uint8> resolutionsPointer =
        CvCamera_Image_ReadFile!(filenamePointer, format);
    //转为dart的字节数组
    final Uint8List data = resolutionsPointer.asTypedList(size);
    //拷贝data
    final Uint8List dataCopy = Uint8List.fromList(data);
    // debugPrint("------------->读取本地图片结果长度:${data.length}");

    // 释放内存
    int flag = CvCamera_Bytes_Free!(resolutionsPointer);

    // debugPrint("释放返回值:$flag");
    return dataCopy;
  }

  // 设置分辨率
  int setResolution(int w, int h) {
    var setResolution = _lib
        ?.lookupFunction<SetResolutionC, SetResolutionDart>("SetResolution");
    // 调用函数设置分辨率
    print('Setting resolution to $w x $h'); // 调试输出

    // 调用函数设置分辨率
    final int result = setResolution!(w, h);
    if (result == 0) {
      print('Resolution set successfully');
    } else {
      print('Failed to set resolution');
    }
    // lib.close();
    return result;
  }

  // 获取可用分辨率
  Future<List<Map<String, dynamic>>> getResolutions() async {
    List<Map<String, dynamic>> resolutions = [];
    try {
      final GetAvailableResolutionsC getAvailableResolutionsFunc = _lib!
          .lookupFunction<GetAvailableResolutionsC,
              GetAvailableResolutionsDart>('GetAvailableResolutions');
      print('Getting available resolutions'); // 调试输出
      final FFI.Pointer<Utf8> resolutionsPointer =
          getAvailableResolutionsFunc();
      final String resolutionsJSON = resolutionsPointer.toDartString();
      final List<dynamic> decodedResolutions = json.decode(resolutionsJSON);
      resolutions = decodedResolutions.cast<Map<String, dynamic>>();
      // 注意:这里没有释放内存,因为toDartString()应该不会保留指向原生内存的引用
    } catch (e) {
      print('Error getting resolutions: $e'); // 打印异常信息
    }
    return resolutions;
  }
}

使用类

late RecognitionEngine REngine;
@override
  void initState() {
    super.initState();
    REngine = RecognitionEngine();
    Test();
  }
  void Test(){
    await REngine.Camera_Scan_All();
  }
  
//如果需要所有组件访问一个示例对象(可以避免多次加载dll)
//使用单例模式
//例如:
//用到了Provider插件
runApp(
    MultiProvider(
      providers: [
        // 创建RecognitionEngine的单例
        Provider<RecognitionEngine>.value(
          value: RecognitionEngine(),
        ),
      ],
      child: MyApp(),
    ),
);
//在其余组件获取已经初始化的RecognitionEngine实例
RecognitionEngine REngine = Provider.of<RecognitionEngine>(context, listen: false);

//其它方法:
//修改类结构
RecognitionEngine._();
static final RecognitionEngine instance = RecognitionEngine._();
Future<void> init() async {

}
//main中调用
await RecognitionEngine.instance.init();

//其它组件访问
final REngine = RecognitionEngine.instance;