目錄

使用 dart:ffi 與 C 進行互動

Dart 的移動端、命令列和伺服器端應用所執行的 Dart 原生平台,均可以使用 dart:ffi 庫呼叫原生的 C 語言 API,用於讀、寫、分配和銷燬原生記憶體。 FFI 指的是 外部函式介面。類似的術語包括 原生介面語言繫結

相關的 API 文件可在 dart:ffi API 文件 檢視。

範例

以下的範例將展示如何使用 dart:ffi 庫:

範例 描述
hello_world 如何呼叫無引數和返回值的 C 語言函式。
primitives 如何呼叫引數和返回值為 整型和指標 的 C 語言函式。同時示範 varargs
structs 如何與 C 語言互相傳遞字串,以及如何處理 C 語言定義的結構
sqlite Dart SDK 儲存庫中包含的 小型範例

快速上手的 hello_world

hello_world 範例 展示瞭如何用最少的程式碼呼叫 C 語言庫。

檔案

hello_world 範例包含了以下檔案:

原始檔 描述
hello.dart 使用了 C 語言庫中的 hello_world() 函式的檔案。
pubspec.yaml Dart 裡常見的 pubspec 檔案,最低 SDK 限制為 2.6。
hello_library/hello.h 聲明瞭 hello_world() 函式。
hello_library/hello.c 該 C 檔案匯入了 hello.h 並實現了 hello_world() 函式。
hello_library/hello.def 包含 DLL 建構資訊的模組定義。
hello_library/CMakeLists.txt 將 C 檔案程式碼編譯為動態庫的 CMake 檔案。

建構 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

hello.dart 檔案 闡述了使用 dart:ffi 呼叫 C 函式的步驟:

  1. 匯入 dart:ffi

  2. 匯入 path 庫用於合成動態庫的路徑。

  3. 為 C 函式的 FFI 型別簽名的定義一個類別型。

  4. 為呼叫 C 函式的變數定義一個類別型。

  5. 利用一個變數儲存動態庫的路徑。

  6. 載入包含 C 函式的動態庫。

  7. 建立該 C 函式的參考,接著將其賦予變數。

  8. 呼叫 C 函式。

以下是每一個步驟對應的程式碼。

  1. 匯入 dart:ffi。

import 'dart:ffi' as ffi;
  1. 匯入 path 庫用於合成動態庫的路徑。

import 'dart:io' show Platform, Directory;
import 'package:path/path.dart' as path;
  1. 為 C 函式的 FFI 型別簽名的定義一個類別型。
    參閱 定義原生型別的介面 瞭解 dart:ffi 庫中定義的常用型別。

typedef hello_world_func = ffi.Void Function();
  1. 為呼叫 C 函式的變數定義一個類別型。

typedef HelloWorld = void Function();
  1. 利用一個變數儲存動態庫的路徑。

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 函式的動態庫。

  final dylib = ffi.DynamicLibrary.open(libraryPath);
  1. 建立該 C 函式的參考,接著將其賦予變數。這段程式碼使用了步驟 2 和 3 定義的型別,以及步驟 4 建立的動態庫變數。

  final HelloWorld hello = dylib
      .lookup<ffi.NativeFunction<hello_world_func>>('hello_world')
      .asFunction();
  1. 呼叫 C 函式。

  hello();

當你理解 hello_world 範例的內容後,可以進一步學習 其他的 dart:ffi 範例

整合並載入 C 庫

根據平臺和庫的型別的不同,捆綁(或 打包分發) C 庫到 package 或應用並進行載入的方式,有所不同。

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.

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.

Purely marker native types

The following are platform-agnostic native types that are used only as markers in type signatures, and 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. Refer to their linked API documentation for more information and a guideline on what types they map to on specific platforms:

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 (unsigned long) 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 包裝,從而減少時間消耗。