node下調用動態庫.DLL

使用 node-ffi 構建 Electron 和 C++ Library 混合桌面應用

最近公司決定進軍 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; }); 

這樣在程序退出前都會保持引用,就不會被垃圾回收了。

原文地址:https://io.hancel.org/2018/05/02/building-an-electron-hybrid-application.html

相關文章
相關標籤/搜索