基于Vue Electron builder 项目调用DLL动态库

6,866 阅读6分钟

一前言

项目需要在自助打印机中调用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手动安装,安装内容如下图所示。

image.png

三生成项目

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插件文档,如下图:

image.png

要将本地的包配置到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文件后:

image.png 这个问题是因为找不到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动态库。