关于在electron中调用C++动态库的经验总结

前言
      electron以nodejs作为底层运行环境,所以自然而然就想到了他能否调用C++编写的动态库,恰好我最近在做一个关于使用electron调用dll的项目,也就花了一点时间去了解和实践,这期间走了不少弯路,这里分享出来,希望能帮助到有遇到相关问题的小伙伴

很多刚入门不久的小伙伴第一个问题可能就是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点:

  1. 安装时报错
  2. 安装成功但是在运行electron时调用ffi时报错
  3. 打包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

peng一碰冷水泼来

再一次安装失败,不过这个问题简单,就是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;

将不同的编译环境编译的dll放在不同的目录下,然后根据系统类型去加载不同文件夹下的dll。如果加载错误的dll也是会报错的,最开始我在这上面是吃过亏的。
      正确加载dll后我们将ffi模块用在electron项目中

//dll.jsconst 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动态库了,并且打包出来的应用调用也没有问题了。

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

  1. 切换npm官方镜像到国内镜像
npm config set registry=https://registry.npm.taobao.org
  1. 安装Python,配置Python的系统环境变量
  2. 安装C++构建环境,这里有两种方案
    1. 安装Visual Studio
    2. 通过npm的方式安装windows环境的的C++构建工具包
    npm install --global --production windows-build-tools
    我推荐使用第二种方式,第一种方式,到目前为止,我也没有完全跑起来,总是再各个环节会出现不同的问题。
  3. 安装forceset问题已修复的ffi模块分支。
    npm install ffi@gavignus/node-ffi#torycl/forceset-fix --save

顺便提一下,安装的的使用一定要加–save,不能是–save-dev或不加,因为在build的时候,会将package.json里dependencies下的所有文件都打包到asar文件中去,否则在应用中调用ffi模块的时候也会出现模块找不到的问题。

  1. 进入下载好的ffi模块中去重新编译一下ffi模块,到此你就可以运行开发环境了
node-gyp rebuild -target=1.8.7 -arch=x64 -dist-url=https://npm.taobao.org/mirrors/atom/
  1. 再项目根目录下创建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官方给出的方式,到时候教教我。

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

(0)

相关推荐

  • nodejs下载、安装和配置

    简介    Node.js 是运行在服务端的 JavaScript.Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境.Node.js 使用了一个事件驱动.非阻塞式 ...

  • Windows10下编译jitsi

    Jitsi Meet是一款免费,开源,安全,简单且可扩展的视频会议解决方案,您可以将其用作独立应用程序或嵌入到Web应用程序中. Jitsi Meet客户端在您的浏览器中运行,因此您无需在计算机上安装 ...

  • React + Electron封装并打包成桌面应用

    Electron是什么 ​Electron 是一个由 GitHub 开发的开源库,通过将 Chromium) 和Node.js 组合并使用 HTML,CSS 和 JavaScript 进行构建 Mac ...

  • 开发笔记:基于Electon的图片采集工具

    题图,由ACE Land 人工智能设计师赞助. 人这一辈子没法做太多的事情, 所以每一件都要做得精彩绝伦. 你的时间有限, 所以不要为别人而活. 不要被教条所限, 不要活在别人的观念里. 不要让别人的 ...

  • vue 开发环境的搭建

    一.整个流程: 安装nodejs >> 安装vue >> 安装vue-cli >> 初始化 webpack(生成代码) >> 安装依赖 >> ...

  • 基于 Vue 的 Electron 项目搭建

    Electron 应用技术体系推荐 目录结构 demo(项目名称) ├─ .electron-vue(webpack配置文件) │ └─ build.js(生产环境构建代码) | └─ dev-cli ...

  • 应用程序设计:在动态库中如何调用外部函数?

    大家好,我是一个动态链接库! 这个名字,相信你一定早就如雷贯耳了. 在计算机早期时代,由于内存资源紧张,我可是发挥了重大的作用! 不论是在 Windows 系统中,还是在 Unix 系列平台上,到处都 ...

  • windows编译darknet静态库和动态库,并在自己的项目中调用

    调用darknet 文章目录 调用darknet 前言 一.编译过程 1.github下载项目 2.编译 二.使用步骤 1.引用库 前言 最近需要做一个图片分类的项目,尝试了很多方法,包括Opencv ...

  • (7条消息) Qt生成和调用动态库dll,和静态库.a(windows和linux通用)

    系统1:ThinkPad T570.Windows10.QT5.12.2(Qt Creater 4.8.2) 一.动态库.dll的创建和调用 1.在qtcreater中按如下步骤创建动态库,动态库名为 ...

  • rpy2库 | 在jupyter中调用R语言代码

    在数据分析中,Python和R各有千秋,虽然Python或R都能在数据分析打通关,从采集.清洗(预处理).分析.可视化,但是在不同的环节,不同的语言易用程度不同.Python胜在干脏活累活,诸如数据采 ...

  • Linux动态库生成以及调用

    Linux下动态库文件的文件名形如 libxxx.so,其中so是 Shared Object 的缩写,即可以共享的目标文件. 在链接动态库生成可执行文件时,并不会把动态库的代码复制到执行文件中,而是 ...

  • UC头条:八字中的四墓库——辰戌丑未

    点击加载图片 四库与钥匙四库是辰戌丑末,库就是库房,库存.有库就是有百宝箱.保险柜.银行卡.有库就要找开库的钥匙,每个库不但有与之相配的钥匙,还有一把锁.有库有钥匙,只要钥匙配对了就有用能打开,库中物 ...

  • 在QT C++中调用 Python并将软件打包发布(裸机可运行)

    为了提高工作效率,需要一个可以自动生成多份相关联的word文档免去繁琐复制粘贴工作的软件.最后选定使用QT C++做界面和主要逻辑程序设计,对word的操作使用python写好对应的函数,然后在QT中 ...

  • C++静态库与动态库深入研究

    这次分享的宗旨是--让大家学会创建与使用静态库.动态库,知道静态库与动态库的区别,知道使用的时候如何选择.这里不深入介绍静态库.动态库的底层格式,内存布局等,有兴趣的同学,推荐一本书<程序员的自 ...

  • gcc/g++ 静态动态库 混链接.

    转载自:http://blog.csdn.net/wangxvfeng101/article/details/15336955 我的环境: centos6 x64. gcc4.4.7 在使用gcc/g ...