NodeJS調用Dll模塊

參考: 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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章