Dart 如何使用C語言函式庫 (Windows)

閱讀時間約 54 分鐘
  雖然Dart 語言本身支援跨平台的編譯方式,但在實務開發時還是不免需要使用外部非Dart語言所提供的函式庫進行功能開發且由於C 語言是最為廣泛且通用的程式語言,因此Dart語言也有提供支援與C語言函式庫互通性的方式;本篇主要是以MSVC作為C的編譯器來實作說明如何引用C語言會遇到的作法。

1.  動態載入C語言函式庫

  與一般C語言載入函式庫的方式相同在Dart語言中載入C函式語言函式庫同樣是操作流程: 1) 定義函式指標  2 ) 開啟C函式庫  3) 查找函式位址
#include <windows.h> 
 
// 1. Define function pointer for imported function prototypes.
typedef BOOL(__cdecl* CreateClassInstanceProc) (__inout HANDLE* ClassHandle);
// 2. Open and load DLL  
HINSTANCE hinstLib = LoadLibrary(TEXT("crosscode_wrapc_lib.dll"));
// 3. Find function addresses in loadded DLLs.
CreateClassInstanceProc CCIP =
  (CreateClassInstanceProc) GetProcAddress(hinstLib, "CreateClassInstance");
  而在Dart 語言中要完成上述載入函式庫的流程則需要先引用dart:ffi文件,且由於在dart:ffi中定義的NativeType資料格式與Dart中所對應的資料格式不完全對應,因此定義C函式指標需要同時以Dart 資料格式定義函式指標和以NativeType資料格式定義函式指標來進行對照。
import 'dart:ffi'
 
// 1. Define function pointer for imported function prototypes.
typedef CreateClassInstanceForNative = Int32 Function(Pointer classHandle);
typedef CreateClassInstanceForDarte = int Function(Pointer classHandle);
// 2. Open and load DLL  
final _wrapc_lib = 
  DynamicLibrary.open("crosscode_wrapc_lib.dll");
// 3. Find function addresses in loadded DLLs.
final createClassInstanceProc = _wrapc_lib.lookupFunction<CreateClassInstanceForNative, CreateClassInstanceForDarte>('CreateClassInstance');

2.  定義資料結構

  在引用C函數庫時常需要使用其定義的相關資結構作為傳遞參數則如何在Dart 語言中定義C函數可接受的資料結構是另一個主要的問題;本節將會自訂包含多個常用資料格式 (wchar_t, array, function pointer ... etc.) 的C語言資料結構並以此資料結構作為參數建立相關的函式來測試Dart語言轉換C語言資料結構的方法。   
typedef bool (*CallbackFunc)(double* pDoubleArray);
typedef bool (*CopyStructCallbackFunc)(PVOID pStruct);
typedef struct _UnrestrictedAlignmentStruct {
  int	intField;
  TCHAR charField;
  CallbackFunc funcptrField;
  float floatField;
  wchar_t stringField[15];
  double dubaryField[10];
} UnrestrictedAlignmentStruct, * pUnrestrictedAlignmentStruct;
#pragma pack(push, 1)
typedef struct _RestrictedAlignmentStruct {
  int	intField;
  TCHAR charField;
  CallbackFunc funcptrField;
  float floatField;
  wchar_t stringField[15];
  double dubaryField[10];
} RestrictedAlignmentStruct, *pRestrictedAlignmentStruct;
#pragma pack(pop)
  要在Dart語言中定義符合C函數的資料結構要使用 dart:ffi 文件中所定義的NativeType資料格式,在Dart語言中使用NativeType定義與C語言函式互通的資料結構需要特別指定Dart資料格式與NativeType資料格式的對照格式。
import 'dart:ffi';
typedef CallbackFuncForDart = int Function( Pointer<Double> pDoubleArray);
typedef CallbackFuncForFFI = Uint32 Function(Pointer<Double> pDoubleArray);
typedef CopyStructCallbackFuncForFFI = Bool Function(Pointer pStruct);
class UnrestrictedAlignmentStruct extends Struct {
  @Int32()
  external int intField;  
  @Uint16()
  external int charField;  
  external Pointer<NativeFunction<CallbackFuncForFFI>> funcptrField;  
  @Float()
  external double floatField;  
  @Array(15)
  external Array<Int16> stringField;  
  @Array(10)
  external Array<Double> dubaryField; 
}
@Packed(1)
class RestrictedAlignmentStruct extends Struct {
  @Int32()
  external int intField;
  @Int16()
  external int charField;  
  external Pointer<NativeFunction<CallbackFuncForFFI>> funcptrField;  
  @Float()
  external double floatField;
  @Array(15)
  external Array<Int16> stringField;
  @Array(10)
  external Array<Double> dubaryField; 
}

3.  實作測試函式

  為了確認上述定義的資料結構與C語言函式庫是否能正確的傳遞與讀取資料結構的資訊,因此特別針常發生的使用情境定義下列其相應的函式進行驗證與實際執行的方式。

  3-1.  記憶體配置的長度

    由於在不同的程式語言中定義的資料結構可能因為其資料格式的長度差異而佔用的記憶體配置的大小不同而造成資料在轉換兩種不同的語言中傳遞錯誤;因此定義回傳在C語言函式中資料結構的記憶體配置長度的函式與Dart語言中相同資料結構是否為相同長度。
// Create a data struct in C executables
UnrestrictedAlignmentStruct sUnrestrictedAlignmentStruct = { 0 };
// Define the function prototype of the C library
typedef int(__stdcall* GetSizeOfUnrestrictedAlignmentStructProc) 
        (HANDLE pClassHandle, UnrestrictedAlignmentStruct sStrut);
// Find the the pointer of function in the C library
GetSizeOfUnrestrictedAlignmentStructProc getSizeOfUnrestrictedAlignmentStructProc =
        (GetSizeOfUnrestrictedAlignmentStructProc)GetProcAddress(hinstLib, "GetSizeOfUnrestrictedAlignmentStruct");
                  
// Display the memory size of data structures in executeables and libraries.
wprintf(L"Used memory size of an unrestricted alignment struct in execute(%llu) and library(%d)\n", 
        sizeof(sUnrestrictedAlignmentStruct),
        getSizeOfUnrestrictedAlignmentStructProc(hinstLib, sUnrestrictedAlignmentStruct));
// Create a data struct in Dart executables
final sUnrestrictedAlignmentStruct = calloc<UnrestrictedAlignmentStruct>()
// Define the function prototype of the C library
typedef GetSizeOfUnrestrictedAlignmentStructProcForNative = 
    int Function(Pointer classHandle,RestrictedAlignmentStruct confirmStrut);
typedef GetSizeOfUnrestrictedAlignmentStructProcForDart = 
    Int32 Function(Pointer classHandle,RestrictedAlignmentStruct confirmStrut);
// Find the the pointer of function in the C library
late final getSizeOfUnrestrictedAlignmentStructProc = _wrapc_lib.lookupFunction<
    GetSizeOfUnrestrictedAlignmentStructProcForDart,
    GetSizeOfUnrestrictedAlignmentStructProcForNative
  >('GetSizeOfUnrestrictedAlignmentStructProc');
// Display the memory size of data structures in executeables and libraries.
int retSize = getSizeOfUnrestrictedAlignmentStructProc(classHandle.value, sUnrestrictedAlignmentStruct.ref);
print("Used memory size of an unrestricted alignment struct in execute(${sizeOf<UnrestrictedAlignmentStruct>()}) and library( $retSize)");

  3-2.  字元轉換的正確性    

    由於不同程式語言可能採用的預設字元編碼(utf16, utf6, unicode... etc.)不同而可能造成函式在傳送與接送字串因編碼錯誤而造成判斷有誤甚至是造成執行崩潰(crash)或執行錯誤的情況,因此在C函式庫中定義函式用於判斷傳遞的資料結構的字串與函式庫中字串是否相同。
// Build and initial data structure in the C executable.
static const TCHAR* pCmpStr = L"Test String";
UnrestrictedAlignmentStruct sUnrestrictedAlignmentStruct = { 0 };
wcsncpy_s(sUnrestrictedAlignmentStruct.stringField, wcslen(pCmpStr) + 1, pCmpStr, sizeof(sUnrestrictedAlignmentStruct.stringField));
// Define the function prototype of the C library.
typedef bool (__stdcall* CompareStringOfUnrestrictedAlignmentStructProc)
      (HANDLE pClassHandle, pUnrestrictedAlignmentStruct pStrut);
// Find the the pointer of function in the C library.
CompareStringOfUnrestrictedAlignmentStructProc compareUnrestrictedAlignmentStructProc =
      (CompareStringOfUnrestrictedAlignmentStructProc)GetProcAddress(hinstLib, "CompareStringOfUnrestrictedAlignmentStruct");
        
// Compare the string of data structure in the C library.
wprintf(L"Compare string in C library : %s\n",
      compareUnrestrictedAlignmentStructProc(hinstLib, &sUnrestrictedAlignmentStruct) == 1 ? L"True " : L"False");
// Convert string to int array and copy to the stringField of structure.
extension StringArrayFill<T> on Array<Int16> {
  void fillFromList(List<T> list) {
    for (var i = 0; i < list.length; i++) {
      this[i] = list[i] as int;
}}}
// Build and initial data structure in the Dart executable.
final String pCmpStr = "Test String";
final sUnrestrictedAlignmentStruct = calloc<UnrestrictedAlignmentStruct>()
    ..ref.stringField.fillFromList(pCmpStr.codeUnits);
// Define the function prototype of the C library.
typedef CompareStringOfUnrestrictedAlignmentStructProcForNative = int Function(Pointer classHandle,Pointer<UnrestrictedAlignmentStruct> pStruct);
typedef CompareStringOfUnrestrictedAlignmentStructProcForDart = Int32 Function(Pointer classHandle,Pointer<UnrestrictedAlignmentStruct> pStruct);
// Find the the pointer of function in the C library.
late final compareStringOfUnrestrictedAlignmentStructProc = _wrapc_lib.lookupFunction<
    CompareStringOfUnrestrictedAlignmentStructProcForDart,
    CompareStringOfUnrestrictedAlignmentStructProcForNative
  >('CompareStringOfUnrestrictedAlignmentStruct');
// Compare the string of data structure in the C library
String isSame = compareStringOfUnrestrictedAlignmentStructProc(classHandle.value, sUnrestrictedAlignmentStruct) == 1 ? "True" : "False";  
print("Compare string in C library : $isSame&quot;);   

  3-3.  回調函數的執行

    回調函數是一種常用於程式與程式之間互相建立傳遞通知訊息的手段,且由於是傳遞函式的指標位置且不同的程式語言對於函式參數載入與讀取的方式也不盡相同,因此若在轉換過程中發生誤差則可能造成執行崩潰(crash)或執行錯誤的情況,所以在函式庫中定義函式來執行資料結構中的回調函數。
// Define the prototype of the callback function
typedef int (*CallbackFunc)(double* pDoubleArray);
typedef bool (*CopyStructCallbackFunc)(PVOID pStruct);
// Define the callback function of data structure.
int comparArray(double *cmpArray) {
  for (int i = 0; i < 10; i++) {
    if (cmpArray[i] != notifyDouble[i]) 
      return 0;
  }
  return 1;
}
// Define double array 
static double notifyDouble[] = {
  0, 0.1, 2.2, 3.33, 44.44, 55.555, 666.666, 777.7777,
  8888.8888, 99999.9999 };
// Define the function prototype of the C library.
typedef bool(__stdcall* NotifyCallbackOfUnrestrictedAlignmentStructProc)
      (HANDLE pClassHandle, pUnrestrictedAlignmentStruct pStruct);
// Build and initial data structure in the C executable.
UnrestrictedAlignmentStruct sUnrestrictedAlignmentStruct = { 0 };
sUnrestrictedAlignmentStruct.funcptrField = comparArray;
for (int i = 0; i < 10; i++) {
  sUnrestrictedAlignmentStruct.dubaryField[i] = notifyDouble[i];
}
// Find the the pointer of function in the C library.
NotifyCallbackOfUnrestrictedAlignmentStructProc notifyCallbackOfUnrestrictedAlignmentStructProc =
      (NotifyCallbackOfUnrestrictedAlignmentStructProc)GetProcAddress(hinstLib, "NotifyCallbackOfUnrestrictedAlignmentStruct");
  
// Execute callback function for unrestricted alignment structure in C library.
wprintf(L"Execute callback function in C library : %d\n", 
      notifyCallbackOfUnrestrictedAlignmentStructProc(hinstLib, &sUnrestrictedAlignmentStruct));
// Define the prototype of the callback function
typedef CallbackFuncForDart = int Function( Pointer<Double> pDoubleArray);
typedef CallbackFuncForFFI = Uint32 Function(Pointer<Double> pDoubleArray);
typedef CopyStructCallbackFuncForFFI = Bool Function(Pointer pStruct);
// Define the callback function of data structure.
int checkArray(List<double> cmpArray){
  for (int i =0; i < cmpArray.length;i++){
    if (cmpArray.elementAt(i) != notifyDouble.elementAt(i)) {  
      return 0;
  }}
  return 1;
}
// Define double array 
final List<double> notifyDouble = [ 0, 0.1, 2.2, 3.33, 44.44, 55.555, 666.666, 777.7777,
    8888.8888, 99999.9999];
// Define the function prototype of the C library.
typedef NotifyCallbackOfUnrestrictedAlignmentStructProcForNative = Int32 Function(Pointer pClassHandle, Pointer<UnrestrictedAlignmentStruct> pStruct);
typedef NotifyCallbackOfUnrestrictedAlignmentStructProcForDart = int Function(Pointer pClassHandle, Pointer<UnrestrictedAlignmentStruct> pStruct);
// Build and initial data structure in the Dart executable.
final sUnrestrictedAlignmentStruct = calloc<UnrestrictedAlignmentStruct>()
    ..ref.funcptrField = Pointer.fromFunction<CallbackFuncForFFI>(comparArray, 0)      
// Find the the pointer of function in the C library.
late final notifyCallbackOfUnrestrictedAlignmentStructProc = wrapclib.lookupFunction<
    NotifyCallbackOfUnrestrictedAlignmentStructProcForNative,
    NotifyCallbackOfUnrestrictedAlignmentStructProcForDart
  >('NotifyCallbackOfUnrestrictedAlignmentStruct');
// Execute callback function for unrestricted alignment structure in C library.
isSame = notifyCallbackOfUnrestrictedAlignmentStructProc(classHandle.value, sUnrestrictedAlignmentStruct) == 1 ? "True":"False";
print("Execute callback function in C library : $isSame");

  3-4.  驗證資料結構的資料

     由於資料結構可能由許多不同的資料格式所組成,因此可能在傳遞時發生部份資料內容有錯誤,所以在函式庫中定義複製資料結構的函式並透過回調函式回傳至執行檔中顯示其複製結果。
// Define a callback function to display replicated data. 
bool getUnrestrictedAlignmentStructProc(PVOID pStruct) {    
  pUnrestrictedAlignmentStruct pUAS = (pUnrestrictedAlignmentStruct) pStruct;
  wprintf(L"stringField : %s    floatField : %f     intField : %d     charField : %c \n",
        
  pUAS->stringField, pUAS->floatField, pUAS->intField, pUAS->charField);
  wprintf(L"Verify the data of dubaryField : %s\n", comparArray(pUAS->dubaryField) == 1 ? L"True" : L"False");
  bool bRetVal = pUAS->funcptrField(notifyDouble) == 1;
  wprintf(L"Execute the function porinter of pointerField : %s", bRetVal ? L"True" : L"False");
  return bRetVal;
}
// Define the function prototype of the C library.
typedef bool(__stdcall* CopyDataOfUnrestrictedAlignmentStructWithCallbackProc) 
      (HANDLE pClassHandle, UnrestrictedAlignmentStruct sStruct, CopyStructCallbackFunc pCopyCallbackFunc);
// Build and initial data structure in the C executable.
UnrestrictedAlignmentStruct sUnrestrictedAlignmentStruct = { 0 };
sUnrestrictedAlignmentStruct.charField = 'A';
sUnrestrictedAlignmentStruct.floatField = 456.123f;
sUnrestrictedAlignmentStruct.intField = 123;
sUnrestrictedAlignmentStruct.funcptrField = comparArray;
wcsncpy_s(sUnrestrictedAlignmentStruct.stringField, wcslen(pCmpStr) + 1, pCmpStr, sizeof(sUnrestrictedAlignmentStruct.stringField));
for (int i = 0; i < 10; i++) {
  sUnrestrictedAlignmentStruct.dubaryField[i] = notifyDouble[i];
}
// Find the the pointer of function in the C library.
CopyDataOfUnrestrictedAlignmentStructWithCallbackProc copyDataOfUnrestrictedAlignmentStructWithCallbackProc =
      (CopyDataOfUnrestrictedAlignmentStructWithCallbackProc) GetProcAddress(hinstLib, "CopyDataOfUnrestrictedAlignmentStructWithCallback");
       
// Validate each field of the replicated data structure in C library.
wprintf(L"\nCopy structure data in C library : %d\n", 
      copyDataOfUnrestrictedAlignmentStructWithCallbackProc(hinstLib, sUnrestrictedAlignmentStruct, getUnrestrictedAlignmentStructProc));
// Define a callback function to display replicated data. 
int getUnrestrictedAlignmentStructProc(Pointer pStruct){
  int iRetVal = 0;
  var pUAS = pStruct.cast<UnrestrictedAlignmentStruct>();
  List<int> strList = [];
  List<double> doubleList =[] ;

  pUAS.ref.dubaryField.fillFromArray(doubleList,10);
  pUAS.ref.stringField.fillFromArray(strList, 15);
  print ('stringField : ${String.fromCharCodes (strList)} floatField : ${pUAS.ref.floatField} intField : ${pUAS.ref.intField} charField : ${String.fromCharCode(pUAS.ref.charField)}');

  var pPassParm = calloc.allocate<Double>(sizeOf<Double>() * 10);
  for (int i = 0;i<10;i++) {
    pPassParm[i] = doubleList[i];
  }

  String isSame = comparArray(pPassParm) == 1 ? "True" : "False";
  print("Verify the data of dubaryField : $isSame");

  final callbackFuncProc = pUAS.ref.funcptrField
      .cast<NativeFunction<CallbackFuncForFFI>>().asFunction<CallbackFuncForDart>();

  iRetVal = callbackFuncProc(pPassParm);
  isSame = iRetVal == 1 ? "True" : "False";
  print ("Execute the function porinter of pointerField : $isSame");

  calloc.free(pPassParm);
  return iRetVal;
}
// Define the function prototype of the C library.
typedef CopyDataOfRestrictedAlignmentStructWithCallbackProcForNative = Int32 Function(Pointer classHandle, RestrictedAlignmentStruct sStruct, Pointer<NativeFunction<CopyStructCallbackFuncForFFI>> pCopyCallbackFunc);
typedef CopyDataOfRestrictedAlignmentStructWithCallbackProcForDart = int Function(Pointer classHandle, RestrictedAlignmentStruct sStruct, Pointer<NativeFunction<CopyStructCallbackFuncForFFI>> pCopyCallbackFunc);
 
// Build and initial data structure in the Dart executable.
final sUnrestrictedAlignmentStruct = calloc<UnrestrictedAlignmentStruct>()
      ..ref.charField = 'A'.codeUnitAt(0)     
      ..ref.floatField = 456.123
      ..ref.intField = 123
      ..ref.funcptrField = Pointer.fromFunction<CallbackFuncForFFI>(comparArray, 0)       
      ..ref.stringField.fillFromList(pCmpStr.codeUnits)
      ..ref.dubaryField.fillFromList(notifyDouble);   
// Find the the pointer of function in the C library.
late final copyDataOfUnrestrictedAlignmentStructWithCallbackProc = wrapclib.lookupFunction<
    CopyDataOfUnrestrictedAlignmentStructWithCallbackProcForNative,
    CopyDataOfUnrestrictedAlignmentStructWithCallbackProcForDart
  >('CopyDataOfUnrestrictedAlignmentStructWithCallback');
// Validate each field of the replicated data structure in C library.
isSame =  copyDataOfUnrestrictedAlignmentStructWithCallbackProc(
        classHandle.value, 
        sUnrestrictedAlignmentStruct.ref, 
        Pointer.fromFunction<CopyStructCallbackFuncForFFI>(getUnrestrictedAlignmentStructProc, false)
      ) == 1 ? "True" : "False";     
print("Copy structure data in C library : $isSame");

  3-5.  回傳資料結構的完整性

    C 語言對於資料結構的記憶體配置規則可藉由設定 pack 的方式來進行調整且若配置規則不一致時會影響資料轉換後函式引用,而dart:ffi 對於資料結構也有提供調整記憶體配置的方式以避免與 C 語言函數所建立的資料結構有所差異而導致資料結構的不完整,因此分別定義使用設定預定對齊格式的資料結構進行來驗證是否能正確執行C函式庫中的函式。
// Default alignment according to computer architecture.
typedef struct _UnrestrictedAlignmentStruct {
    int intField;
    TCHAR charField;
    CallbackFunc funcptrField;
    float floatField;
    wchar_t stringField[15];
    double dubaryField[10];
} UnrestrictedAlignmentStruct, * pUnrestrictedAlignmentStruct;
// Default alignment according to pack function.
#pragma pack(push, 1)
typedef struct _RestrictedAlignmentStruct {
    int intField;
    TCHAR charField;
    CallbackFunc funcptrField;
    float floatField;
    wchar_t stringField[15];
    double dubaryField[10];
} RestrictedAlignmentStruct, *pRestrictedAlignmentStruct;
#pragma pack(pop)
// Default alignment according to computer architecture.
class UnrestrictedAlignmentStruct  extends Struct {
  @Int32()
  external int intField;
  @Uint16()
  external int charField;  
  external Pointer<NativeFunction<CallbackFuncForFFI>> funcptrField;
  @Float()
  external double floatField;
  @Array(15)
  external Array<Int16> stringField;
  @Array(10)
  external Array<Double> dubaryField; 
}
// Default alignment according to pack function.
@Packed(1)
class RestrictedAlignmentStruct  extends Struct {
  @Int32()
  external int intField;
  @Int16()
  external int charField;  
  external Pointer<NativeFunction<CallbackFuncForFFI>> funcptrField;
  @Float()
  external double floatField;
  @Array(15)
  external Array<Int16> stringField;
  @Array(10)
  external Array<Double> dubaryField; 
}

4.  自動轉換C語言標頭檔套件

  在pub.dev中有提供將C語言標頭檔的內容自動轉換成Dart語言所支援dart:ffi中定義的NativeType資料格式的工具套件。

  4-1.  設定流程

    1.  環境需求
     使用FFIGEN套件前需要先行安裝Visual Studio With C++ 與LLVM套件並在開發專案的pubspec.yaml文件中設定安裝ffigen套件,才能正常的執行其套件功能。
Install Visual Studio C++
wingetinstall -e --id LLVM.LLVMll
LLVM 安裝路徑
// pubspec.yaml
dev_dependencies:
  ffigen: ^4.0.0
    
    2.  新增FFIGEN套件的組態檔
    新增FFIGEN轉換的組態檔,ffigen提供下列兩種設定的方式:
    2-1. 新增ffigen專屬的yaml檔案並搭配 --config 參數來指定組態檔。
name: AutoConvertByffigen
description: A starting point for Dart libraries or applications.
output: 'lib/crosscode_by_auto.dart'
headers:
  entry-points:   
    - 'third_party/crosscode_wrap_cfunc.h'   
   
  # 只轉換下列的header文件而其內所引用的相關header文件將不會被轉換
  include-directives:  
    - '**crosscode_defstruct.h'
    - '**crosscode_wrap_cfunc.h'
structs:
  include:
    - '.*' 
  exclude:
    - '_ExcludeUnrestrictedAlignmentStruct'  
  # 移除起始"_"符號並加入FFI字串
  rename:
    '_(.*)': 'FFI$1'
# 此選項中只有被引用的typedef才會進行轉換
typedefs:
  # 若要直接引用其原始定義而不要建立其對應的定義
  exclude:
    - 'CallbackFunc2'
    - 'UChar'
  # 將轉換後的定義變數名稱加入ForFFI字串
  rename: 
    '(.*)': '$1'
functions: 
  include:
    - '.*' 
  # 指定排除轉換的函式
  exclude:
    - 'ExcludeFunction' 
# 指定型別對應至NativeType型別  
typedef-map:
  'TCHAR': 'Int16' 
comments:
  style: any
  length: full
preamble:
  /* ************** Cross Code Co.Ltd ********************
  how to auto convert from C to dart by ffigen packages       
  ***************************************************** */
    2-2. 在pubspec.yaml中新增ffigen標籤來設定組態資訊
name: crosscode_wrapc_exe
description: A sample command-line application.
version: 1.0.0
homepage: https://vocus.cc/user/61ca9f1bfd89780001efa2a6
environment:
  sdk: '>=2.15.1 <3.0.0'
dependencies:
  ffi: ^1.1.0
dev_dependencies:
  lints: ^1.0.0
  test: ^1.16.0
  ffigen: ^4.0.0
ffigen:
  name: AutoConvertByffigen
  description: Convert Header file of C to dart file of Dart by ffigen package
  output: 'lib/crosscode_by_auto.dart'
  headers:
    entry-points:   
      - 'third_party/crosscode_wrap_cfunc.h'   
    # 只轉換下列的header文件而其內所引用的相關header文件將不會被轉換
    include-directives:  
      - '**crosscode_defstruct.h'
      - '**crosscode_wrap_cfunc.h'
  structs:
    include:
      - '.*' 
    exclude:
      - '_ExcludeUnrestrictedAlignmentStruct' 
    # 移除起始"_"符號並加入FFI字串
    rename:
      '_(.*)': 'FFI$1'
  # 此選項中只有被引用的typedef才會進行轉換
  typedefs:
    # 若要直接引用其原始定義而不要建立其對應的定義
    exclude:
      - 'CallbackFunc2'
      - 'UChar'
    # 將轉換後的定義變數名稱加入ForFFI字串
    rename: 
      '(.*)': '$1'
  functions: 
    include:
      - '.*' 
    # 指定排除轉換的函式
    exclude:
      - 'ExcludeFunction' 
  # 指定C型別對應至NativeType型別
  typedef-map:
    'TCHAR': 'Int16' 
  comments:
    style: any
    length: full
  preamble:
  /* ************** Cross Code Co.Ltd ********************
    how to auto convert from C to dart by ffigen packages       
  ***************************************************** */
GitHub : https://github.com/crosscode-software/dart_wrap_c
 

留言0
查看全部
發表第一個留言支持創作者!
本範例主要說明如何運用Flutter 繪圖與動態相關的API並搭配provider套件進行實作輪盤賭選擇 ( Roulette Wheel Selection ) 程式。
說明Flutter 模組(Module)專案範例的架構與如何載入Android專案中的流程與執行畫面
說明Flutter 插件(Plugin)專案範例的架構與實際載入並執行在各平台的顯示畫面
說明Flutter 包(Package)專案範例的架構與實際載入並執行在各平台的顯示畫面
說明Flutter 骨架(skeleton)專案範例的架構與在各平台執行的顯示畫面
說明Flutter 應用軟體(Application)專案範例的架構與在各平台執行的顯示畫面
本範例主要說明如何運用Flutter 繪圖與動態相關的API並搭配provider套件進行實作輪盤賭選擇 ( Roulette Wheel Selection ) 程式。
說明Flutter 模組(Module)專案範例的架構與如何載入Android專案中的流程與執行畫面
說明Flutter 插件(Plugin)專案範例的架構與實際載入並執行在各平台的顯示畫面
說明Flutter 包(Package)專案範例的架構與實際載入並執行在各平台的顯示畫面
說明Flutter 骨架(skeleton)專案範例的架構與在各平台執行的顯示畫面
說明Flutter 應用軟體(Application)專案範例的架構與在各平台執行的顯示畫面
你可能也想看
Google News 追蹤
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
Rees Dart Track健行之旅來到最後一天,體力也差不多消耗殆盡,今天需要健行16公里,且配合接駁車時間,需要在下午一點前就抵達登山口,趕路壓力可謂不小。然而這段路程危機四伏,除了一開始通過深及大腿的溪流,沿途因自然災害,須行經崩塌路段或是高繞,好在我們平安於中午之前順利完成。
Thumbnail
Rees Dart Track的健行之旅已經來到第三天,今天共計有18公里的路程要趕。經過兩天的測試,我們發現我們的腳程僅能勉強達到保育部的建議健行時間,因此在天未亮前就啟程,穿越櫸木森林來到河谷地,沿著平坦的草原一路往山屋前行。即便有趕路的壓力,但在風景如詩如畫的自然中,仍會忍不住停下腳步拍照。
Thumbnail
Rees Dart Track第二天行程須健行10公里到Dart山屋,路線不長,但本日天氣不好,下著綿綿細雨,需爬升上鞍部在沿著山腰路徑走在河谷之中,強風環伺,需要做好保暖工作。雖然無緣在鞍部一覽無際的河谷,但是雨天煙雨濛濛,瀑布水勢變大,也有其獨特之美。
Thumbnail
終於踏上Rees Dart Track,開始在紐西蘭第二條長程步道健行之旅。第一天的行程長達近20公里,我們在出發前閱讀行前紀錄,已經了解劇烈天氣變化、路線陡峭難行等風險,但萬萬沒想到一開始的草原沼澤就來個下馬威。我們也非省油的燈,沿著指標安然通過多處涉溪、雪崩潛在區域,順利於日落之前抵達山屋。
Thumbnail
Rees-Dart Track全程不包含支線共計63公里,被歸類為挑戰型路線,雖官網上說沿途指標清楚毋需攜帶地圖,但因多處行經沼澤、溪流,健行者仍須具備一定的山野能力,並下載離線地圖為佳。步道沿著Rees和Dart兩條河流,健行在河谷、草原、森林之中,可見壯麗高山、冰河、峽谷景象。
Thumbnail
估值思維 與 如何使用估值思維來解讀新聞
如何使用筋膜槍?  雖然你的物理治療師或脊椎按摩師可能會使用敲擊式筋膜槍作為治療的一部分,但也可以自己使用該設備。下面,奧倫多夫分享了走DIY路線時的四個技巧。 筋膜槍用法慢慢開始。筋膜槍可能真的很強大,所以要小心,特別是如果你已經很痛了。"我讓我的所有運動員從最低的設置開始,然後從那里增加。 筋膜
Thumbnail
不同類別股票應該選擇適合的評估方法,衡量股價與合理價關係以進行投資決策。本篇教您如何使用「現金股利殖利率法」進行價值評估,以三檔股票(中保科、一零四、華研)為例, 計算合理價,並解讀2020 Q2財報指標,檢視重要轉投資公司績效,擬定現階段投資策略,晚上睡得著覺,安心領股息。
Thumbnail
記錄內在的指引,臣服於內在指引師 什麼是直覺療癒自然法? 首先你必須先向靈性直覺臣服,接著我們來看看作者提出自我療癒的五個自然方法。  假設你有病在身,可以參考以下這些接近直覺的步驟。 請善用這些步驟,並配合醫生提供的各種治療資訊。 若你對如何處理某個健康問題沒有把
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
Rees Dart Track健行之旅來到最後一天,體力也差不多消耗殆盡,今天需要健行16公里,且配合接駁車時間,需要在下午一點前就抵達登山口,趕路壓力可謂不小。然而這段路程危機四伏,除了一開始通過深及大腿的溪流,沿途因自然災害,須行經崩塌路段或是高繞,好在我們平安於中午之前順利完成。
Thumbnail
Rees Dart Track的健行之旅已經來到第三天,今天共計有18公里的路程要趕。經過兩天的測試,我們發現我們的腳程僅能勉強達到保育部的建議健行時間,因此在天未亮前就啟程,穿越櫸木森林來到河谷地,沿著平坦的草原一路往山屋前行。即便有趕路的壓力,但在風景如詩如畫的自然中,仍會忍不住停下腳步拍照。
Thumbnail
Rees Dart Track第二天行程須健行10公里到Dart山屋,路線不長,但本日天氣不好,下著綿綿細雨,需爬升上鞍部在沿著山腰路徑走在河谷之中,強風環伺,需要做好保暖工作。雖然無緣在鞍部一覽無際的河谷,但是雨天煙雨濛濛,瀑布水勢變大,也有其獨特之美。
Thumbnail
終於踏上Rees Dart Track,開始在紐西蘭第二條長程步道健行之旅。第一天的行程長達近20公里,我們在出發前閱讀行前紀錄,已經了解劇烈天氣變化、路線陡峭難行等風險,但萬萬沒想到一開始的草原沼澤就來個下馬威。我們也非省油的燈,沿著指標安然通過多處涉溪、雪崩潛在區域,順利於日落之前抵達山屋。
Thumbnail
Rees-Dart Track全程不包含支線共計63公里,被歸類為挑戰型路線,雖官網上說沿途指標清楚毋需攜帶地圖,但因多處行經沼澤、溪流,健行者仍須具備一定的山野能力,並下載離線地圖為佳。步道沿著Rees和Dart兩條河流,健行在河谷、草原、森林之中,可見壯麗高山、冰河、峽谷景象。
Thumbnail
估值思維 與 如何使用估值思維來解讀新聞
如何使用筋膜槍?  雖然你的物理治療師或脊椎按摩師可能會使用敲擊式筋膜槍作為治療的一部分,但也可以自己使用該設備。下面,奧倫多夫分享了走DIY路線時的四個技巧。 筋膜槍用法慢慢開始。筋膜槍可能真的很強大,所以要小心,特別是如果你已經很痛了。"我讓我的所有運動員從最低的設置開始,然後從那里增加。 筋膜
Thumbnail
不同類別股票應該選擇適合的評估方法,衡量股價與合理價關係以進行投資決策。本篇教您如何使用「現金股利殖利率法」進行價值評估,以三檔股票(中保科、一零四、華研)為例, 計算合理價,並解讀2020 Q2財報指標,檢視重要轉投資公司績效,擬定現階段投資策略,晚上睡得著覺,安心領股息。
Thumbnail
記錄內在的指引,臣服於內在指引師 什麼是直覺療癒自然法? 首先你必須先向靈性直覺臣服,接著我們來看看作者提出自我療癒的五個自然方法。  假設你有病在身,可以參考以下這些接近直覺的步驟。 請善用這些步驟,並配合醫生提供的各種治療資訊。 若你對如何處理某個健康問題沒有把