? 目錄 ?
- 🛫 導讀
- 需求
- 開發環境
- 1?? FFI
- 概念
- 優點
- 注意事項
- 2?? 【廢棄】node-ffi
- 3?? node-ffi-napi
- 安裝(windows系統下)
- 示例:MessageBoxA、NtSuspendProcess
- 4?? node-win32-api
- 安裝
- 示例:查找窗口并設置窗口標題
- 5?? hmc-win32
- 安裝
- 示例
- 🛬 文章小結
- 📖 參考資料
🛫 導讀
需求
C++開發中經常遇到使用腳本語言,如lua、python等,提供更為靈活的更新操作。而腳本調用dll,又得導出接口。為了不更新二進制代碼,往往通過ffi的形式,為腳本語言增加調用dll的功能。
electron同樣支持js調用原生模塊的功能(參考 Node 原生模塊 https://www.electronjs.org/zh/docs/latest/tutorial/using-native-node-modules)。然而為了更加豐富electron的功能,我們是否可以提供類似lua等腳本語言調用原生dll的接口能力呢?
這就是今天我們要講的FFI
。
開發環境
版本號 | 描述 | |
---|---|---|
文章日期 | 2023-12-10 | |
操作系統 | Win11 - 21H2 - 22000.1335 | |
1?? FFI
概念
Foreign Function Interface(外語函數接口,FFI)是指一種在不同編程語言之間調用函數的方式。FFI 可以使語言 A 的程序能調用由語言 B 編寫的函數,從而實現兩種語言之間的交互。FFI 可以使用用戶態庫或者內核態庫實現,并且可以在不同的操作系統上使用。FFI 的一般實現方式是通過在目標語言(如 C)中編寫一個封裝函數,然后在調用方語言中使用該封裝函數來調用目標函數。
優點
使用 FFI 有以下幾個優勢:
- 跨語言調用:FFI 使得在不同編程語言之間進行函數調用成為可能。
- 使用外部庫:FFI 使得可以調用外部庫中的函數,而不需要將其代碼集成到程序中。
- 提高效率:FFI 使得可以調用由其他編程語言編寫的高效函數,提高程序的效率。
- 利用已有的庫資源:FFI 使得可以利用已有的由其他語言編寫的庫資源,避免重復開發。
總之,FFI 是一種很靈活的技術,可以幫助程序員在不同編程語言之間進行函數調用,提高程序的效率和擴展性。
注意事項
盡管FFI有大量優勢,我們依然需要注意:
- 如調用方和被調用方的類型
- 參數和返回值的轉換
- 調用方與被調用方的內存管理等問題。
2?? 【廢棄】node-ffi
網上搜索node ffi,會出現大量的關于庫
node-ffi
的文章,其git地址為https://github.com/node-ffi/node-ffi。
從git上可以看到,該庫已經從19年不再更新了,而node和electron的版本日新月異,經過測試,該庫在新版本的node中已經無法正常運行,可以完全放棄了。
3?? node-ffi-napi
通過github,小編發現了替代庫node-ffi-napi: https://github.com/node-ffi-napi/node-ffi-napi,該庫在node16及node18下測試均正常。
安裝(windows系統下)
步驟一:確保您已安裝所有
必要的構建
適合您平臺的工具node-gyp
步驟二:然后調用下面語句
npm install ffi-napi
注意這里,會發現不是node-ffi-napi
,而是ffi-napi
。
示例:MessageBoxA、NtSuspendProcess
node-ffi-napi提供了對象
Library
,其構造函數生成dll對應的接口集合。
參數一為dll名稱
參數二為對象,表示所有要用到的api接口。
下面分別通過MessageBoxA演示UI能力,以及NtSuspendProcess掛起某進程。
import ffi from 'ffi-napi'function 測試ffi_napi() {var current = ffi.Library('user32.dll', {'MessageBoxA': [ 'int', [ 'int', 'int' , 'int' , 'int' ] ]});current.MessageBoxA(0,0,0,0); // 1234// NTSTATUS NTAPI NtSuspendProcess(HANDLE ProcessHandle)var ntdll = ffi.Library('ntdll.dll', {'NtSuspendProcess': [ 'int', [ 'int' ] ]});ntdll.NtSuspendProcess(-1);
}測試ffi_napi()
4?? node-win32-api
node-win32-api是基于node-ffi-napi的一個
純js庫
,封裝了部分接口和數據結構,方便用戶調用。
github地址:https://github.com/waitingsong/node-win32-api
安裝
npm install win32-api
示例:查找窗口并設置窗口標題
// **Find calc's hWnd, need running a calculator program manually at first**/*** Exposed modules:* Comctl32: Comctl32 from lib/comctl32/api* Kernel32: kernel32 from lib/kernel32/api* User32: user32 from lib/user32/api*/
import { Kernel32, User32 } from 'win32-api/promise'
import ref from 'ref-napi'const knl32 = Kernel32.load()
const user32 = User32.load()// const user32 = load(['FindWindowExW']) // load only one api defined in lib/{dll}/api from user32.dllconst title = 'Calculator\0' // null-terminated string
// const title = '計算器\0' // null-terminated string 字符串必須以\0即null結尾!const lpszWindow = Buffer.from(title, 'ucs2')
const hWnd = await user32.FindWindowExW(0, 0, null, lpszWindow)assert((typeof hWnd === 'string' && hWnd.length > 0) || hWnd > 0)
console.log('buf: ', hWnd)// Change title of the Calculator
const res = await user32.SetWindowTextW(hWnd, Buffer.from('Node-Calculator\0', 'ucs2'))
if ( ! res) {console.log('SetWindowTextW failed')
}
else {console.log('window title changed')
}
5?? hmc-win32
hmc-win32 是中國香港的小哥寫的庫,實現了各種常用的函數的封裝,貌似對標自動化庫autoitX。
該庫通過C++實現的,功能十分豐富,作者自己說自測完善,可放心使用。
github地址:https://github.com/kihlh/hmc-win32
安裝
npm i hmc-win32
示例
枚舉進程列表:
import hmc from 'hmc-win32';function 測試hmc() {// console.log(hmc)let procList = hmc.Process.getDetailsList()console.log(procList)
}測試hmc()
🛬 文章小結
上述是對electron使用ffi擴展的各種嘗試,如若不滿足需求。
可通過Node 原生模塊
開發自己需要的功能。其實ffi也是原生模塊的一個應用而已。
📖 參考資料
- Node 原生模塊 https://www.electronjs.org/zh/docs/latest/tutorial/using-native-node-modules
- 調用 c++原生 dll https://zh-sky.gitee.io/electron-vue-template-doc/Overview/advanced/ffi.html
- node-win32-api https://github.com/waitingsong/node-win32-api
- hmc-win32 https://github.com/kihlh/hmc-win32
ps: 文章中內容僅用于技術交流,請勿用于違規違法行為。