前言 electron以nodejs作為底層運行環(huán)境,所以自然而然就想到了他能否調(diào)用C++編寫的動態(tài)庫,恰好我最近在做一個關于使用electron調(diào)用dll的項目,也就花了一點時間去了解和實踐,這期間走了不少彎路,這里分享出來,希望能幫助到有遇到相關問題的小伙伴
很多剛入門不久的小伙伴第一個問題可能就是electron能不能調(diào)用DLL動態(tài)庫?這里給一個明確的答復是可以的。為什么?因為electron本身就集成了nodejs運行環(huán)境,而nodejs又是用C++實現(xiàn)的,相當于C++是他的原配。 既然能調(diào)用,那么第二個問題來了,怎么調(diào)用?nodejs在官網(wǎng)也給出了方案addon有興趣的可以看看,但是對于我們前端來說這個方案太過于復雜了,學習成本太高,如果沒有c++開發(fā)背景還是另辟蹊徑吧,electron是可以做到和C++混合開發(fā)的,如果團隊中有C++的開發(fā)人員也可以嘗試,結合C++本身的優(yōu)勢,開發(fā)出來的產(chǎn)品效果可能會更好,我們常用的IDE工具VSCode其實有部分功能也是使用C++來時實現(xiàn)的,這其中規(guī)避了一些electron的缺點。另一種調(diào)用DLL動態(tài)庫的方案就是這里要重點介紹的模塊node-ffi。 其實這個模塊時用起來非常的簡單,先貼一段官方使用案例,簡單加了幾句注釋:
var ffi = require('ffi');//引入ffi模塊
/*
*使用ffi模塊將dll和js打通,可以把它看做是RPC(遠程調(diào)用協(xié)議)
*@libm 動態(tài)庫的絕對地址例如"C://plugin/test.dll"
*@ceil 動態(tài)庫中方法的名稱 double返回值的數(shù)據(jù)類型 ['double'] 這是函數(shù)輸入?yún)?shù)的數(shù)據(jù)類型
*這里提一下,應為C++是是屬于強類型語言這個js不同,所以這里一定要指定返回參數(shù)和輸入?yún)?shù)的類型
*/
var libm = ffi.Library('libm', {
'ceil': [ 'double', [ 'double' ] ]
});
//通過上面的注冊的libm對象來調(diào)用dll中的ceil方法
libm.ceil(1.5); // 2
這一切看起來就是這么簡單,但是在這幾句簡單的代碼后面,會讓剛入門的小伙伴非常的蛋疼,因為在使用的過程中你會遇到各種問題,而且這些問題會讓你對本身的目的產(chǎn)生動搖,目前公開的環(huán)境能夠支持nodejs調(diào)用的dll的npm包好像就只有ffi,而在使用中出現(xiàn)的問題,貌似你嘗試了所有解決問題的方案,最終還是解決不了。我自己在搞這個的時候也弄了大概一周時間,各種環(huán)境搗騰,各種測試。最后弄出來了,這里總結一下這個過程中遇見的問題,打開分為這么3點:
- 安裝時報錯
- 安裝成功但是在運行electron時調(diào)用ffi時報錯
- 打包electron時報錯
安裝過程中報錯
//全局安裝原生模塊編譯模塊,編譯ffi模塊時需要用到的,這里一定要安裝
npm install node-gyp -g
//安裝ffi模塊
npm install ffi --save
 這時安裝ffi會提示Python沒有安裝,需要安裝python,我安裝的是Python2.7.13。完成之后當我興高采烈的再去安裝ffi模塊,結果失望的發(fā)現(xiàn)下一個錯誤來了: 錯誤非常明顯,就是C++的構建環(huán)境沒有,后面也告訴了我們要怎么來解決:
1). 就是安裝.NET Framework 2.0 。一般我們使用的windows系統(tǒng)本本身會自動帶上.NET Framework的,只是版本可能會有所區(qū)別而已,我們可以去看看C:\Windows\Microsoft.NET\Framework目錄下看看到底有沒安裝,安裝了什么版本 ,一般來說這里不需要重新再去安裝的,所以這里的第一條建議就可以忽略了。 2). 第二條建議就是安裝Microsoft Visual Stu,熟悉C++開發(fā)的人可能對它很熟悉,對前端來所就是比較陌生了,前端可以把他理解C++的編譯環(huán)境,這里面需要下載很多C++的組件庫,在node-gyp編譯的時候會使用到,就好像nodejs一樣,如果沒有裝nodejs環(huán)境,在使用npm進行安裝的時候,及會提示命令找不到等等。咋一看,路子好像挺對的,因為node-gyp會重新編譯node-ffi模塊,所以使用到C++的環(huán)境很正常,也是因為這樣我就吭呲吭吃去安裝了,因為Visual Stu很大,有好幾個G,像我這種家里只裝了10M帶寬人來說,你懂的。晚上睡覺的時候開始下載,第二天一早起來開始安裝,安裝好了,又開始安裝ffi模塊,心里那種期待的感覺,只有程序員能理解。結果沒什么意外還是失敗。 經(jīng)過各種嘗試發(fā)現(xiàn),其實我門除了安裝VS外其實還可以通過npm 安裝c++在windows環(huán)境下的構建工具windows-build-tools,好了不多說了直接開始干
npm install --global --production windows-build-tools
這里可能需要等待的時間比較久一點,有點耐心,好東西都是值得等待的。 安裝完成后再來安裝ffi模塊,結果還是讓人失望,不信你看,是不是和你遇見的一樣: 通過日志可以看出是在node-gyp rebuild的時候出錯了,這里也耽擱了大量的時間,因為對C++的編譯環(huán)境不熟,我一直認為是我C++的本地編譯環(huán)境不對,最后各種論壇翻遍后得出的結論是這個ffi模塊有問題,其實在報錯信息中也說的比較明白了
“ForceSet”: 不是“v8::Object”的成員
因為不明白什么意思,就一直在哪里坑次坑次的瞎忙活,其實有大牛早就發(fā)現(xiàn)這個問題,并在ffi的master上開了一個分支來解決這個問題,我們安裝這個分支就好了
npm install ffi@gavignus/node-ffi#torycl/forceset-fix --save
peng一碰冷水潑來 再一次安裝失敗,不過這個問題簡單,就是git沒裝啦,我們裝一個git就行了
終于成功了,天啊,終于安裝成功了,怎么安裝一個npm模塊費了這么大的勁。好了不管了,能安裝成功就謝天謝地了。寫段測試代碼測試一下,看看使用node-ffi到底能不能調(diào)用DLL
const ffi = require("ffi");
const User32 = ffi.Library('user32', {
'GetWindowLongPtrW': ['int', ['int', 'int']],
'SetWindowLongPtrW': ['int', ['int', 'int', 'long']],
'GetSystemMenu': ['int', ['int', 'bool']],
'DestroyWindow': ['bool', ['int']]
})
console.log(User32.GetWindowLongPtrW);
測試結果
E:\code workplace\test>node app.js
{ [Function: proxy] async: [Function] }
確實調(diào)用到了。走到這里心里的大石頭好像可以放下來了,畢竟解決了能不調(diào)用的問題。但是這僅僅是完成了一半。因為這只是在nodejs成功調(diào)用的dll,還沒有在electron中調(diào)用,事實證明兩者還是有一定區(qū)別的,下面就來講講在electron調(diào)用時會出現(xiàn)什么問題。
在electron中使用ffi模塊時報錯
首先說一下ffi.Library加載的dll路徑問題,上面使用到的user32,部分人可能開始犯迷糊了,前面不是說這里應該是dll文件的地址,這個user32哪里冒出來的,其實這是系統(tǒng)的dll文件,也就是說如果我們不寫成路徑的形式,ffi模塊就會自動去系統(tǒng)文件夾中尋找這個文件,有明確的路徑時才會加載該路徑下的dll,可以在C:\Windows\System32和C:\Windows\SysWOW64兩個路徑下發(fā)現(xiàn)這兩個文件,這兩個文件夾是分別放的是32位和64位dll。那到底是使用的那個文件夾下dll那?這里其實就出現(xiàn)了另外一個我們在開發(fā)是需要注意的問題了,因為我們的應用最終在那個環(huán)境上跑這個是不確定的,所以ffi模塊加載的dll在編譯的時候需要編譯32位和64位兩個版本的,我們需要在程序中判斷系統(tǒng)的類型:
//x64代表64位,x86代表32位
const type = process.version.arch;
將不同的編譯環(huán)境編譯的dll放在不同的目錄下,然后根據(jù)系統(tǒng)類型去加載不同文件夾下的dll。如果加載錯誤的dll也是會報錯的,最開始我在這上面是吃過虧的。 正確加載dll后我們將ffi模塊用在electron項目中
//dll.js
const remote = require("electron").remote;
const ffi = remote.require("ffi");
const options = {
User32:(()=>{
return ffi.Library('user32', {
'GetWindowLongPtrW': ['int', ['int', 'int']],
'SetWindowLongPtrW': ['int', ['int', 'int', 'long']],
'GetSystemMenu': ['int', ['int', 'bool']],
'DestroyWindow': ['bool', ['int']]
})
})()
};
export default options;
import Dll from "@/static/js/dll.js";
console.log("Dll:",Dll.User32.GetSystemMenu)
執(zhí)行
npm run dev
我們會發(fā)現(xiàn)應用打開后一片空白并不是我們希望的那樣,打開F12調(diào)試窗口我們會發(fā)現(xiàn)一堆紅色報錯
大致的意思就是ffi加載失敗了。納尼,前面已經(jīng)成功安裝了ffi模塊,并且測試了一下調(diào)用dll沒毛病啊,咋又作妖了呢?事實上electron在使用c++模時還需要根據(jù)electron的版本等信息重新編譯一下,這樣在electron中才能執(zhí)行,我們需要進入ffi模塊執(zhí)行重新編譯的命令,并注入?yún)?shù)
node-gyp rebuild -target=1.8.7 -arch=x64 -dist-url=https://npm./mirrors/atom/
target:electron的版本號 arch : 計算機的架構 dist-url :文件的下載地址,編譯的時候回去這個地址上下載一些額外的文件,具體作用我不是很清楚。這里使用的是國內(nèi)鏡像,不是官方給出的地址,至于為什么,太慢了。然后再來執(zhí)行啟動命令
npm run dev
然后,沒有然后了,應用跑起來了: 這樣就完了嗎?事實證明并沒有,要正在把應用打包出來,才能算結束了。下面說一下在打包的過程中他又可能出現(xiàn)的問題,
打包后運行electron,調(diào)用dll可能出現(xiàn)的問題
執(zhí)行
//構建
npm run build
完成打包后,安裝應用,然后啟動,發(fā)現(xiàn)還是出現(xiàn)了前面那種頁面白屏,ffi模塊調(diào)用失敗的問題,原因是我們在執(zhí)行npm run build的時候,是通過electron-rebuild 來build的,這時會再次執(zhí)行node-gyp rebuild,這里沒有添加任何參數(shù),所以打包出來的應用可能還是會失敗,我們可以參照electron官方的方案來解決使用node原生模塊,最直接的方式就是在項目根目錄下添加一個npm安裝配置文件.npmrc,里面包含了一下npm的運行需要注入的參數(shù)。 .npmrc:
# Electron 的版本。
set npm config --target=1.2.3
# Electron 的系統(tǒng)架構, 值為 ia32 或者 x64。
set npm config --arch=x64
# 下載 Electron 的 headers。
eset npm config --disturl=https://npm./mirrors/atom/
# 告訴 node-pre-gyp 我們是在為 Electron 生成模塊。
set npm config --runtime=electron
通過上面的一系列填坑,差不多可以成功的再electron項目中使用DLL動態(tài)庫了,并且打包出來的應用調(diào)用也沒有問題了。
上面可能講的有點雜,下面稍微捋一下整個的解決流程流程:
- 切換npm官方鏡像到國內(nèi)鏡像
npm config set registry=https://registry.npm.
- 安裝Python,配置Python的系統(tǒng)環(huán)境變量
- 安裝C++構建環(huán)境,這里有兩種方案
1. 安裝Visual Studio 2. 通過npm的方式安裝windows環(huán)境的的C++構建工具包 npm install --global --production windows-build-tools 我推薦使用第二種方式,第一種方式,到目前為止,我也沒有完全跑起來,總是再各個環(huán)節(jié)會出現(xiàn)不同的問題。 - 安裝forceset問題已修復的ffi模塊分支。
npm install ffi@gavignus/node-ffi#torycl/forceset-fix --save
順便提一下,安裝的的使用一定要加–save,不能是–save-dev或不加,因為在build的時候,會將package.json里dependencies下的所有文件都打包到asar文件中去,否則在應用中調(diào)用ffi模塊的時候也會出現(xiàn)模塊找不到的問題。
- 進入下載好的ffi模塊中去重新編譯一下ffi模塊,到此你就可以運行開發(fā)環(huán)境了
node-gyp rebuild -target=1.8.7 -arch=x64 -dist-url=https://npm./mirrors/atom/
- 再項目根目錄下創(chuàng)建npm運行環(huán)境配置參數(shù)文件.npmrc
# Electron 的版本。
set npm config --target=1.2.3
# Electron 的系統(tǒng)架構, 值為 ia32 或者 x64。
set npm config --arch=x64
# 下載 Electron 的 headers。
eset npm config --disturl=https://npm./mirrors/atom/
# 告訴 node-pre-gyp 我們是在為 Electron 生成模塊。
set npm config --runtime=electron
通過上面這一系列的操作。基本上也就可以成功調(diào)用DLL動態(tài)庫了。據(jù)說這種方式比起官方給出的方式性能上有些損失,具體損失到什么程度,我這邊還沒有去測過,也沒有明顯感覺出來。這種方式的優(yōu)勢就是使用簡單,開發(fā)成本低,而且一般的需求也是能夠滿足的,如果團隊有足夠的資源,也可以去嘗試nodejs官方給出的方式,到時候教教我。
總結: 上面寫了很多,其實干貨感覺好像就是上面最后這幾條,寫前面的過程主要是記錄一下我在解決這個問題是遇到的各個問題,然后怎么一步一步去尋找解決方案的,很多小伙伴在使用的過程中可能也會遇見相同的問題,關鍵在于我們遇見問題后不要輕易去下這個問題能不能解決的結論,多嘗試幾種方式,論壇,度娘,谷歌上面多查查,即使查出來的這條信息最終可能并沒什么用,但是多嘗試幾次,自己大概也就知道這個問題出現(xiàn)的原因,可能也會形成相關解決的思路,也許還會有意外的收獲。 最后總結一句:多嘗試,多動手,多思考。 忽略"通假字"…
|