跳到主要內容

使用 dart:ffi 的 C 語言互通性

在 Dart Native 平台上執行的 Dart 行動、命令列和伺服器應用程式可以使用 dart:ffi 函式庫來呼叫原生 C API,以及讀取、寫入、分配和釋放原生記憶體。FFI 代表外部函式介面 (foreign function interface)。類似功能的其他術語包括原生介面語言綁定

API 文件可在dart:ffi API 參考中找到。

下載範例檔案

#

若要使用本指南中的範例,請下載完整的ffi 範例目錄。其中包含以下範例,示範如何使用 dart:ffi 函式庫

範例說明
hello_world如何呼叫沒有引數和沒有傳回值的 C 語言函式。
primitives如何呼叫具有引數和傳回值為整數或指標的 C 語言函式。
structs如何使用 structs 來傳遞字串到 C 語言和從 C 語言傳遞字串,以及處理簡單和複雜的 C 語言結構
test_utils適用於所有這些範例的通用測試工具。

檢閱 hello_world 範例

#

hello_world 範例具有呼叫 C 語言函式庫所需的最低限度程式碼。此範例可在您在前一節下載的 samples/ffi 中找到。

檔案

#

hello_world 範例包含下列檔案

原始碼檔案說明
hello.dart使用 C 語言函式庫中的 hello_world() 函式的 Dart 檔案。
pubspec.yamlDart pubspec 檔案,SDK 下限為 3.4。
hello_library/hello.h宣告 hello_world() 函式。
hello_library/hello.c匯入 hello.h 並定義 hello_world() 函式的 C 語言檔案。
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

#

若要瞭解如何使用 dart:ffi 函式庫呼叫 C 語言函式,請檢閱 hello.dart 檔案。本節說明此檔案的內容。

  1. 匯入 dart:ffi

    dart
    import 'dart:ffi' as ffi;
  2. 匯入 path 函式庫,您將使用它來儲存動態函式庫的路徑。

    dart
    import 'dart:io' show Platform, Directory;
    import 'package:path/path.dart' as path;
  3. 使用 C 語言函式的 FFI 類型簽章建立 typedef。
    若要瞭解根據 dart:ffi 函式庫最常用的類型,請參閱與原生類型介接

    dart
    typedef hello_world_func = ffi.Void Function();
  4. 為呼叫 C 語言函式時要使用的變數建立 typedef。

    dart
    typedef HelloWorld = void Function();
  5. 建立變數來儲存動態函式庫的路徑。

    dart
    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');
    }
  6. 開啟包含 C 語言函式的動態函式庫。

    dart
    final dylib = ffi.DynamicLibrary.open(libraryPath);
  7. 取得 C 語言函式的參考,並將其放入變數中。此程式碼使用步驟 2 和 3 的 typedef,以及步驟 4 的動態函式庫變數。

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

    dart
    hello();

一旦您瞭解 hello_world 範例,請參閱其他 dart:ffi 範例

捆綁和載入 C 語言函式庫

#

捆綁/封裝/發布然後載入原生 C 語言函式庫的方法取決於平台和函式庫類型。

若要瞭解如何操作,請參閱以下頁面和範例。

  • 適用於 Android 應用程式的 Flutter dart:ffi
  • 適用於 iOS 應用程式的 Flutter dart:ffi
  • 適用於 macOS 應用程式的 Flutter dart:ffi
  • dart:ffi 範例

與原生類型介接

#

dart:ffi 函式庫提供多種實作 NativeType 並表示 C 語言原生類型的類型。您可以實例化某些原生類型。其他一些原生類型只能在類型簽章中用作標記。

可以實例化這些類型簽章標記

#

以下原生類型可以用作類型簽章中的標記。它們或其子類型可以在 Dart 程式碼中實例化。

Dart 類型說明
陣列固定大小的項目陣列。類型特定陣列的超類型。
指標表示指向原生 C 語言記憶體的指標。
Struct所有 FFI 結構類型的超類型。
Union所有 FFI 聯集類型的超類型。

僅作為類型簽章標記

#

以下列表顯示哪些平台無關的原生類型在類型簽章中用作標記。它們無法在 Dart 程式碼中實例化。

Dart 類型說明
Bool表示 C 語言中的原生布林值。
Double表示 C 語言中的原生 64 位元雙精度浮點數。
Float表示 C 語言中的原生 32 位元單精度浮點數。
Int8表示 C 語言中的原生有號 8 位元整數。
Int16表示 C 語言中的原生有號 16 位元整數。
Int32表示 C 語言中的原生有號 32 位元整數。
Int64表示 C 語言中的原生有號 64 位元整數。
NativeFunction表示 C 語言中的函式類型。
OpaqueC 語言中所有不透明類型的超類型。
Uint8表示 C 語言中的原生無號 8 位元整數。
Uint16表示 C 語言中的原生無號 16 位元整數。
Uint32表示 C 語言中的原生無號 32 位元整數。
Uint64表示 C 語言中的原生無號 64 位元整數。
Void表示 C 語言中的 void 類型。

還有許多 ABI 特定的標記原生類型擴充了 AbiSpecificInteger。若要瞭解這些類型如何在特定平台上對應,請參閱下表中連結的 API 文件。

Dart 類型說明
AbiSpecificInteger所有 ABI 特定整數類型的超類型。
Int表示 C 語言中的 int 類型。
IntPtr表示 C 語言中的 intptr_t 類型。
Long表示 C 語言中的 long int (long) 類型。
LongLong表示 C 語言中的 long long 類型。
Short表示 C 語言中的 short 類型。
SignedChar表示 C 語言中的 signed char 類型。
Size表示 C 語言中的 size_t 類型。
UintPtr表示 C 語言中的 uintptr_t 類型。
UnsignedChar表示 C 語言中的 unsigned char 類型。
UnsignedInt表示 C 語言中的 unsigned int 類型。
UnsignedLong表示 C 語言中的 unsigned long int 類型。
UnsignedLongLong表示 C 語言中的 unsigned long long 類型。
UnsignedShort表示 C 語言中的 unsigned short 類型。
WChar表示 C 語言中的 wchar_t 類型。

使用 package:ffigen 產生 FFI 綁定

#

對於大型 API 介面,撰寫與 C 語言程式碼整合的 Dart 綁定可能很耗時。若要讓 Dart 從 C 語言標頭檔建立 FFI 包裝函式,請使用 package:ffigen 綁定產生器。

建置和捆綁原生資源

#

原生資源功能應解決與依賴原生程式碼的 Dart 套件發布相關的許多問題。它透過提供統一的 hook 來與建置 Flutter 和獨立 Dart 應用程式所涉及的各種建置系統整合來實現這一點。

此功能應簡化 Dart 套件依賴和使用原生程式碼的方式。原生資源應提供以下優點

  • 使用套件的 hook/build.dart 建置 hook 來建置原生程式碼或取得二進位檔。
  • 捆綁 build.dart 建置 hook 報告的原生 Asset
  • 使用 assetId 透過宣告式 @Native<>() extern 函式使原生資源在執行階段可用。

當您選擇加入原生實驗性功能時,flutter (run|build)dart (run|build) 命令會建置原生程式碼並將其與 Dart 程式碼捆綁在一起。

檢閱 native_add_library 範例

#

native_add_library 範例包含在 Dart 套件中建置和捆綁 C 語言程式碼所需的最低限度程式碼。

此範例包含下列檔案

原始碼檔案說明
src/native_add_library.c包含 add 程式碼的 C 語言檔案。
lib/native_add_library.dart透過 FFI 在資源 package:native_add_library/native_add_library.dart 中調用 C 語言函式 add 的 Dart 檔案。(請注意,資源 ID 預設為函式庫 URI。)
test/native_add_library_test.dart使用原生程式碼的 Dart 測試。
hook/build.dart用於編譯 src/native_add_library.c 並宣告資源 ID 為 package:native_add_library/native_add_library.dart 的編譯資源的建置 hook。

當 Dart 或 Flutter 專案依賴 package:native_add_library 時,它會在 runbuildtest 命令上調用 hook/build.dart 建置 hook。native_add_app 範例展示了 native_add_library 的用法。

檢閱原生資源 API 文件

#

以下套件的 API 文件可在以下位置找到

選擇加入實驗性功能

#

若要瞭解如何啟用實驗性功能並提供意見回饋,請參閱這些追蹤問題