Nodejs C++插件(N-API)

N-API 作为Nodejs项目的一部分,旨在更加便捷稳定的创建Nodejs Native 插件。这个API作为应用二进制接口(ABI),在Node.js的各个版本中都是稳定的,而且ABI允许一个在主要版本编译的模块,在以后的Node.js版本上运行,而无需重新编译。

0. 环境搭建

  1. 安装最新的npm,且Nodejs版本10.x以上
  2. 工具链
    • Windows执行 npm install --global windows-build-tools
    • MacOS执行 xcode-select --install
    • Linux使用系统自带的GCC即可
  3. 安装构建工具 npm install --global node-gyp
  4. 初始化测试项目 npm init
  5. 添加napi模块npm i node-addon-api
  6. 为VSCode添加智能提示所需头文件(以Windows为例)
    • Ctrl+Shift+P -> C++ 添加 c_cpp_properties.json 文件

    • 执行 node-gyp configure 查看 -Dnode_root_dir 选项打印,确认node头文件目录

    • c_cpp_properties.json includePath 中加入 C:\\Users\\gaobowen\\AppData\\Local\\node-gyp\\Cache\\10.16.0\\include\\node

1. JS中调用C++方法

1.1 JS中调用源文件的C++方法

  1. 编写功能函数

    test.h、test.cc
#ifndef SAMPLE_TEST_H
#define SAMPLE_TEST_H
#include <napi.h>
#include <windows.h>
#include <sstream>
#include <thread> Napi::Value plus(const Napi::CallbackInfo& info); #endif
#include "test.h"

Napi::Value plus(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
Napi::EscapableHandleScope scope(env);
if (info.Length() < 1)
return Napi::Boolean::New(env, false);
if (!info[0].IsNumber())
return Napi::Boolean::New(env, false);
if (!info[1].IsNumber())
return Napi::Boolean::New(env, false);
auto a = info[0].As<Napi::Number>().DoubleValue();
auto b = info[1].As<Napi::Number>().DoubleValue(); return Napi::Number::New(env, a + b);
}
  1. 编写导出函数

    exports.cc
#include <napi.h>
#include "test.h" Napi::Object Init(Napi::Env env, Napi::Object exports)
{
exports.Set(Napi::String::New(env, "plus"), Napi::Function::New(env, plus));
return exports;
} NODE_API_MODULE(napi_sample, Init)
  1. 添加 binding.gyp 文件
{
"targets": [
{
"target_name": "napi_sample",
"sources": [ ],
"cflags!": [ "-fno-exceptions" ],
"cflags_cc!": [ "-fno-exceptions" ],
"defines": [ "NAPI_DISABLE_CPP_EXCEPTIONS" ],
"include_dirs": [
"<!--@(node -p \"require('node-addon-api').include\")"
],
"conditions": [
['OS=="win"', {
"sources": [ "sample/exports.cc", "sample/test.cc" ],
}]
]
}
]
}
  1. 命令行执行 node-gyp rebuild 生成 ./build/Release/napi_sample.node文件
  2. 测试使用napi_sample.node插件

    node .\test.js
let { plus } = require('./build/Release/napi_sample.node');

console.log('test export=-->', plus);
console.log('test call=>', plus(1.2, 1.3));

控制台输出

test export=> function () { [native code] }
test call=> 2.5

1.2 JS中调用动态库的C++方法

Napi::Value call_dll(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
Napi::EscapableHandleScope scope(env);
//注,若动态库还有其他依赖库,请放在同一级目录下
auto m = LoadLibraryExA("user32.dll", NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
typedef int(*MyMessageBoxA)(int, char*, char*, int);
auto box = (MyMessageBoxA)GetProcAddress(m, "MessageBoxA");
box(0, "js call dll function", "abc", 0); return Napi::Boolean::New(env, true);
}

控制台输出

test call_dll=> true

2. C++中调用JS方法

2.1. C++单线程调用JS方法

test.cpp

Napi::Value cpp_call_js(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
Napi::EscapableHandleScope scope(env);
if (info.Length() < 0)
return Napi::Boolean::New(env, false);
if (!info[0].IsFunction())
return Napi::Boolean::New(env, false);
auto jscb = info[0].As<napi::function>(); //回调函数入参
napi_value a, b;
napi_create_double(env, 1, &a);
napi_create_double(env, 2, &b); napi_value argv[] = { a, b }; //回调函数返回值
napi_value result;
napi_call_function(env, env.Global(), jscb, 2, argv, &result); return Napi::Value(env, result);
}

test.js

let { cpp_call_js } = require('./build/Release/napi_sample.node');
(async function () { let c = 3;
let js_callback = function(a, b){
return a + b + c;
}
console.log('test cpp_call_js=>', cpp_call_js(js_callback)); })();

控制台输出

test cpp_call_js=> 6

2.2. C++多线程调用JS方法

test.cpp

Napi::ThreadSafeFunction tsfunc;
void thread_function() {
auto threadid = std::this_thread::get_id();
std::stringstream ss;
ss << threadid;
auto threadidstr = new std::string(ss.str()); auto callback = [threadid](Napi::Env env, Napi::Function jscb, std::string* p_str) {
jscb.Call({ Napi::String::New(env, *p_str) });
delete p_str;
}; tsfunc.Acquire();
napi_status status = tsfunc.BlockingCall(threadidstr, callback);
if (status != napi_ok) {
printf("tsfunc.BlockingCall error.\n");
}
tsfunc.Release(); } Napi::Value cpp_thread_call_js(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
Napi::EscapableHandleScope scope(env);
if (info.Length() < 0)
return Napi::Boolean::New(env, false);
if (!info[0].IsFunction())
return Napi::Boolean::New(env, false);
auto jscb = info[0].As<napi::function>();
tsfunc = Napi::ThreadSafeFunction::New(env, jscb, "my-tsfunc", 2, 2);
std::thread(thread_function).join();
std::thread(thread_function).join();
return Napi::Boolean::New(env, true);
}

test.js

let { cpp_thread_call_js } = require('./build/Release/napi_sample.node');
(async function () { let js_callback_async = function(id){
console.log('thread_id',id);
}
console.log('test cpp_thread_call_js=>', cpp_thread_call_js(js_callback_async)); })();

控制台输出

test cpp_thread_call_js=> true
thread_id 6768
thread_id 12352

DEMO地址

Nodejs C++插件(N-API)的更多相关文章

  1. Linux中安装nodejs及插件

    Linux中安装nodejs及插件 1.去官网下载安装包 英文网址:https://nodejs.org/en/download/ 中文网址:http://nodejs.cn/download/ 通过 ...

  2. 教你如何通过CodeArts IDE插件调用API,高效合成语音

    摘要:本实验基于华为云自研CodeArts IDE,指导用户通过使用华为云API,来实现一个文字合成语音的应用. 本文分享自华为云社区<通过CodeArts IDE插件调用API,高效合成语音! ...

  3. NodeJS学习三之API

    Node采用V8引擎处理JavaScript脚本,最大特点就是单线程运行,一次只能运行一个任务.这导致Node大量采用异步操作(asynchronous opertion),即任务不是马上执行,而是插 ...

  4. sublime text 安装nodejs开发插件

    系统:windows10nodejs版本:v6.1.14 请先配置好环境变量,这里就不说啦. 下载并设置nodejs插件 下载地址为https://github.com/tanepiper/Subli ...

  5. nodejs 各种插件

    __dirname:全局变量,存储的是文件所在的文件目录 __filename:全局变量,存储的是文件名 代码:dirname.jsconsole.log(__dirname); 运行node dir ...

  6. FullCalendar日历插件(中文API)

    FullCalendar提供了丰富的属性设置和方法调用,开发者可以根据FullCalendar提供的API快速完成一个日历日程的开发,本文将FullCalendar的常用属性和方法.回调函数等整理成中 ...

  7. PhpStorm插件之Api Debugger

    安装插件 File->Setting->Pluugins   搜索  Api Debugger 如何使用 安装完插件后,RESTART IDE,在编辑器右侧 即可找到最新安装的 Api D ...

  8. Elasticsearch--集群管理_别名&插件&更新API

    目录 使用索引别名 别名 创建别名 修改别名 合并命令 获取所有别名 移除别名 别名中过滤 别名和路由 Elasticsearch插件 基础知识 安装插件 移除插件 更新设置API 使用索引别名 通过 ...

  9. 【Azure 应用服务】NodeJS Express + MSAL 实现API应用Token认证(AAD OAuth2 idToken)的认证实验 -- passport.authenticate('oauth-bearer', {session: false})

    问题描述 在前两篇博文中,对NodeJS Express应用 使用MSAL + AAD实现用户登录并获取用户信息,获取Authorization信息 ( ID Token, Access Token) ...

  10. 笔记-Nodejs中的核心API之Events

    最近正在学习Node,在图书馆借了基本关于Node的书,同时在网上查阅资料,颇有收获,但是整体感觉对Node的理解还是停留在一个很模棱两可的状态.比如Node中的模块,平时练习就接触到那么几个,其他的 ...

随机推荐

  1. 【SVN】属性功能配置

    一.配置SVN提交模板 更改SVN提交信息模板 (参考源博客): https://www.cnblogs.com/fairylyl/p/10505833.html 右键属性配置: 新建一项属性,选择[ ...

  2. 【FastDFS】04 Docker搭建

    直接拉取镜像,创建容器并运行容器一把梭哈: docker run -d --restart=always \ --privileged=true \ --net=host \ --name=fastd ...

  3. 【Layui】16 表单元素 Form

    文档地址: https://www.layui.com/demo/form.html 表单元素: 1.输入框 2.密码框 3.下拉列表 4.单选框 5.复选框 6.文档域 7.富文本 8.开关 单行输 ...

  4. FFmpeg在游戏视频录制中的应用:画质与文件大小的综合比较

    我们游戏内的视频录制目前只支持avi固定码率,在玩家见面会上有玩家反馈希望改善录制画质,我最近在研究了有关视频画质的一些内容并做了一些统计. 录制视频大小对比 首先在游戏引擎中增加了对录制mp4格式的 ...

  5. 【转载】【技术杂谈】shell和terminal

    分享视频: [技术杂谈]shell和terminal

  6. 深度学习需要float64精度吗 —— 为什么各大深度学习框架均不支持float64的深度学习运算呢 —— 商用NVIDIA显卡的float64性能是否多余呢

    首先要知道这么几个事实,也是交代一下本文要讨论的问题的背景: 各大深度学习框架均支持float64类型的简单运算,但是均不支持float64的深度学习的运算操作: 作为深度学习运行的加速设备,各种GP ...

  7. ( Ubuntu环境下 )Vim插件推荐-Python自动补齐Vim插件jedi-vim的安装(使用插件管理器vundle进行安装)

    Ubuntu系统下,为 Vim 安装python自动补齐的插件   jedi-vim   . 1.   jedi-vim安装依赖 首先,jedi-vim插件需要当前Vim版本支持python,在终端输 ...

  8. Linux系统中目录和文件夹是一个东西吗

    目录和文件夹的区别   在阐述Linux系统的目录前,我们先来回忆一下现实中的目录和文件夹是什么:   在现实中,文件夹(封套)可以用来保存几个文件(或其他项目).而目录则可以用来维护项目的索引,这样 ...

  9. Jenkins配置分布式构建环境——添加固定Agent并使用JNLP启动Agent详解

    1.概述 在<Jenkins部署架构概述>这篇博文中对Jenkins部署架构进行了讲解.对于分布式架构,Jenkins包括固态Agent和动态Agent两种方案. 固定Agent(常用于虚 ...

  10. [粉丝问答16]应届生被放鸽子,怒怼HR!找工作和找对象哪个更残酷?

    很多应届生在求职过程中遇到过被放鸽子的情况,但是由于段位不高,资源不够,社会阅历尚浅,很多人都是忍气吐声,但是也不乏有些学生性格刚硬,怒怼的. 比如下面这位学生,竟然直接怼了HR. 0.应届硕士小伙怒 ...