在Electron中调用C++动态库 转

转自:https://blog.csdn.net/wang839305939/article/details/83780789

很多刚入门不久的小伙伴第一个问题可能就是electron能不能调用DLL动态库?这里给一个明确的答复是可以的。为什么?因为electron本身就集成了nodejs运行环境,而nodejs又是用C++实现的,相当于C++是他的原配。
既然能调用,那么第二个问题来了,怎么调用?nodejs在官网也给出了方案addon有兴趣的可以看看,但是对于我们前端来说这个方案太过于复杂了,学习成本太高,如果没有c++开发背景还是另辟蹊径吧,electron是可以做到和C++混合开发的,如果团队中有C++的开发人员也可以尝试,结合C++本身的优势,开发出来的产品效果可能会更好,我们常用的IDE工具VSCode其实有部分功能也是使用C++来时实现的,这其中规避了一些electron的缺点。另一种调用DLL动态库的方案就是这里要重点介绍的模块node-ffi。
其实这个模块时用起来非常的简单,先贴一段官方使用案例,简单加了几句注释:

var ffi = require('ffi');//引入ffi模块
/*
*使用ffi模块将dll和js打通,可以把它看做是RPC(远程调用协议)
*@libm 动态库的绝对地址例如"C://plugin/test.dll"
*@ceil 动态库中方法的名称 double返回值的数据类型 ['double'] 这是函数输入参数的数据类型
*这里提一下,应为C++是是属于强类型语言这个js不同,所以这里一定要指定返回参数和输入参数的类型
*/
var libm = ffi.Library('libm', {
'ceil': [ 'double', [ 'double' ] ]
});
//通过上面的注册的libm对象来调用dll中的ceil方法
libm.ceil(1.5); // 2

这一切看起来就是这么简单,但是在这几句简单的代码后面,会让刚入门的小伙伴非常的蛋疼,因为在使用的过程中你会遇到各种问题,而且这些问题会让你对本身的目的产生动摇,目前公开的环境能够支持nodejs调用的dll的npm包好像就只有ffi,而在使用中出现的问题,貌似你尝试了所有解决问题的方案,最终还是解决不了。我自己在搞这个的时候也弄了大概一周时间,各种环境捣腾,各种测试。最后弄出来了,这里总结一下这个过程中遇见的问题,打开分为这么3点:

安装时报错
安装成功但是在运行electron时调用ffi时报错
打包electron时报错
安装过程中报错

//全局安装原生模块编译模块,编译ffi模块时需要用到的,这里一定要安装
npm install node-gyp -g
//安装ffi模块
npm install ffi --save
&nbsp这时安装ffi会提示Python没有安装,需要安装python,我安装的是Python2.7.13。完成之后当我兴高采烈的再去安装ffi模块,结果失望的发现下一个错误来了:

错误非常明显,就是C++的构建环境没有,后面也告诉了我们要怎么来解决:

1). 就是安装.NET Framework 2.0 。一般我们使用的windows系统本本身会自动带上.NET Framework的,只是版本可能会有所区别而已,我们可以去看看C:\Windows\Microsoft.NET\Framework目录下看看到底有没安装,安装了什么版本 ,一般来说这里不需要重新再去安装的,所以这里的第一条建议就可以忽略了。
2). 第二条建议就是安装Microsoft Visual Stu,熟悉C++开发的人可能对它很熟悉,对前端来所就是比较陌生了,前端可以把他理解C++的编译环境,这里面需要下载很多C++的组件库,在node-gyp编译的时候会使用到,就好像nodejs一样,如果没有装nodejs环境,在使用npm进行安装的时候,及会提示命令找不到等等。咋一看,路子好像挺对的,因为node-gyp会重新编译node-ffi模块,所以使用到C++的环境很正常,也是因为这样我就吭呲吭吃去安装了,因为Visual Stu很大,有好几个G,像我这种家里只装了10M带宽人来说,你懂的。晚上睡觉的时候开始下载,第二天一早起来开始安装,安装好了,又开始安装ffi模块,心里那种期待的感觉,只有程序员能理解。结果没什么意外还是失败。
经过各种尝试发现,其实我门除了安装VS外其实还可以通过npm 安装c++在windows环境下的构建工具windows-build-tools,好了不多说了直接开始干

npm install --global --production windows-build-tools

这里可能需要等待的时间比较久一点,有点耐心,好东西都是值得等待的。

安装完成后再来安装ffi模块,结果还是让人失望,不信你看,是不是和你遇见的一样:

通过日志可以看出是在node-gyp rebuild的时候出错了,这里也耽搁了大量的时间,因为对C++的编译环境不熟,我一直认为是我C++的本地编译环境不对,最后各种论坛翻遍后得出的结论是这个ffi模块有问题,其实在报错信息中也说的比较明白了

“ForceSet”: 不是“v8::Object”的成员

因为不明白什么意思,就一直在哪里坑次坑次的瞎忙活,其实有大牛早就发现这个问题,并在ffi的master上开了一个分支来解决这个问题,我们安装这个分支就好了

npm install ffi@gavignus/node-ffi#torycl/forceset-fix --save

再一次安装失败,不过这个问题简单,就是git没装啦,我们装一个git就行了

终于成功了,天啊,终于安装成功了,怎么安装一个npm模块费了这么大的劲。好了不管了,能安装成功就谢天谢地了。写段测试代码测试一下,看看使用node-ffi到底能不能调用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] }

确实调用到了。走到这里心里的大石头好像可以放下来了,毕竟解决了能不调用的问题。但是这仅仅是完成了一半。因为这只是在nodejs成功调用的dll,还没有在electron中调用,事实证明两者还是有一定区别的,下面就来讲讲在electron调用时会出现什么问题。

在electron中使用ffi模块时报错

首先说一下ffi.Library加载的dll路径问题,上面使用到的user32,部分人可能开始犯迷糊了,前面不是说这里应该是dll文件的地址,这个user32哪里冒出来的,其实这是系统的dll文件,也就是说如果我们不写成路径的形式,ffi模块就会自动去系统文件夹中寻找这个文件,有明确的路径时才会加载该路径下的dll,可以在C:\Windows\System32和C:\Windows\SysWOW64两个路径下发现这两个文件,这两个文件夹是分别放的是32位和64位dll。那到底是使用的那个文件夹下dll那?这里其实就出现了另外一个我们在开发是需要注意的问题了,因为我们的应用最终在那个环境上跑这个是不确定的,所以ffi模块加载的dll在编译的时候需要编译32位和64位两个版本的,我们需要在程序中判断系统的类型:

//x64代表64位,x86代表32位
const type = process.version.arch;
1
2
将不同的编译环境编译的dll放在不同的目录下,然后根据系统类型去加载不同文件夹下的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)

执行

npm run dev

我们会发现应用打开后一片空白并不是我们希望的那样,打开F12调试窗口我们会发现一堆红色报错

大致的意思就是ffi加载失败了。纳尼,前面已经成功安装了ffi模块,并且测试了一下调用dll没毛病啊,咋又作妖了呢?事实上electron在使用c++模时还需要根据electron的版本等信息重新编译一下,这样在electron中才能执行,我们需要进入ffi模块执行重新编译的命令,并注入参数

node-gyp rebuild -target=1.8.7 -arch=x64 -dist-url=https://npm.taobao.org/mirrors/atom/
target:electron的版本号
arch : 计算机的架构
dist-url :文件的下载地址,编译的时候回去这个地址上下载一些额外的文件,具体作用我不是很清楚。这里使用的是国内镜像,不是官方给出的地址,至于为什么,太慢了。然后再来执行启动命令

npm run dev
然后,没有然后了,应用跑起来了:

这样就完了吗?事实证明并没有,要正在把应用打包出来,才能算结束了。下面说一下在打包的过程中他又可能出现的问题,

打包后运行electron,调用dll可能出现的问题

执行

//构建
npm run build
完成打包后,安装应用,然后启动,发现还是出现了前面那种页面白屏,ffi模块调用失败的问题,原因是我们在执行npm run build的时候,是通过electron-rebuild 来build的,这时会再次执行node-gyp rebuild,这里没有添加任何参数,所以打包出来的应用可能还是会失败,我们可以参照electron官方的方案来解决使用node原生模块,最直接的方式就是在项目根目录下添加一个npm安装配置文件.npmrc,里面包含了一下npm的运行需要注入的参数。
.npmrc:

# Electron 的版本。
set npm config --target=1.2.3
# Electron 的系统架构, 值为 ia32 或者 x64。
set npm config --arch=x64
# 下载 Electron 的 headers。
eset npm config --disturl=https://npm.taobao.org/mirrors/atom/
# 告诉 node-pre-gyp 我们是在为 Electron 生成模块。
set npm config --runtime=electron

通过上面的一系列填坑,差不多可以成功的再electron项目中使用DLL动态库了,并且打包出来的应用调用也没有问题了。

上面可能讲的有点杂,下面稍微捋一下整个的解决流程流程:

切换npm官方镜像到国内镜像
npm config set registry=https://registry.npm.taobao.org

安装Python,配置Python的系统环境变量
安装C++构建环境,这里有两种方案
1. 安装Visual Studio
2. 通过npm的方式安装windows环境的的C++构建工具包
npm install --global --production windows-build-tools
我推荐使用第二种方式,第一种方式,到目前为止,我也没有完全跑起来,总是再各个环节会出现不同的问题。
安装forceset问题已修复的ffi模块分支。
npm install ffi@gavignus/node-ffi#torycl/forceset-fix --save
顺便提一下,安装的的使用一定要加–save,不能是–save-dev或不加,因为在build的时候,会将package.json里dependencies下的所有文件都打包到asar文件中去,否则在应用中调用ffi模块的时候也会出现模块找不到的问题。

进入下载好的ffi模块中去重新编译一下ffi模块,到此你就可以运行开发环境了
node-gyp rebuild -target=1.8.7 -arch=x64 -dist-url=https://npm.taobao.org/mirrors/atom/

再项目根目录下创建npm运行环境配置参数文件.npmrc
# Electron 的版本。
set npm config --target=1.2.3
# Electron 的系统架构, 值为 ia32 或者 x64。
set npm config --arch=x64
# 下载 Electron 的 headers。
eset npm config --disturl=https://npm.taobao.org/mirrors/atom/
# 告诉 node-pre-gyp 我们是在为 Electron 生成模块。
set npm config --runtime=electron

通过上面这一系列的操作。基本上也就可以成功调用DLL动态库了。据说这种方式比起官方给出的方式性能上有些损失,具体损失到什么程度,我这边还没有去测过,也没有明显感觉出来。这种方式的优势就是使用简单,开发成本低,而且一般的需求也是能够满足的,如果团队有足够的资源,也可以去尝试nodejs官方给出的方式,到时候教教我。

总结:
上面写了很多,其实干货感觉好像就是上面最后这几条,写前面的过程主要是记录一下我在解决这个问题是遇到的各个问题,然后怎么一步一步去寻找解决方案的,很多小伙伴在使用的过程中可能也会遇见相同的问题,关键在于我们遇见问题后不要轻易去下这个问题能不能解决的结论,多尝试几种方式,论坛,度娘,谷歌上面多查查,即使查出来的这条信息最终可能并没什么用,但是多尝试几次,自己大概也就知道这个问题出现的原因,可能也会形成相关解决的思路,也许还会有意外的收获。
最后总结一句:多尝试,多动手,多思考。

点赞
  1. RouletteRogue说道:
    Google Chrome Windows 10
    https://t.me/s/officials_pokerdom/3160
  2. AceSorcerer说道:
    Google Chrome Windows 10
    https://t.me/s/ezcash_officials
  3. LuckyBandit说道:
    Google Chrome Windows 10
    http://images.google.ki/url?q=https://t.me/s/officials_7k/375
  4. AceSorcerer说道:
    Google Chrome Windows 10
    В мире игр, где любой ресурс стремится привлечь заверениями быстрых выигрышей, рейтинг лучших онлайн казино на деньги онлайн является как раз той картой, которая проводит сквозь заросли рисков. Тем ветеранов плюс начинающих, кто надоел из-за фальшивых заверений, такой инструмент, чтоб увидеть подлинную выплату, как тяжесть ценной монеты у ладони. Минус пустой воды, только проверенные клубы, там rtp не лишь цифра, а реальная удача.Собрано на основе яндексовых трендов, словно ловушка, которая захватывает наиболее свежие тренды по сети. Тут минуя роли для шаблонных приёмов, всякий момент словно ставка в покере, там блеф выявляется немедленно. Профи понимают: по России манера речи на иронией, там юмор маскируется под совет, позволяет миновать ловушек.На https://telegra.ph/Don8Play---svezhee-nachalo-01-06 такой список находится будто открытая колода, подготовленный к старту. Зайди, коли нужно почувствовать биение подлинной игры, минуя обмана плюс разочарований. Игрокам тех знает вес выигрыша, такое будто взять карты в руках, минуя пялиться на экран.

发表回复

电子邮件地址不会被公开。必填项已用 * 标注