一前言
项目需要在自助打印机中调用DLL动态库,使用环境时windows10 64位操作系统。由于特殊原因需要前端实现,单纯的vue+浏览器环境是实现不了该需求的,需要node环境,所以这里选择electron+vue+ffi-napi来实现调用DLL动态库。开发环境如下:
- vue-cli: 4.5.13
- node: 14.17.0
- vue: 2.6.11
- electron: 12.0.0
- ffi-napi: 4.0.3
- vue-cli-plugin-electron-builder: 2.0.0
- node-gyp: 8.1.0
二环境准备
在安装依赖之前,先做些准备工作。因为安装ffi_napi依赖的时候,需要有编译环境,否则会因为无法编译而报错。
# 使用Vue Cli创建vue项目的时候会用到,全局安装node-sass
npm i -g node-sass
# NodeJS 编译 C/C++ 依赖用到
npm i -g node-gyp
# windows 编译工具,需要用管理员身份运行 PowerShell,如果 报错 Could not install Visual Studio Build Tools. 则到 C:\Users\xx\.windows-build-tools 目录下 手工进行安装,安装成功后在执行上面的命令。
npm i -g --production windows-build-tools
坑1
安装node-gyp成功,后续使用node-gyp build
命令时,提示在某位置找不到\node-gyp\bin\node-gyp.js
,网络上解决方案是清除node环境然后重新安装node环境。个人解决方案是在node的node_global中找到node-gyp文件夹,然后复制到上面命令的位置上,可以解决问题。
坑2
执行npm i -g --production windows-build-tools
命令需要管理员身份运行的终端。
坑3
安装windows-build-tools时,python安装成功后,visual studio 15生成工具一直在等待。这时可以先结束当前进程。找到C:\Users\xx\.windows-build-tools
目录下双击vs_BuildTools.exe手动安装,安装内容如下图所示。
三生成项目
1生成vue项目
# 全局安装vue-cli
npm i -g @vue/cli
vue create electron_vue_dll_demo
# 选择vue2.x,不选history模式,CSS预处理node-sass,选择router、vuex和axios等等。
2安装 electron-builder 插件
cd electron_vue_dll_demo
vue add electron-builder
# 选择electron-12.0.0版本。
坑4
安装electron等待时间比较长,可以提前配置安装源。
# 添加配置,被保存到了 <windows用户主目录>/.npmrc 配置文件中
npm set registry https://registry.npm.taobao.org/
npm set ELECTRON_MIRROR https://npm.taobao.org/mirrors/electron/
npm set SASS_BINARY_SITE http://npm.taobao.org/mirrors/node-sass
npm set PYTHON_MIRROR http://npm.taobao.org/mirrors/python
# 非必须,备以后使用
npm i chromedriver -g --chromedriver_cdnurl=http://npm.taobao.org/mirrors/chromedriver
3安装 ffi-napi 依赖
npm i ffi-napi ref-napi ref-array-napi ref-struct-napi -S
ffi-napi会自动调用windows编译工具进行编译。但是ref-napi不会,还需要手动执行node-gyp
命令进行编译
cd node_modules\ref-napi\
node-gyp configure
node-gyp build
4引入DLL动态库
由于是DEMO,我们先将拷贝到项目目录下。Home.vue的script部分如下:
import { Library } from "ffi-napi";
import path from "path";
const libPath = path.resolve("demo-dll.dll");
Home.vue的created生命周期中调用:
const libm = Library(libPath, {
returnNumber2: ["int", []],
returnString: ["string", []],
sum: ["int", ["int", "int"]],
});
const a = libm.returnNumber2(); // 2
const b = libm.returnString(); // returnString
const c = libm.sum(2, 3); // 2+3 = 5
console.log(a, b, c);
结果打印如下:
2 "returnString" 5
坑5
electron里还需要设置nodeIntegration:true
。
在vue.config.js中设置如下:
module.exports = {
pluginOptions: {
electronBuilder: {
nodeIntegration: true
},
},
};
坑6
首次运行报错如下:
No native build was found for platform=win32 arch=x64 runtime=electron abi=76 uv=1 libc=glibc
at Function.load.path (webpack-internal:///382:47)
at load (webpack-internal:///382:22)
at eval (webpack-internal:///71:1)
at Object.<anonymous> (renderer.prod.js:1)
at N (renderer.prod.js:1)
at eval (webpack-internal:///371:5)
at Object.<anonymous> (renderer.prod.js:1)
at N (renderer.prod.js:1)
at eval (webpack-internal:///409:17268)
at Module.<anonymous> (renderer.prod.js:1)
因为找不到本地编译模块导致。查询vue-cli-plugin-electron-builder插件文档,如下图:
要将本地的包配置到webpack的externals(外部扩展)中指定。引用 webpack官方文档中的话:
防止将某些import的包(package)打包到bundle中,而是在运行时(runtime)再去从外部获取这些扩展依赖(external dependencies)。 在vue.config.js中设置如下:
module.exports = {
pluginOptions: {
electronBuilder: {
nodeIntegration: true,
externals: ['ffi-napi', 'ref-napi']
},
},
};
坑7
运行报错:Error: Dynamic Linking Error: Win32 error 126。
原因:路径问题,很可能就是没有找需要的dll动态库。
解决:避免出现这个问题的原因,就是尽可能使用绝对路径,避免使用相对路径。当然,如果你可以保证路径没有问题的话,这个是随意的。
因为Electron的调试启动路径和安装后的启动路径是不一样的,这一点需要特别注意。
坑8
运行报错:Error: Dynamic Linking Error: Win32 error 193。
原因1:调用的动态库dll是32位的,而目标模块需要的是64位的。
解决:重新编译一份64位的dll动态库,问题解决。
如果我们换成了64位的dll动态库,还有这个问题,大概率问题就是dll缺少对应的依赖dll库。
原因2:缺少依赖库。
解决:从事windows系统开发的小伙伴都知道,我们一般开发的工具dll库,或多或少都会依赖系统的或者第三方的dll库。这也是dll动态库的优势,可以动态依赖和动态调用。如果想省事儿,我们可以直接把需要的系统或者第三方库,以静态库的形式打包进去,这样拿到哪里就可以用了,大概率不会出现这个问题。所以,我们可以使用depends等工具,查看dll的依赖库是否都全了。
坑9
运行报错:Error: Dynamic Symbol Retrieval Error: Win32 error 127。
这个问题的产生原因一般是dll有问题。就是说生成的C++的dll动态库有问题,一般是因为没有导出方法的符号,所以调用dll的时候找不到对应的方法。
最简单的原因就是导出函数方法时,没有加 extern "C" 。
5打包
执行打包脚本:
npm run electron:build
坑10
执行exe文件后:
这个问题是因为找不到DLL文件。原因是 打包的时候,没有将项目中的dll文件拷贝到最终生成的dist_electron\win-unpacked 文件夹中。这同样需要在vue.config.js文件中做配置:
module.exports = {
pluginOptions: {
electronBuilder: {
nodeIntegration: true,
//因为这两个模块中包含原生 C代码,所以要在运行的时候再获取,而不是被webpack打包到bundle中
externals: ['ffi-napi', 'ref-napi'],
builderOptions: {
extraResources: {
// 拷贝静态文件到指定位置,否则打包之后出现找不到资源的问题.将整个resources目录拷贝到 发布的根目录下
from: 'resources/',
to: './'
}
}
}
}
}
附录
感谢:
文章中动态库的例子:github.com/alwxkxk/dem… 如果出现193错误,则需要使用VS重新生成X64的dll动态库。