使用NAPI和C++开发Node插件(windows)

Posted by admin on 2019-11-27

Node支持两种C++插件形式,一种是依赖V8引擎,一种是使用NAPI。本文主要讲如何利用NAPI开发插件。

为什么要开发插件?

可能的原因有:

  1. 效率上的考虑,因为C++通常会比较快 :)
  2. 安全上的考虑,相比于JS,C++反编译毕竟难一些。(JS也有很多加密、混淆工具,但是大概难以同时满足效率和安全)

开始

安装node/npm(略)
先去看看N-API的官方文档,N-API,讲得比较粗略,读完仍然一头雾水。
新建工作目录

安装依赖

1
2
3
npm install node-gyp --save-dev
npm install node-addon-api
npm install -g --production windows-build-tools

bingding.gyp

新建文件bingding.gyp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"targets": [{
"target_name": "testaddon",
"cflags!": [ "-fno-exceptions" ],
"cflags_cc!": [ "-fno-exceptions" ],
"sources": [
"functionexample.cpp"
],
'include_dirs': [
"<!@(node -p \"require('node-addon-api').include\")"
],
'libraries': [],
'dependencies': [
"<!(node -p \"require('node-addon-api').gyp\")"
],
'defines': [ 'NAPI_DISABLE_CPP_EXCEPTIONS' ]
}]
}

cflags 和 cflags_cc可以根据需要增删

functionexample.cpp

新建文件functionexample.h

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <napi.h>

namespace functionexample {

std::string hello();
Napi::String HelloWrapped(const Napi::CallbackInfo& info);

int add(int a, int b);
Napi::Number AddWrapped(const Napi::CallbackInfo& info);

Napi::Object Init(Napi::Env env, Napi::Object exports);

}

新建文件functionexample.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include "functionexample.h"

std::string functionexample::hello(){
return "Hello World";
}

int functionexample::add(int a, int b){
return a + b;
}

Napi::String functionexample::HelloWrapped(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
Napi::String returnValue = Napi::String::New(env, functionexample::hello());
return returnValue;
}


Napi::Number functionexample::AddWrapped(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
if (info.Length() < 2 || !info[0].IsNumber() || !info[1].IsNumber()) {
Napi::TypeError::New(env, "Number expected").ThrowAsJavaScriptException();
}

Napi::Number first = info[0].As<Napi::Number>();
Napi::Number second = info[1].As<Napi::Number>();

int returnValue = functionexample::add(first.Int32Value(), second.Int32Value());

return Napi::Number::New(env, returnValue);
}

Napi::Object functionexample::Init(Napi::Env env, Napi::Object exports) {
exports.Set("hello", Napi::Function::New(env, functionexample::HelloWrapped));
exports.Set("add", Napi::Function::New(env, functionexample::AddWrapped));
return exports;
}

Napi::Object InitAll(Napi::Env env, Napi::Object exports) {
return functionexample::Init(env,exports);
}

NODE_API_MODULE(testaddon, InitAll)

编译

1
node-gyp configure build

最后如果gyp info ok表示成功了。
如果遇到NODE_MODULE_VERSION xx 不匹配的问题,请下载electron-rebuild重新编译应该可以解决。

1
2
npm install electron-rebuild --save
./node_modules/.bin/electron-rebuild

测试

新建 test.js

1
2
const testAddon = require('./build/Release/testaddon.node');
console.log(testAddon.hello());

然后

1
2
3
$ node test.js
Hello World

参考

Beginners guide to writing NodeJS Addons