目錄
前言
正文
常見問題梳理
問題一、*.node is not a valid Win32 application
問題二、Error: Dynamic Linking Error: Win32 error 193
問題三、Error: Dynamic Linking Error: Win32 error 126
問題四、Error: Dynamic Symbol Retrieval Error: Win32 error 127
實作步驟和極簡代碼
1. 安裝工具庫
2. 匯入依賴庫
3. 匯出動態庫方法
4. 呼叫匯出方法
結尾
前言
Electron開發程序中,很多時候都會遇到呼叫C++動態庫dll的需求,使用JS呼叫dll庫,聽上去都很高大上,JS和C++基本上沒有什么交集的兩種語言,在這個時候碰出了火花,今天就在Electron的基礎上介紹一下這個神奇的使用程序和程序可能遇到的各種問題,前端同學感興趣的話,歡迎收藏+實踐!
正文
不得不說,想實作JS呼叫C++的dll動態庫,是需要借助第三方工具和中間件的,
nodejs有很多相關的工具類,它們都可以輕松實作使用JavaScript加載和呼叫dll動態庫,同時還負責處理跨JavaScript和C的型別轉換問題,

類似的插件大致有如下幾種:
1. node-ffi-napi
倉庫地址:https://github.com/node-ffi-napi/node-ffi-napi
2. node-ffi
倉庫地址:https://github.com/node-ffi/node-ffi
常見問題梳理
先說說可能遇到的問題,文章的最后給出一個極簡代碼實體,
問題一、*.node is not a valid Win32 application
報錯資訊:
Error: \\?\G:\project\work\electron-quick-start\node_modules\ffi-napi\build\Release\ffi_bindings.node is not a valid Win32 application.
\\?\G:\project\work\electron-quick-start\node_modules\ffi-napi\build\Release\ffi_bindings.node
at process.func [as dlopen] (electron/js2c/asar.js:140:31)
at Object.Module._extensions..node (internal/modules/cjs/loader.js:1034:18)
問題截圖:

原因:依賴庫不是32位的,
解決:這個問題一般比較常見,因為很多依賴庫可能和目標應用型別不一致,大多數情況下都發生在 node_modules 的依賴庫中,
處理起來也非常簡單,只需要重新 rebuild 即可,
在依賴庫中執行命令如下:
node-gyp clean configure build --verbose --arch=ia32
如果還不清楚怎么做,可以參考我之前寫的一篇文章——《Electron常見問題 9 - *.node is not a valid Win32 application》
問題二、Error: Dynamic Linking Error: Win32 error 193
報錯資訊:
G:\project\work\electron-quick-start\dll.dll
(node:23644) UnhandledPromiseRejectionWarning: Error: Dynamic Linking Error: Win32 error 193
at new DynamicLibrary (G:\project\work\electron-quick-start\node_modules\ffi-napi\lib\dynamic_library.js:75:11)
at Object.Library (G:\project\work\electron-quick-start\node_modules\ffi-napi\lib\library.js:47:10)
at G:\project\work\electron-quick-start\main.js:81:18
(node:23644) UnhandledPromiseRejectionWarning: Error: Dynamic Linking Error: Win32 error 193
at new DynamicLibrary (G:\project\work\electron-quick-start\node_modules\ffi-napi\lib\dynamic_library.js:75:11)
at Object.Library (G:\project\work\electron-quick-start\node_modules\ffi-napi\lib\library.js:47:10)
at G:\project\work\electron-quick-start\main.js:81:18
問題截圖:

原因1. 呼叫的動態庫dll是32位的,而目標模塊需要的是64位的,
解決:
重新編譯一份64位的dll動態庫,問題解決,
如果我們換成了64位的dll動態庫,還有這個問題,大概率問題就是dll缺少對應的依賴dll庫,
原因2. 缺少依賴庫,
解決:
從事windows系統開發的小伙伴都知道,我們一般開發的工具dll庫,或多或少都會依賴系統的或者第三方的dll庫,這也是dll動態庫的優勢,可以動態依賴和動態呼叫,如果想省事兒,我們可以直接把需要的系統或者第三方庫,以靜態庫的形式打包進去,這樣拿到哪里就可以用了,大概率不會出現這個問題,所以,我們可以使用depends等工具,查看dll的依賴庫是否都全了,
問題三、Error: Dynamic Linking Error: Win32 error 126
報錯資訊:
(node:17224) UnhandledPromiseRejectionWarning: Error: Dynamic Linking Error: Win32 error 126
at new DynamicLibrary (G:\project\work\electron-quick-start\node_modules\ffi-napi\lib\dynamic_library.js:75:11)
at Object.Library (G:\project\work\electron-quick-start\node_modules\ffi-napi\lib\library.js:47:10)
at G:\project\work\electron-quick-start\main.js:81:18
問題截圖:

原因:路徑問題,很可能就是沒有找需要的dll動態庫,
解決:避免出現這個問題的原因,就是盡可能使用絕對路徑,避免使用相對路徑,當然,如果你可以保證路徑沒有問題的話,這個是隨意的,
因為Electron的除錯啟動路徑和安裝后的啟動路徑是不一樣的,這一點需要特別注意,
注意:Electron在開發模式下啟動的路徑是 node_modules/electron/ 目錄,
問題四、Error: Dynamic Symbol Retrieval Error: Win32 error 127
報錯資訊:
(node:21764) UnhandledPromiseRejectionWarning: Error: Dynamic Symbol Retrieval Error: Win32 error 127
at DynamicLibrary.get (G:\project\work\electron-quick-start\node_modules\ffi-napi\lib\dynamic_library.js:113:11)
at G:\project\work\electron-quick-start\node_modules\ffi-napi\lib\library.js:55:21
at Array.forEach (<anonymous>)
問題截圖:

這個問題的產生原因一般是dll有問題,就是說生成的C++的dll動態庫有問題,一般是因為沒有匯出方法的符號,所以呼叫dll的時候找不到對應的方法,
最簡單的原因就是匯出函式方法時,沒有加 extern "C" ,
居然,也被我遇到了,,,😢
那么 extern "C" 為什么可以影響匯出符號呢?那就需要我們簡單介紹一下它的作用,
本質作用就是為了能夠正確實作C++代碼呼叫其他C語言代碼,
當我們加上 extern "C" 后,會指示編譯器將這部分代碼按照C語言(而不是C++)的方式進行編譯,
由于C++支持函式多載,因此,編譯器編譯函式的程序中會將函式的引數型別也加到編譯后的代碼中,而不僅僅是函式名;而C語言并不支持函式多載,因此編譯C語言代碼的函式時不會帶上函式的引數型別,一般只包括函式名, 這個功能十分有用處,因為在C++出現以前,很多代碼都是C語言寫的,而且很底層的庫也是C語言寫的,為了更好的支持原來的C代碼和已經寫好的C語言庫,需要在C++中盡可能的支持C,因此 extern "C" 就變成了一個必然的選擇,
說了那么多,快把自己動態庫的方法們加上 extern "C",再重新編譯一份吧,
看看是不是問題就解決了,
最后,附上一段代碼實體:
#include <stdint.h>
#if defined(WIN32) || defined(_WIN32)
#define EXPORT __declspec(dllexport)
#else
#define EXPORT
#endif
extern "C" EXPORT int sum(int a, int b) {
return a + b;
}
實作步驟和極簡代碼
能遇到上面這些問題,也算不容易了,
接下來,介紹一下具體步驟,
1. 安裝工具庫
安裝文章開頭介紹的任一工具庫,這里以 ffi-napi 為例,具體命令:
npm i ffi-napi
2. 匯入依賴庫
在代碼中增加 ffi-napi 依賴庫的參考,
代碼:
var ffi = require('ffi-napi')
3. 匯出動態庫方法
接下來,就是使用 ffi-napi 匯出dll動態庫中的需要用到的方法,需要注意的是匯出的宣告陳述句要和dll動態庫中C++方法的名字、引數表、回傳值一致,
這里以一個求和方法 int sum(int a, int b) 為例,代碼如下:
var dllPath = path.resolve("cpp.dll");
var dllfuns = ffi.Library(dllPath, {
'sum': [ 'int', [ 'int', 'int' ] ],
});
4. 呼叫匯出方法
這一步也是簡單的,代碼如下:
console.log('=======================================')
var s = dllfuns.sum(1,2); // 呼叫方法
console.log("call c++ dll function sum, result: ", s);
console.log('=======================================')
代碼執行結果:

完美!!!!!!

漂亮~~~~~~
結尾
實作JS呼叫C++動態庫的介紹到此就結束了,是不是超級簡單,感興趣和需要demo的小伙伴歡迎評論留言,一鍵三連(科普:評論+關注+收藏)也是極好的,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/275496.html
標籤:其他
