Dart 调用C语言混合编程
Dart 調(diào)用C語言
本篇博客研究Dart語言如何調(diào)用C語言代碼混合編程,最后我們實現(xiàn)一個簡單示例,在C語言中編寫簡單加解密函數(shù),使用dart調(diào)用并傳入字符串,返回加密結(jié)果,調(diào)用解密函數(shù),恢復(fù)字符串內(nèi)容。
環(huán)境準備
編譯器環(huán)境
如未安裝過VS編譯器,則推薦使用GCC編譯器,下載一個64位Windows版本的GCC——MinGW-W64
下載地址
如上,它有兩個版本,sjlj和seh后綴表示異常處理模式,seh 性能較好,但不支持 32位。 sjlj 穩(wěn)定性好,可支持 32位,推薦下載seh 版本
將編譯器安裝到指定的目錄,完成安裝后,還需要配置一下環(huán)境變量,將安裝目錄下的bin目錄加入到系統(tǒng)Path環(huán)境變量中,bin目錄下包含gcc.exe、make.exe等工具鏈。
測試環(huán)境
配置完成后,檢測一下環(huán)境是否搭建成功,打開cmd命令行,輸入gcc -v能查看版本號則成功。
Dart SDK環(huán)境
去往Dart 官網(wǎng)下載最新的2.3 版本SDK,注意,舊版本不支持ffi 下載地址
下載安裝后,同樣需要配置環(huán)境變量,將dart-sdk\bin配置到系統(tǒng)Path環(huán)境變量中。
測試Dart ffi接口
簡單示例
創(chuàng)建測試工程,打開cmd命令行
mkdir ffi-proj
cd ffi-proj
mkdir bin src
1
2
3
創(chuàng)建工程目錄ffi-proj,在其下創(chuàng)建bin、src文件夾,在bin中創(chuàng)建main.dart文件,在src中創(chuàng)建test.c文件
編寫test.c
我們在其中包含了windows頭文件,用于showBox函數(shù),調(diào)用Win32 API,創(chuàng)建一個對話框
#include<windows.h>
int add(int a, int b){
 return a + b;
}
void showBox(){
 MessageBox(NULL,"Hello Dart","Title",MB_OK);
}
1
2
3
4
5
6
7
8
9
10
進入src目錄下,使用gcc編譯器,將C語言代碼編譯為dll動態(tài)庫
gcc test.c -shared -o test.dll
1
編寫main.dart
import 'dart:ffi' as ffi;
import 'dart:io' show Platform;
/// 根據(jù)C中的函數(shù)來定義方法簽名(所謂方法簽名,就是對一個方法或函數(shù)的描述,包括返回值類型,形參類型)
/// 這里需要定義兩個方法簽名,一個是C語言中的,一個是轉(zhuǎn)換為Dart之后的
typedef NativeAddSign = ffi.Int32 Function(ffi.Int32,ffi.Int32);
typedef DartAddSign = int Function(int, int);
/// showBox函數(shù)方法簽名
typedef NativeShowSign = ffi.Void Function();
typedef DartShowSign = void Function();
void main(List<String> args) {
 if (Platform.isWindows) {
 // 加載dll動態(tài)庫
 ffi.DynamicLibrary dl = ffi.DynamicLibrary.open("../src/test.dll");
 // lookupFunction有兩個作用,1、去動態(tài)庫中查找指定的函數(shù);2、將Native類型的C函數(shù)轉(zhuǎn)化為Dart的Function類型
 var add = dl.lookupFunction<NativeAddSign, DartAddSign>("add");
 var showBox = dl.lookupFunction<NativeShowSign, DartShowSign>("showBox");
 // 調(diào)用add函數(shù)
 print(add(8, 9));
 // 調(diào)用showBox函數(shù)
 showBox();
 }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
深入用法
這里寫一個稍微深入一點的示例,我們在C語言中寫一個簡單加密算法,然后使用dart調(diào)用C函數(shù)加密解密
編寫encrypt_test.c,這里寫一個最簡單的異或加密算法,可以看到加密和解密實際上是一樣的
#include <string.h>
#define KEY 'abc'
void encrypt(char *str, char *r, int r_len){
 int len = strlen(str);
 for(int i = 0; i < len && i < r_len; i++){
 r[i] = str[i] ^ KEY;
 }
 if (r_len > len) r[len] = '\0';
 else r[r_len] = '\0';
}
void decrypt(char *str, char *r, int r_len){
 int len = strlen(str);
 for(int i = 0; i < len && i < r_len; i++){
 r[i] = str[i] ^ KEY;
 }
 if (r_len > len) r[len] = '\0';
 else r[r_len] = '\0';
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
編譯為動態(tài)庫
gcc encrypt_test.c -shared -o encrypt_test.dll
1
編寫main.dart
import 'dart:ffi';
import 'dart:io' show Platform;
import "dart:convert";
/// encrypt函數(shù)方法簽名,注意,這里encrypt和decrypt的方法簽名實際上是一樣的,兩個函數(shù)返回值類型和參數(shù)類型完全相同
typedef NativeEncrypt = Void Function(CString,CString,Int32);
typedef DartEncrypt = void Function(CString,CString,int);
void main(List<String> args) {
 if (Platform.isWindows) {
 // 加載dll動態(tài)庫
 DynamicLibrary dl = DynamicLibrary.open("../src/encrypt_test.dll");
 var encrypt = dl.lookupFunction<NativeEncrypt, DartEncrypt>("encrypt");
 var decrypt = dl.lookupFunction<NativeEncrypt, DartEncrypt>("decrypt");
 CString data = CString.allocate("helloworld");
 CString enResult = CString.malloc(100);
 encrypt(data,enResult,100);
 print(CString.fromUtf8(enResult));
print("-------------------------");
 CString deResult = CString.malloc(100);
 decrypt(enResult,deResult,100);
 print(CString.fromUtf8(deResult));
 }
}
/// 創(chuàng)建一個類繼承Pointer<Int8>指針,用于處理C語言字符串和Dart字符串的映射
class CString extends Pointer<Int8> {
 /// 申請內(nèi)存空間,將Dart字符串轉(zhuǎn)為C語言字符串
 factory CString.allocate(String dartStr) {
 List<int> units = Utf8Encoder().convert(dartStr);
 Pointer<Int8> str = allocate(count: units.length + 1);
 for (int i = 0; i < units.length; ++i) {
 str.elementAt(i).store(units[i]);
 }
 str.elementAt(units.length).store(0);
 return str.cast();
 }
 // 申請指定大小的堆內(nèi)存空間
 factory CString.malloc(int size) {
 Pointer<Int8> str = allocate(count: size);
 return str.cast();
 }
 /// 將C語言中的字符串轉(zhuǎn)為Dart中的字符串
 static String fromUtf8(CString str) {
 if (str == null) return null;
 int len = 0;
 while (str.elementAt(++len).load<int>() != 0);
 List<int> units = List(len);
 for (int i = 0; i < len; ++i) units[i] = str.elementAt(i).load();
 return Utf8Decoder().convert(units);
 }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
運行結(jié)果
可以看到將"helloworld"字符串加密后變成一串亂碼,解密字符串后,恢復(fù)內(nèi)容
完善代碼
上述代碼雖然實現(xiàn)了我們的目標,但是存在明顯的內(nèi)存泄露,我們使用CString 的allocate和malloc申請了堆內(nèi)存,但是卻沒有手動釋放,這樣運行一段時間后可能會耗盡內(nèi)存空間,手動管理內(nèi)存往往是C/C++中最容易出問題的地方,這里我們只能進行一個簡單的設(shè)計來回收內(nèi)存
/// 創(chuàng)建Reference 類來跟蹤CString申請的內(nèi)存
class Reference {
 final List<Pointer<Void>> _allocations = [];
 T ref<T extends Pointer>(T ptr) {
 _allocations.add(ptr.cast());
 return ptr;
 }
 // 使用完后手動釋放內(nèi)存
 void finalize() {
 for (final ptr in _allocations) {
 ptr.free();
 }
 _allocations.clear();
 }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
修改代碼
import 'dart:ffi';
import 'dart:io' show Platform;
import "dart:convert";
/// encrypt函數(shù)方法簽名,注意,這里encrypt和decrypt的方法簽名實際上是一樣的,兩個函數(shù)返回值類型和參數(shù)類型完全相同
typedef NativeEncrypt = Void Function(CString,CString,Int32);
typedef DartEncrypt = void Function(CString,CString,int);
void main(List<String> args) {
 if (Platform.isWindows) {
 // 加載dll動態(tài)庫
 DynamicLibrary dl = DynamicLibrary.open("../src/hello.dll");
 var encrypt = dl.lookupFunction<NativeEncrypt, DartEncrypt>("encrypt");
 var decrypt = dl.lookupFunction<NativeEncrypt, DartEncrypt>("decrypt");
 // 創(chuàng)建Reference 跟蹤CString
 Reference ref = Reference();
 CString data = CString.allocate("helloworld",ref);
 CString enResult = CString.malloc(100,ref);
 encrypt(data,enResult,100);
 print(CString.fromUtf8(enResult));
print("-------------------------");
 CString deResult = CString.malloc(100,ref);
 decrypt(enResult,deResult,100);
 print(CString.fromUtf8(deResult));
 // 用完后手動釋放
 ref.finalize();
 }
}
class CString extends Pointer<Int8> {
 /// 開辟內(nèi)存控件,將Dart字符串轉(zhuǎn)為C語言字符串
 factory CString.allocate(String dartStr, [Reference ref]) {
 List<int> units = Utf8Encoder().convert(dartStr);
 Pointer<Int8> str = allocate(count: units.length + 1);
 for (int i = 0; i < units.length; ++i) {
 str.elementAt(i).store(units[i]);
 }
 str.elementAt(units.length).store(0);
 ref?.ref(str);
 return str.cast();
 }
 factory CString.malloc(int size, [Reference ref]) {
 Pointer<Int8> str = allocate(count: size);
 ref?.ref(str);
 return str.cast();
 }
 /// 將C語言中的字符串轉(zhuǎn)為Dart中的字符串
 static String fromUtf8(CString str) {
 if (str == null) return null;
 int len = 0;
 while (str.elementAt(++len).load<int>() != 0);
 List<int> units = List(len);
 for (int i = 0; i < len; ++i) units[i] = str.elementAt(i).load();
 return Utf8Decoder().convert(units);
 }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
總結(jié)
dart:ffi包目前正處理開發(fā)中,暫時釋放的只有基礎(chǔ)功能,且使用dart:ffi包后,Dart代碼不能進行aot編譯,不過Dart開發(fā)了ffi接口后,極大的擴展了dart語言的能力邊界,就如同的Java的Jni一樣,如果ffi接口開發(fā)得足夠好用,Dart就能像Python那樣成為一門真正的膠水語言。
大家如果有興趣進一步研究,可以查看dart:ffi包源碼,目前該包總共才5個dart文件,源碼很少,適合學(xué)習(xí)。
---------------------?
轉(zhuǎn)載于:https://www.cnblogs.com/ly570/p/10942293.html
總結(jié)
以上是生活随笔為你收集整理的Dart 调用C语言混合编程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: LiveGBS国标流媒体-摄像机网页低延
- 下一篇: Hadoop前期准备--centos7
