最近公司決定進軍 Mac 市場,因此開始搞搞 Mac 下的開發。爲了實現 GUI 的共用,決定使用 Electron 作界面。但咱們是顯卡外接盒的應用,因此就須要把硬件控制的部分用 C++ 封裝成類庫給 Electron 調用了。這篇文章,將重點講一下這方面的應用和開發過程遇到過的問題。javascript
使用 node-ffi 可讓 Node.js 調用 C++ 的 Library 。在 Windows 下是 dll
,在 Mac OS 下是 dylib
,Linux 則是 so
。node-ffi 加載 Library 是有限制的,只能處理 C 風格的 Library 。也就是函數要被放在 extern "C"
裏。html
安裝 node-ffi 對於不一樣操做系統,會有不一樣的環境要求。具體能夠參看:https://github.com/nodejs/node-gyp#installationjava
for electron 編譯
而對於 electron ,須要對 node-ffi 從新編譯。咱們安裝 electron-rebuild 和 electron-prebuilt 進行編譯。node
shell npm i --save-dev electron-rebuild npm i -g electron-prebuilt
設定環境變量:react
bash
# Electron 的版本 export npm_config_target=1.8.4 # 要構建的 electron 類型. export npm_config_arch=x64 export npm_config_target_arch=x64 # Electron 下載地址頭,能夠用鏡像. export npm_config_disturl=https://atom.io/download/electron # 告訴 node-pre-gyp 是爲 Electron 編譯. export npm_config_runtime=electron # 告訴 node-pre-gyp 從源碼編譯. export npm_config_build_from_source=true # 下載的緩存路徑. HOME=~/.electron-gyp npm install
而後 rebuild:git
shell
./node_modules/.bin/electron-rebuild -e /usr/local/lib/node_modules/electron-prebuilt
-e
是本地 electron-prebuilt 的絕對路徑。github
載入 Library
假設有 Library 函數以下:shell
cpp int add(int n1, int n2); int div(int d1, int d2, int* r);
那麼 node-ffi 應該這樣調用:npm
javascript
var ref = require("ref");
var ffi = require("ffi");
var intPtr = ref.refType(ref.types.int); // 建立一個 int 指針類型
var lib = ffi.Library('mylib', {
"add": [ 'int', [ 'int', 'int' ] ],
"div": [ 'int', [ 'int', 'int', intPtr ] ]
});
let sum = lib.add(1, 2);
console.log(`1 + 2 = ${sum}`); let remainder = ref.alloc(ref.types.int, 0); let quotient = lib.div(10, 3, remainder); console.log(`10 ÷ 3 = ${quotient} ...... ${remainder.deref()}`);
關於 ffi
的其餘類型,能夠參考:https://github.com/ffi/ffi/wiki/Types數組
數組參數的調用
對於參數有包含數組的函數,如:
cpp int analysis(int number, int factor[]);
咱們則須要使用到 ref-array
來建立一個數組類型加載函數:
javascript
var ffi = require("ffi");
var ArrayType = require('ref-array');
var IntArray = ArrayType(ref.types.int);
var lib = ffi.Library('mylib', {
"analysis": [ 'int', [ 'int', IntArray ] ]
});
var factors = new IntArray(new Array(100));
lib.analysis(32, factors);
console.log(`The factors of 32 are ${factors.join(',')}`);
回調函數的使用
有些 C++ Library 會包含有回調函數做爲參數的調用。好比:
cpp typedef void (*ioCallback) (int uVID, int uPID); int device_listen(ioCallback MatchingCallback, ioCallback RemovalCallback);
node-ffi 對此有專門的用於生成回調函數參數的方法 Callback
,示例:
javascript
var ffi = require("ffi");
var lib = ffi.Library('mylib', {
'device_listen': ['int', ['pointer', 'pointer']],
});
let matchCallback = ffi.Callback('void', ['int', 'int'], (vid, pid) => {
console.log('match device, VID: ', vid, ', PID: ', pid) }); let removeCallback = ffi.Callback('void', ['int', 'int'], (vid, pid) => { console.log('remove device, VID: ', vid, ', PID: ', pid) }); lib.device_listen(matchCallback, removeCallback);
這個地方有個坑,若是你回調函數是用於持續監聽,在程序運行過程當中隨時可能被調用的話(好比監聽設備插入拔出),可能會在程序啓動一段時間後,執行回調時引發程序崩潰退出。
這是由於一段時間後,回調函數被垃圾回收了。這裏能夠在程序最後添加:
javascript
process.on('exit', function() {
matchCallback; removeCallback; });
這樣在程序退出前都會保持引用,就不會被垃圾回收了。