node-addon-api

hello world

#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)
const addon = require("./build/Release/addon");
console.log( addon.hello() );

args

info.Length()

info[0].IsNumber()

double arg0 = info[0].As<Napi::Number>().DoubleValue();

std::string str = info[0].ToString().Utf8Value();

// 和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++")});
posted @ 2021-04-15 09:39  Ajanuw  阅读(1450)  评论(0编辑  收藏  举报