JS调用C++动态库dll

常见问题梳理

问题一、*.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. 调用导出方法

结尾


环境总结  nodejs调dll,环境安装报错处理

1.要安装Visual Studio 2019
2.node v16.14.0或以上
3.安装python-2.7.15,并配置他的环境变量
4.全局安装node-gyp v9.0.0
5.全局安装最新的windows-build-tools
6.最后在项目里安装最新的ffi-napi
以上6步都完成后,就可以用node调用dll文件了

前言

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;
}

 问题五:安装失败,提示node-gyp rebuild 失败

如果windows-build-tools没有安装好,在npm install ffi时候会出现错误。

如果已经安装了node-gyp,则需要先删除C:\Users\Administrator\.node-gyp 目录,再卸载npm uninstall node-gyp。

重新安装npm install -g node-gyp.

然后安装

npm install --global windows-build-tools

安装完毕之后再执行npm install ffi就可以了。 


实现步骤和极简代码

能遇到上面这些问题,也算不容易了。

接下来,介绍一下具体步骤。

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('=======================================')

代码执行结果:

  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Node.js可以使用`ffi`模块调用设备驱动的dll动态库。`ffi`模块 stands for `Foreign Function Interface`(外部函数接口),它允许Node.js应用程序直接调用动态链接库中的C/C++函数。 使用`ffi`模块调用设备驱动的dll动态库,首先需要安装`ffi`模块。可以使用npm(Node.js的包管理器)通过以下命令进行安装: ```shell npm install ffi ``` 安装完成后,可以在Node.js应用程序中使用`ffi`模块调用设备驱动的dll动态库。首先需要引入`ffi`模块: ```javascript const ffi = require('ffi'); ``` 然后,使用`ffi.Library`方法加载dll动态库,并定义要调用的函数: ```javascript const libraryPath = 'path/to/your/device/driver.dll'; const myLibrary = ffi.Library(libraryPath, { 'functionName1': ['returnType1', ['parameterType1', 'parameterType2']], 'functionName2': ['returnType2', ['parameterType3', 'parameterType4']] }); ``` 在上述代码中,`functionName1`和`functionName2`是设备驱动DLL中的函数名,`returnType1`和`returnType2`是函数的返回值类型,`parameterType1`、`parameterType2`、`parameterType3`和`parameterType4`是参数的类型。 接下来,可以通过调用`myLibrary`对象的方法来调用设备驱动DLL中的函数: ```javascript myLibrary.functionName1(parameter1, parameter2); ``` 其中,`parameter1`和`parameter2`是要传递给设备驱动DLL函数的参数。 通过以上步骤,就可以在Node.js应用程序中调用设备驱动的dll动态库了。当然,在调用之前,需要确保设备驱动dll动态库的路径正确,以及设备驱动dll动态库中的函数名、参数类型和返回值类型的定义正确。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值