目录

Contents

使用 dart:ffi 与 C 进行交互

Dart 的移动端、命令行和服务端应用所运行的 Dart 原生平台,均可以使用 dart:ffi 库调用原生的 C 语言 API,用于读、写、分配和销毁原生内存。 FFI 指的是 外部函数接口。类似的术语包括 原生接口语言绑定

Dart mobile, command-line, and server apps running on the Dart Native platform can use the dart:ffi library to call native C APIs, and to read, write, allocate, and deallocate native memory. FFI stands for foreign function interface. Other terms for similar functionality include native interface and language bindings.

相关的 API 文档可在 dart:ffi API 文档 查看。

API documentation is available in the dart:ffi API reference.

示例

Examples

以下的示例将展示如何使用 dart:ffi 库:

The following examples show how to use the dart:ffi library:

示例

Example

描述

Description

hello_world

如何调用无参数和返回值的 C 语言函数。

How to call a C function with no arguments and no return value.

primitives

如何调用参数和返回值为 整型和指针 的 C 语言函数。同时演示 varargs

How to call C functions that have arguments and return values that are ints or pointers. Also demonstrates varargs.

structs

如何与 C 语言互相传递字符串,以及如何处理 C 语言定义的结构

How to use structs to pass strings to and from C and to handle simple and complex C structures.

sqlite

Dart SDK 仓库中包含的 小型示例

An example in the Dart SDK repo that comes with a mini tutorial.

快速上手的 hello_world

Walkthrough of hello_world

hello_world 示例 展示了如何用最少的代码调用 C 语言库。

The hello_world example has the minimum necessary code for calling a C library.

文件

Files

hello_world 示例包含了以下文件:

The hello_world example has the following files:

源文件

Source file

描述

Description

hello.dart

使用了 C 语言库中的 hello_world() 函数的文件。

A Dart file that uses the hello_world() function from a C library.

pubspec.yaml

Dart 里常见的 pubspec 文件,最低 SDK 限制为 2.6。

The usual Dart pubspec, with a lower bounds on the SDK that’s at least 2.6.

hello_library/hello.h

声明了 hello_world() 函数。

Declares the hello_world() function.

hello_library/hello.c

该 C 文件导入了 hello.h 并实现了 hello_world() 函数。

A C file that imports hello.h and defines the hello_world() function.

hello_library/hello.def

包含 DLL 构建信息的模块定义。

A module-definition file which specifies information used when building a DLL.

hello_library/CMakeLists.txt

将 C 文件代码编译为动态库的 CMake 文件。

A CMake build file for compiling the C code into a dynamic library.

构建 C 代码库时将创建几个文件,包括动态库 libhello.dylib(仅 macOS)、 libhello.dll(仅 Windows)或 libhello.so(仅 Linux)。

Building the C library creates several files, including a dynamic library file named libhello.dylib (macOS), libhello.dll (Windows), or libhello.so (Linux).

构建并运行

Building and running

以下是构建动态库并执行 Dart 应用的示例:

Here’s an example of building the dynamic library and executing the Dart app:

$ cd hello_library
$ cmake .
...
$ make
...
$ cd ..
$ dart pub get
$ dart run hello.dart
Hello World

使用 dart:ffi

Using dart:ffi

hello.dart 文件 阐述了使用 dart:ffi 调用 C 函数的步骤:

The hello.dart file illustrates the steps for using dart:ffi to call a C function:

  1. 导入 dart:ffi

    Import dart:ffi.

  2. 导入 path 库用于合成动态库的路径。

    Import the path library that you’ll use to store the path of dynamic library.

  3. 为 C 函数的 FFI 类型签名的定义一个类型。

    Create a typedef with the FFI type signature of the C function.

  4. 为调用 C 函数的变量定义一个类型。

    Create a typedef for the variable that you’ll use when calling the C function.

  5. 利用一个变量保存动态库的路径。

    Create a variable to store the path of the dynamic library.

  6. 加载包含 C 函数的动态库。

    Open the dynamic library that contains the C function.

  7. 创建该 C 函数的引用,接着将其赋予变量。

    Get a reference to the C function, and put it into a variable.

  8. 调用 C 函数。

    Call the C function.

以下是每一个步骤对应的代码。

Here’s the code for each step.

  1. 导入 dart:ffi。

    Import dart:ffi.

import 'dart:ffi' as ffi;
  1. 导入 path 库用于合成动态库的路径。

    Import the path library that you’ll use to store the path of dynamic library.

import 'dart:io' show Platform, Directory;
import 'package:path/path.dart' as path;
  1. 为 C 函数的 FFI 类型签名的定义一个类型。
    参阅 定义原生类型的接口 了解 dart:ffi 库中定义的常用类型。

    Create a typedef with the FFI type signature of the C function.
    See Interfacing with native types for commonly used types defined by dart:ffi library.

typedef hello_world_func = ffi.Void Function();
  1. 为调用 C 函数的变量定义一个类型。

    Create a typedef for the variable that you’ll use when calling the C function.

typedef HelloWorld = void Function();
  1. 利用一个变量保存动态库的路径。

    Create a variable to store the path of the dynamic library.

var libraryPath = path.join(Directory.current.path, 'hello_library',
    'libhello.so');
if (Platform.isMacOS) { 
  libraryPath = path.join(Directory.current.path, 'hello_library', 
      'libhello.dylib');
} else if (Platform.isWindows) { 
  libraryPath = path.join(Directory.current.path, 'hello_library', 
      'Debug', 'hello.dll');
} 
  1. 加载包含 C 函数的动态库。

    Open the dynamic library that contains the C function.

  final dylib = ffi.DynamicLibrary.open(libraryPath);
  1. 创建该 C 函数的引用,接着将其赋予变量。这段代码使用了步骤 2 和 3 定义的类型,以及步骤 4 创建的动态库变量。

    Get a reference to the C function, and put it into a variable. This code uses the typedefs defined in steps 2 and 3, along with the dynamic library variable from step 4.

  final HelloWorld hello = dylib
      .lookup<ffi.NativeFunction<hello_world_func>>('hello_world')
      .asFunction();
  1. 调用 C 函数。

    Call the C function.

  hello();

当你理解 hello_world 示例的内容后,可以进一步学习 其他的 dart:ffi 示例

Once you understand the hello_world example, you should be ready to look at the other dart:ffi examples.

集成并加载 C 库

Bundling and loading C libraries

根据平台和库的类型的不同,捆绑(或 打包分发) C 库到 package 或应用并进行加载的方式,有所不同。

How you bundle (or package or distribute) a C library with your package or app and then load that library depends on your platform and the type of library. For details, see the following:

Interfacing with native types

The dart:ffi library provides multiple types that implement NativeType and represent native types in C.

Some native types are only used as markers in type signatures while others (or their subtypes) can be instantiated.

Purely marker native types

The following table lists some native types that are only used as markers in type signatures, and can’t be instantiated in Dart code:

Dart type Description
AbiSpecificInteger The supertype of all Abi specific integer types.
Bool Represents a native bool in C.
Double Represents a native 64 bit double in C.
Float Represents a native 32 bit float in C.
Int8 Represents a native signed 8 bit integer in C.
Int16 Represents a native signed 16 bit integer in C.
Int32 Represents a native signed 32 bit integer in C.
Int64 Represents a native signed 64 bit integer in C.
NativeFunction Represents a function type in C.
Opaque The supertype of all opaque types in C.
Uint8 Represents a native unsigned 8 bit integer in C.
Uint16 Represents a native unsigned 16 bit integer in C.
Uint32 Represents a native unsigned 32 bit integer in C.
Uint64 Represents a native unsigned 64 bit integer in C.
Void Represents a void type in C.

Instantiable native types

The following native types can be used as markers in type signatures and they (or their subtypes) can be instantiated in Dart code:

Dart type Description
Array A fixed-sized array of items. Supertype of type specific arrays.
Pointer Represents a pointer into native C memory.
Struct The supertype of all FFI struct types.
Union The supertype of all FFI union types.

使用 package:ffigen 生成 FFI 的绑定

Generating FFI bindings with package:ffigen

为大量的 API 编写绑定可能要花费你的大量时间。你可以使用 package:ffigen 绑定生成器,自动地从 C 头文件生成 FFI 包装,从而减少时间消耗。

For large API surfaces it can be time-consuming to write the Dart bindings that integrate with the C code. To reduce this burden, you can use the package:ffigen binding generator to automatically create FFI wrappers from C header files.