参考: http://nodejs.cn/api/addons.html
NodeJs调用Dll中的类或者函数接口有一套基于V8的框架,有固定的写法:
注册入口函数,js在require的时候会调用该注册接口
Param1: 注册名称,无特殊要求
Param2: js第一次加载模块时调用的函数接口,一般用于注册给js调用的接口,或者初始化函数
NODE_MODULE(NODE_MODULE_NAME_YPP, Init);
初始化接口固定参数类型 v8::Local<v8::Object>
void Init(v8::Local<v8::Object> exports)
在初始化函数中注册给js调用的接口:
//Param1:exports
//Param2:给js调用的接口名
//Param3:对应的C++函数名
NODE_SET_METHOD(exports, "PRINT", JsPrint);
注册给js调用的类对象:
//生成新的函数模板,指定JS构造时对应的C++函数未JsInit
auto isolate = exports->GetIsolate();
v8::Local<v8::FunctionTemplate> tpl = v8::FunctionTemplate::New(isolate, JSInit);
//设置fieldcount为1
tpl->InstanceTemplate()->SetInternalFieldCount(1);
//为该函数对象挂接成员函数,可以是普通函数,也可以是类静态成员函数
NODE_SET_PROTOTYPE_METHOD(tpl, "Print",JsPrint);
//获取函数对象
auto JsConstruct = tpl->GetFunction(isolate->GetCurrentContext()).ToLocalChecked();
//将函数对象设置给Js调用
exports->Set(isolate->GetCurrentContext(), v8::String::NewFromUtf8(isolate, "Test"),
JsConstruct);
Ps:实际上就是模拟了一个类给js调用
具体js调用对应C++函数接口形式:
//Param1: const v8::FunctionCallbackInfo<v8::Value>&
static void JsPrint(const v8::FunctionCallbackInfo<v8::Value>& args)
//可以通过args下标获取js调用时传入的参数,并转化为C++数据类型
Int number = args[0]->Int32Value(args.GetIsolate()->GetCurrentContext()).FromJust();
符一个可以调用的例子:
#pragma once
#include <node.h>
#include <node_object_wrap.h>
#include <iostream>
#include <uv.h>
namespace nodeCppWrapper
{
struct Data
{
v8::Persistent<v8::Function> callback;
int count;
};
class Test : public node::ObjectWrap
{
public:
static void Init(v8::Local<v8::Object> exports)
{
auto isolate = exports->GetIsolate();
v8::Local<v8::FunctionTemplate> tpl = v8::FunctionTemplate::New(isolate, JSConstruct);
//tpl->SetClassName(v8::String::NewFromUtf8(isolate, "DNS"));
tpl->InstanceTemplate()->SetInternalFieldCount(1);
NODE_SET_PROTOTYPE_METHOD(tpl, "SetNumber", JsSetNumber);
NODE_SET_PROTOTYPE_METHOD(tpl, "Calc", JSCalc);
NODE_SET_METHOD(exports, "PRINT", JsSetNumber);
auto JsConstruct = tpl->GetFunction(isolate->GetCurrentContext()).ToLocalChecked();
exports->Set(isolate->GetCurrentContext(), v8::String::NewFromUtf8(isolate, "Test"),
JsConstruct);
}
private:
Test()
{
}
static void JSConstruct(const v8::FunctionCallbackInfo<v8::Value>& args)
{
auto isolate = args.GetIsolate();
if (args.IsConstructCall())
{
std::cout << "JSConstruct" << std::endl;
auto obj = new Test;
obj->Wrap(args.This());
args.GetReturnValue().Set(args.This());
}
else
{
std::cout << "error.this is contructor, please use new." << std::endl;
}
}
static void JsSetNumber(const v8::FunctionCallbackInfo<v8::Value>& args)
{
int n = args[0]->Int32Value(args.GetIsolate()->GetCurrentContext()).FromJust();
std::cout << "JsSetNumber: " << n << std::endl;
auto test = node::ObjectWrap::Unwrap<Test>(args.Holder());
test->number = n;
args.GetReturnValue().Set(v8::Number::New(args.GetIsolate(), 1));
}
static void calculate(uv_work_t* work) {
auto data = (Data*)work->data;
for (int i=0; i< data->count; ++i)
{
Sleep(1000);
}
}
static void complete(uv_work_t* work)
{
auto isolate = v8::Isolate::GetCurrent();
v8::HandleScope handleScope(isolate);
auto data = (Data*)work->data;
v8::Local<v8::Value> result[1] = { v8::String::NewFromUtf8(isolate,"calc finish.") };
v8::Local<v8::Function> cb = v8::Local<v8::Function>::New(isolate, data->callback);
cb->Call(isolate->GetCurrentContext(), Null(isolate),1, result);
data->callback.Reset();
delete data;
delete work;
}
static void JSCalc(const v8::FunctionCallbackInfo<v8::Value>& args)
{
auto test = node::ObjectWrap::Unwrap<Test>(args.Holder());
int nCount = test->number;
std::cout << "JSCalc, Wait..." <<nCount <<"s" << std::endl;
auto jsCallback = v8::Local<v8::Function>::Cast(args[0]);
Data* pData = new Data;
pData->callback.Reset(args.GetIsolate(), jsCallback);
pData->count = nCount;
uv_work_t* work = new uv_work_t;
work->data = pData;
uv_queue_work(uv_default_loop(), work, (uv_work_cb)calculate, (uv_after_work_cb)complete);
}
static v8::Persistent<v8::Function> constructor;
private:
int number = 0;
};
void Init(v8::Local<v8::Object> exports)
{
Test::Init(exports);
}
NODE_MODULE(NODE_MODULE_NAME_YPP, Init);
};
JS调用代码:
const addon = require('./bin/invoke');
const obj1 = new addon.Test;
obj1.SetNumber(9);
obj1.Calc((sum)=>{
console.log('total:' + sum);
});
异步调用JSCalc中用到了NodeJs模块中的yuv模块,将calc放在了额外的模块中执行。这个异步操作不可用其他thread代替,否则NodeJs不会等待回调,主线程逻辑完成后直接会退出进程。
附上完全demo:https://download.csdn.net/download/linfengmove/11778028