C++ addon
node-addon-api
copy
https://github.com/nodejs/node-addon-api
https://www.cnblogs.com/ajanuw/p/14404060.html
hello world
copy#include <napi.h> using namespace Napi; String Method(const CallbackInfo& info) { Env env = info.Env(); return String::New(env, "world"); // 返回数据 } Object Init(Env env, Object exports) { // 导出 exports.Set(String::New(env, "hello"), Function::New(env, Method)); return exports; } NODE_API_MODULE(hello, Init)
copyconst addon = require("./build/Release/addon"); console.log( addon.hello() );
args
``
info.Length()
info[0].IsNumber()
double arg0 = info[0].AsNapi::Number().DoubleValue();
std::string str = info[0].ToString().Utf8Value();
copy// 和js一样模糊判断 Boolean b = opt.Get("b").ToBoolean(); if(b)... env.Null(); env.Undefined(); Napi::Boolean::New(env, true); callback Napi::Function cb = info[0].As<Napi::Function>(); cb.Call(env.Global(), { Napi::String::New(env, "hello world") }); return function String MyFunction(const CallbackInfo& info) { Env env = info.Env(); return String::New(env, "hello world"); } Function CreateFunction(const CallbackInfo& info) { Env env = info.Env(); Function fn = Function::New(env, MyFunction, "funName"); return fn; } Wrap 获取传入的c++类 MyObject* obj1 = Napi::ObjectWrap<MyObject>::Unwrap( info[0].As<Napi::Object>() ); 接收arraybuffer #include <napi.h> using namespace Napi; Napi::Value a(const Napi::CallbackInfo& info) { Napi::ArrayBuffer buf = info[0].As<Napi::ArrayBuffer>(); uint32_t* n = (uint32_t*)buf.Data(); printf("%d\n", *n); // 10 return info.Env().Undefined(); } Napi::Object Init(Napi::Env env, Napi::Object exports) { exports["a"] = Napi::Function::New(env, a); return exports; } NODE_API_MODULE(hello, Init) const array = new Uint8Array([0x0A, 0, 0, 0]); addon.a(array.buffer) 返回array_buffer let r = addon.test() console.log(r); // ArrayBuffer { [Uint8Contents]: <0a 00 00 00>, byteLength: 4 } console.log( new Uint8Array(r)[0] ); // 10 Env env = info.Env(); DWORD a = 10; auto buf = ArrayBuffer::New(env, 4); memcpy_s((void *)buf.Data(), 4, &a, 4); return buf; 接收 TypedArray let r = addon.test(new Uint8Array([0x0A, 0])); console.log(r); // 1 Value test(const CallbackInfo &info) { auto env = info.Env(); auto ta = info[0].As<TypedArray>(); auto buf = ta.Get("buffer").As<ArrayBuffer>(); return Number::From(env, *(WORD*)buf.Data()); } 接收 async function nw.test(async function () { return 100; }); Napi::Value test(const Napi::CallbackInfo &info) { Napi::Env env = info.Env(); auto promise = info[0].As<Napi::Function>().Call({}).As<Napi::Promise>(); auto thenFunc = promise.Get("then").As<Napi::Function>(); auto v = thenFunc.Call(promise, {Napi::Function::New(env, [](const Napi::CallbackInfo &info) { printf("%d\n", info[0].ToNumber().Int32Value()); /* 100 */ })}); return env.Undefined(); } event Napi::Value a(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); Napi::Function emit = info[0].As<Napi::Function>(); emit.Call({ Napi::String::New(env, "start") }); emit.Call({ Napi::String::New(env, "data"), Napi::Number::New(env, 1) }); emit.Call({ Napi::String::New(env, "end") }); emit.Call({ Napi::String::New(env, "ok") }); } const EventEmitter = require("events").EventEmitter; const emitter = new EventEmitter(); emitter.on("start", () => { console.log("### START ..."); }); emitter.on("data", (evt) => { console.log(evt); }); emitter.on("end", () => { console.log("### END ###"); }); addon.a(emitter.emit.bind(emitter)); 保存callback函数不被垃圾回收 #include <napi.h> class NativeAddon : public Napi::ObjectWrap<NativeAddon> { public: static Napi::Object Init(Napi::Env env, Napi::Object exports) { Napi::Function func = DefineClass(env, "NativeAddon", { InstanceMethod("tryCallByStoredReference", &NativeAddon::TryCallByStoredReference), InstanceMethod("tryCallByStoredFunction", &NativeAddon::TryCallByStoredFunction) }); constructor = Napi::Persistent(func); constructor.SuppressDestruct(); exports.Set("NativeAddon", func); return exports; } NativeAddon(const Napi::CallbackInfo& info) : Napi::ObjectWrap<NativeAddon>(info) { jsFnRef = Napi::Persistent(info[0].As<Napi::Function>()); // use Persistent jsFn = info[1].As<Napi::Function>(); } private: static Napi::FunctionReference constructor; Napi::FunctionReference jsFnRef; Napi::Function jsFn; void TryCallByStoredReference(const Napi::CallbackInfo& info) { // Napi::Env env = info.Env(); jsFnRef.Call({}); } void TryCallByStoredFunction(const Napi::CallbackInfo& info) { // Napi::Env env = info.Env(); jsFn.Call({}); } }; Napi::FunctionReference NativeAddon::constructor; Napi::Object Init(Napi::Env env, Napi::Object exports) { NativeAddon::Init(env, exports); return exports; } NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init) const { NativeAddon } = require("./build/Release/addon"); const addon = new NativeAddon( function () { console.log("JSFnRef"); }, function JSFn() { console.log("JSFn"); } ); addon.tryCallByStoredReference(); // ok addon.tryCallByStoredFunction(); // err 继承 EventEmitter #include <napi.h> class NativeEmitter : public Napi::ObjectWrap<NativeEmitter> { public: static Napi::Object Init(Napi::Env env, Napi::Object exports) { Napi::Function func = DefineClass(env, "NativeEmitter", { InstanceMethod("callAndEmit", &NativeEmitter::CallAndEmit) }); constructor = Napi::Persistent(func); constructor.SuppressDestruct(); exports.Set("NativeEmitter", func); return exports; } NativeEmitter(const Napi::CallbackInfo& info) : Napi::ObjectWrap<NativeEmitter>(info) {} private: static Napi::FunctionReference constructor; Napi::Value CallAndEmit(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); Napi::Function emit = info.This().As<Napi::Object>().Get("emit").As<Napi::Function>(); emit.Call(info.This(), { Napi::String::New(env, "start") }); for (int i = 0; i < 3; i++) { std::this_thread::sleep_for(std::chrono::seconds(1)); emit.Call( info.This(), { Napi::String::New(env, "data"), Napi::String::New(env, "data ...") }); } emit.Call(info.This(), { Napi::String::New(env, "end") }); return Napi::String::New(env, "OK"); } }; Napi::FunctionReference NativeEmitter::constructor; Napi::Object Init(Napi::Env env, Napi::Object exports) { NativeEmitter::Init(env, exports); return exports; } NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init) const { NativeEmitter } = require("./build/Release/addon"); const EventEmitter = require("events").EventEmitter; const inherits = require("util").inherits; inherits(NativeEmitter, EventEmitter); const emitter = new NativeEmitter(); emitter.on("start", () => { console.log("### START ..."); }); emitter.on("data", (evt) => { console.log(evt); }); emitter.on("end", () => { console.log("### END ###"); }); emitter.callAndEmit(); c++ 返回 promise Value a(const CallbackInfo& info) { Env env = info.Env(); auto re = Promise::Deferred::New(env); re.Resolve(String::New(env, "str")); return re.Promise(); } addon.a().then((e) => { console.log(e); // str }); 主线程同步回调 struct TsfnContext { TsfnContext(Napi::Env env, const Napi::CallbackInfo &info) : env_(env), cb(Persistent(info[0].As<Function>())){}; FunctionReference cb; Napi::Env env_; }; uintptr_t WINAPI threadEntry(void *hwnd, TsfnContext *context) { return context->cb.Call({Number::New(context->env_, (uintptr_t)hwnd)}).ToNumber().Int64Value(); } Napi::Value CreateTSFN(const Napi::CallbackInfo &info) { Napi::Env env = info.Env(); auto testData = new TsfnContext(env, info); BOOL r = EnumWindows((WNDENUMPROC)threadEntry, (LPARAM)testData); return Number::New(env, r); } let r = addon.createTSFN((data) => { return false; }); 对象包装 类属性和描述符 在子线程中调用jscallback addon.test(() => { console.log("ok"); // ok 10 }); struct TsfnContext { TsfnContext(const CallbackInfo &info, Napi::Env env){}; Napi::ThreadSafeFunction tsfn; HANDLE hThread; }; void mythread(TsfnContext*context) { auto callback = [](Napi::Env env, Napi::Function jsCallback, void *data) { jsCallback.Call({Number::New(env, (int)data)}); }; napi_status status = context->tsfn.BlockingCall((void *)10, callback); if (status != napi_ok) Napi::Error::Fatal("ThreadEntry", "err."); context->tsfn.Release(); } void FinalizerCallback(Napi::Env env, void *finalizeData, TsfnContext*context) { CloseHandle(context->hThread); delete context; } void test(const CallbackInfo &info) { Napi::Env env = info.Env(); auto testData = new TsfnContext(info, env); testData->tsfn = Napi::ThreadSafeFunction::New( env, // Environment info[0].As<Napi::Function>(), // JS function from caller "name", // Resource name 0, // Max queue size (0 = unlimited). 1, // Initial thread count testData, // Context, FinalizerCallback, // Finalizer (void *)nullptr // Finalizer data ); testData->hThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)mythread, testData, 0, 0); } 获取global对象 napi_value g; napi_get_global(env, &g); auto global = Object(env, g); auto str = global.Get("name").ToString().Utf8Value(); printf("str: %s\n", str.c_str()); // str: ajanuw name = "ajanuw"; addon.test(); 运行js字符串 globalThis.num = 1; console.log(nw.test()); console.log(globalThis.num); // 2 Napi::String jsStr = Napi::String::New(env, "num++"); napi_value result; napi_run_script(env, jsStr, &result); return Napi::Value::From(env, result); 可以这样运行函数 globalThis.xx = () => { return 233; } console.log(nw.test()); // 233 Napi::String jsStr = Napi::String::New(env, "xx()"); 使用eval globalThis.num = 1; console.log(nw.test()); console.log(globalThis.num); // 2 auto eval = env.Global().Get("eval").As<Napi::Function>(); return eval.Call(env.Global(), {Napi::String::New(env, "num++")});
标签:
C++
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步