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

如何在Golang与Dart间传递结构体和函数?FFI调用结构体报错求助

Flutter FFI调用Go结构体返回值问题解决

问题概述

在Flutter应用中使用ffi调用Go编译的共享库时,GetKey和SumNumbers函数可正常调用,但调用GetPerson时触发错误:

Invalid argument(s): Failed to lookup symbol 'GetPerson': error code 127

核心原因

Go的-buildmode=c-shared编译模式仅支持导出符合C语言ABI的函数和类型:

  1. Go自定义结构体无法直接作为函数返回值被C识别,编译时不会生成对应导出符号,导致Dart找不到GetPerson函数;
  2. Go原生string、int类型与C语言类型不兼容,直接使用会导致内存布局不匹配。

修正后的Go代码

将结构体改为C兼容类型,通过指针返回结构体实例,并添加内存释放函数避免泄漏:

package main

import "C"
import "unsafe"

//export GetKey
func GetKey() *C.char {
	theKey := "123-456-789"
	return C.CString(theKey)
}

//export SumNumbers
func SumNumbers(a, b C.int) C.int {
	return a + b
}

//export GetPerson
func GetPerson() *C.struct_Person {
	p := &C.struct_Person{
		name:   C.CString("name1"),
		age:    C.int(20),
		job:    C.CString("job1"),
		salary: C.int(100),
	}
	return p
}

//export FreePerson
func FreePerson(p *C.struct_Person) {
	C.free(unsafe.Pointer(p.name))
	C.free(unsafe.Pointer(p.job))
	C.free(unsafe.Pointer(p))
}

// 定义C兼容的Person结构体
type Person C.struct_Person

func main() {}

修正后的Dart代码

对应Go的C结构体内存布局调整类型定义,修改函数签名为指针返回,并添加内存释放逻辑:

import 'dart:ffi';
import 'package:ffi/ffi.dart';

// 与Go中C兼容Person结构体内存布局完全匹配
class Person extends Struct {
  external Pointer<Utf8> name;
  @Int32()
  external int age;
  external Pointer<Utf8> job;
  @Int32()
  external int salary;
}

// 函数类型定义
typedef GetPersonC = Pointer<Person> Function();
typedef GetPersonDart = Pointer<Person> Function();

typedef FreePersonC = Void Function(Pointer<Person>);
typedef FreePersonDart = void Function(Pointer<Person>);

void main() {
  // 加载共享库(根据平台调整路径:Windows用.dll,macOS用.dylib,Linux用.so)
  final dylib = DynamicLibrary.open('lib.a');

  // 查找导出函数
  final getPerson = dylib.lookupFunction<GetPersonC, GetPersonDart>('GetPerson');
  final freePerson = dylib.lookupFunction<FreePersonC, FreePersonDart>('FreePerson');

  // 获取Person指针并读取字段
  final personPtr = getPerson();
  final name = personPtr.ref.name.toDartString();
  final age = personPtr.ref.age;
  final job = personPtr.ref.job.toDartString();
  final salary = personPtr.ref.salary;

  print('Name: $name, Age: $age, Job: $job, Salary: $salary');

  // 释放内存,避免泄漏
  freePerson(personPtr);
}

关键注意事项

  1. 类型严格匹配:Dart Struct的字段类型必须与Go中C结构体内存布局完全一致,例如Go的C.int对应Dart的@Int32();
  2. 内存手动管理:Go中C.CString分配的内存需通过自定义释放函数在Dart中调用释放;
  3. 平台适配编译:根据目标平台调整编译输出后缀:
    # Windows
    go build -buildmode=c-shared -o lib.dll lib.go
    # macOS
    go build -buildmode=c-shared -o lib.dylib lib.go
    # Linux
    go build -buildmode=c-shared -o lib.so lib.go
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 04:10:29