Electron加载第三方C++库

前言

在node的程序中,如果有大数据量的计算,处理起来比较慢,可以用C来处理,然后通过回调(callback的形式),返回给node.
那么编写好的C组件如何可以在node.js环境中调用呢.
Node.js 插件是用 C++ 编写的动态链接共享对象,可以使用 require() 函数加载到 Node.js 中,且像普通的 Node.js 模块一样被使用.

它们主要用于为运行在 Node.js 中的 JavaScript 与 C/C++ 库之间提供接口.

如何编写c++组件

以Hello World 示例是一个简单的插件,用 node.js 编写

如下代码:

1
module.exports.hello = () => 'world';

那么怎么用C++写供node.js调用呢?

创建hello.cc文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// hello.cc
#include <node.h>

namespace demo {

using v8::FunctionCallbackInfo;
using v8::Isolate;
using v8::Local;
using v8::Object;
using v8::String;
using v8::Value;

void Method(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
args.GetReturnValue().Set(String::NewFromUtf8(isolate, "world"));
}

void init(Local<Object> exports) {
NODE_SET_METHOD(exports, "hello", Method);
}

NODE_MODULE(NODE_GYP_MODULE_NAME, init)

}

注意 所有的 Node.js 插件必须导出一个如下模式的初始化函数:

1
2
void Initialize(Local<Object> exports);
NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)
  • NODE_MODULE 后面没有分号,因为它不是一个函数(详见 node.h)
  • module_name 必须匹配最终的二进制文件名(不包括 .node 后缀)
  • 在 hello.cc 示例中,初始化函数是 init,插件模块名是 myaddon

构建

当源代码已被编写,它必须被编译成二进制 myaddon.node 文件.
要做到这点,需在项目的顶层创建一个名为 binding.gyp 的文件,它使用一个类似 JSON 的格式来描述模块的构建配置.
该文件会被 node-gyp(一个用于编译 Node.js 插件的工具)使用

1
2
3
4
5
6
7
8
{
"targets": [
{
"target_name": "myaddon",
"sources": [ "hello.cc" ]
}
]
}

需要直接使用 node-gyp 的开发者可以使用 npm install -g node-gyp 命令进行安装

binding.gyp 文件已被创建,使用 node-gyp configure 为当前平台生成相应的项目构建文件.
这会在 build/ 目录下生成一个 Makefile 文件(在 Unix 平台上)或 vcxproj 文件(在 Windows 上).

编译

调用 node-gyp build 命令生成编译后的 myaddon.node 的文件.
它会被放进项目文件 build/Release/ 内.

调用

编译后的二进制插件的文件扩展名是 .node(而不是 .dll 或 .so).
require() 函数用于查找具有 .node 文件扩展名的文件,并初始化为动态链接库.

1
2
3
4
5
// hello.js
const myaddon = require('./build/Release/myaddon');

console.log(myaddon.hello());
// 打印: 'world'

Electron调用C++组件

Electron是GitHub开发的一个开源框架.它允许使用Node.js和Chromium完成桌面GUI应用程序的开发.
我们可以看出 electron 已经集成了前端js环境(Chromium)和后端js环境(Node.js).

那么上面我们已经看到node.js可以直接通过 node-gyp编译c库转化成node.js可以直接交互的二进制 .node 编译文件.
所以我们一样可以理解electron框架下可以自己直接加载 node-gyp编译的c库.

可能遇到的坑

NODE_MODULE_VERSION不匹配

这是因为你当前使用的node.js版本与electron内置的node.js版本不一致,
当你更新系统的node.js版本与electron一致时,编译后启动客户端,发现还是有这样的问题,提示NODE_MODULE_VERSION不匹配.
这可能的原因是node.js版本一致,但是内置的v8引擎版本不一致导致.

如何填坑?
如下解决方案,前提是系统成功安装 node.js环境,python2.7环境,node-gyp环境.
如果你是Windows系统:
强烈建议使用如下方式安装vs环境及python环境

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

electron-rebuild重新编译c++库

在electron环境下编译c++库,确保node.js版本,v8引擎版本和使用环境一模一样.
注意
electron-rebuild 安装到开发环境中,install时必须指定 -dev模式
安装:

1
npm install --save-dev electron-rebuild

执行:

1
./node_modules/.bin/electron-rebuild

编译成功后,build/release 文件夹下有编译生成的 .node 文件,用于electron require此二进制文件.

node-gyp编译时指定系统及electron版本

另外一种方法是使用node-gyp编译时指定系统参数:
编译:

1
node-gyp rebuild --target=7.1.2 --dist-url=https://atom.io/download/electron

注意 7.1.2 是我的electron版本
然后 build/release 目录下生成的 .node 文件就可以在electron中require使用了.