由於不同程式語言可能採用的預設字元編碼(utf16, utf6, unicode... etc.)不同而可能造成函式在傳送與接送字串因編碼錯誤而造成判斷有誤甚至是造成執行崩潰(crash)或執行錯誤的情況,因此在C函式庫中定義函式用於判斷傳遞的資料結構的字串與函式庫中字串是否相同。
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
// 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