node下調用動態庫.DLL


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

最近公司決定進軍 Mac 市場,所以開始搞搞 Mac 下的開發。為了實現 GUI 的共用,決定使用 Electron 做界面。但我們是顯卡外接盒的應用,所以就需要把硬件控制的部分用 C++ 封裝成類庫給 Electron 調用了。這篇文章,將重點講一下這方面的應用和開發過程遇到過的問題。

使用 node-ffi 可以讓 Node.js 調用 C++ 的 Library 。在 Windows 下是 dll ,在 Mac OS 下是 dylib ,Linux 則是 so 。node-ffi 加載 Library 是有限制的,只能處理 C 風格的 Library 。也就是函數要被放在 extern "C" 里。

安裝 node-ffi 對於不同操作系統,會有不同的環境要求。具體可以參看:https://github.com/nodejs/node-gyp#installation

for electron 編譯

而對於 electron ,需要對 node-ffi 重新編譯。我們安裝 electron-rebuild 和 electron-prebuilt 進行編譯。

shell
npm i --save-dev electron-rebuild
npm i -g electron-prebuilt

設定環境變量:

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:

shell
./node_modules/.bin/electron-rebuild -e /usr/local/lib/node_modules/electron-prebuilt 

-e 是本地 electron-prebuilt 的絕對路徑。

載入 Library

假設有 Library 函數如下:

cpp
int add(int n1, int n2);
int div(int d1, int d2, int* r);

那么 node-ffi 應該這樣調用:

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM