使用 dart:ffi 与 C 进行交互
Dart 的移动端、命令行和服务端应用所运行的 Dart 原生平台,均可以使用 dart:ffi 库调用原生的 C 语言 API,用于读、写、分配和销毁原生内存。
FFI 指的是 外部函数接口。类似的术语包括 原生接口 和 语言绑定。
相关的 API 文档可在
dart:ffi API 文档
查看。
下载示例文件
#
请下载完整的 ffi samples 目录,来使用本指南中的示例。以下的示例将展示如何使用 dart:ffi 库:
| hello_world | |
| primitives | |
| structs | |
| test_utils |
回顾 hello_world 示例
#
hello_world 示例 展示了如何用最少的代码调用 C 语言库。该示例可在上一节下载的 samples/ffi 中找到。
文件
#hello_world 示例包含了以下文件:
hello.dart
|
hello_world() 函数的文件。 |
pubspec.yaml
|
|
hello_library/hello.h
|
hello_world() 函数。 |
hello_library/hello.c
|
hello.h 并实现了 hello_world() 函数。 |
hello_library/hello.def
|
|
hello_library/CMakeLists.txt
|
构建 C 代码库时将创建几个文件,包括动态库
libhello.dylib(仅 macOS)、
libhello.dll(仅 Windows)或 libhello.so(仅 Linux)。
构建并执行
#以下一系列命令是构建动态库并执行 Dart 应用的示例:
$ cd hello_library
$ cmake .
...
$ make
...
$ cd ..
$ dart pub get
$ dart run hello.dart
Hello World
使用 dart:ffi
#
要学习如何使用 dart:ffi 库调用 C 函数,请查看 hello.dart 文件。本节将分析该文件的内容。
-
导入
dart:ffi。dartimport 'dart:ffi' as ffi; -
导入
path库用于合成动态库的路径。dartimport 'dart:io' show Platform, Directory; import 'package:path/path.dart' as path; -
用 C 函数的 FFI 类型签名定义一个 typedef。
请参阅 定义原生类型的接口 来了解dart:ffi库中定义的常用类型。darttypedef hello_world_func = ffi.Void Function(); -
为调用 C 函数的变量定义一个
typedef。darttypedef HelloWorld = void Function(); -
定义一个变量来保存动态库的路径。
dartfinal String libraryPath; 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', ); } else { libraryPath = path.join( Directory.current.path, 'hello_library', 'libhello.so', ); } -
加载包含 C 函数的动态库。
dartfinal dylib = ffi.DynamicLibrary.open(libraryPath); -
获取该 C 函数的引用,接着将其赋予变量。这段代码使用了步骤 2 和 3 定义的
typedef,以及步骤 4 定义的动态库变量。dartfinal HelloWorld hello = dylib .lookup<ffi.NativeFunction<hello_world_func>>('hello_world') .asFunction(); -
调用 C 函数。
darthello();
当你理解 hello_world 示例的内容后,可以进一步学习 其他的 dart:ffi 示例。
集成并加载 C 库
#根据平台和库的类型的不同,集成 / 打包 / 分发后加载原生 C 库的方式,也有所不同。
请参考以下页面和示例,来了解如何使用。
定义原生类型的接口
#
The dart:ffi library provides multiple types that implement NativeType
and represent native types in C. You can instantiate some native types.
Some other native types can be used only as markers in type signatures.
Can instantiate these type signature markers
#The following native types can be used as markers in type signatures. 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. |
Serve as type signature markers only
#The following list shows which platform-agnostic native types that serve as markers in type signatures. They can't be instantiated in Dart code.
| Dart type | Description |
|---|---|
| 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 the void type in C. |
There are also many ABI specific marker native types that extend AbiSpecificInteger. To learn how these types map on specific platforms, consult the API documentation linked in the following table.
| Dart type | Description |
|---|---|
| AbiSpecificInteger | The supertype of all ABI-specific integer types. |
| Int | Represents the int type in C. |
| IntPtr | Represents the intptr_t type in C. |
| Long | Represents the long int (long) type in C. |
| LongLong | Represents the long long type in C. |
| Short | Represents the short type in C. |
| SignedChar | Represents the signed char type in C. |
| Size | Represents the size_t type in C. |
| UintPtr | Represents the uintptr_t type in C. |
| UnsignedChar | Represents the unsigned char type in C. |
| UnsignedInt | Represents the unsigned int type in C. |
| UnsignedLong | Represents the unsigned long int type in C. |
| UnsignedLongLong | Represents the unsigned long long type in C. |
| UnsignedShort | Represents the unsigned short type in C. |
| WChar | Represents the wchar_t type in C. |
使用 package:ffigen 生成 FFI 的绑定
#
为大量的 API 编写绑定可能要花费你的大量时间。你可以使用 package:ffigen
绑定生成器,自动地从 C 头文件生成 FFI 包装,从而减少时间消耗。
要了解 Dart FFI 对代码资源的支持,请查阅 dart:ffi API 参考文档中的
Native
和 DefaultAsset。
Build and bundle native code
#Dart build hooks (formerly known as native assets) enable packages to contain native code assets that are transparently built, bundled, and made available at runtime. For more information, see Hooks.