鍍金池/ 問答/C++  HTML/ 關(guān)于Node和C++互相調(diào)用的問題

關(guān)于Node和C++互相調(diào)用的問題

最近看了很多的相關(guān)文檔;但是有有一個(gè)地方一直很不解,關(guān)于Node調(diào)用C++傳遞回調(diào)函數(shù):
官方文檔如下:

C++部分:

// addon.cc
#include <node.h>

namespace demo {

using v8::Function;
using v8::FunctionCallbackInfo;
using v8::Isolate;
using v8::Local;
using v8::Null;
using v8::Object;
using v8::String;
using v8::Value;

void RunCallback(const FunctionCallbackInfo<Value>& args) {
  Isolate* isolate = args.GetIsolate();
  Local<Function> cb = Local<Function>::Cast(args[0]);
  const unsigned argc = 1;
  Local<Value> argv[argc] = { String::NewFromUtf8(isolate, "hello world") };
  cb->Call(Null(isolate), argc, argv);
}

void Init(Local<Object> exports, Local<Object> module) {
  NODE_SET_METHOD(module, "exports", RunCallback);
}

NODE_MODULE(NODE_GYP_MODULE_NAME, Init)

}  // namespace demo

js部分

// test.js
const addon = require('./build/Release/addon');

addon((msg) => {
  console.log(msg);
// Prints: 'hello world'
});

可以看得出來(lái),這個(gè)demo中js雖然傳遞的是function,但是在C++模塊是同步的執(zhí)行的;而且同步執(zhí)行的話我也調(diào)通了,但是實(shí)際的場(chǎng)景肯定不是這種需求,而是在C++模塊某一個(gè)異步的事件監(jiān)聽中收到某一種消息之后才通過js模塊傳遞的回調(diào)函數(shù)回調(diào)給js;

目前已經(jīng)做好的事情:
封裝一個(gè)C++的類,并且export,可用js調(diào)用此類的實(shí)例方法;

.h文件的兩個(gè)屬性:

Isolate *onPlayNoteIsolate;
Local <Function> onPlayNote;

.cpp文件

// 標(biāo)記為方法1:
void MIDIDeviceHelperBridge::setOnPlayNote(const FunctionCallbackInfo<Value>& args) {
    MIDIDeviceHelperBridge *obj = ObjectWrap::Unwrap<MIDIDeviceHelperBridge>(args.Holder());
    
    Local<Function> callback = Local<Function>::Cast(args[1]);
    obj->onPlayNote = callback;
    obj->onPlayNoteIsolate = args.GetIsolate();
}

// 標(biāo)記為方法2:
void MIDIDeviceHelperBridge::ReceiveMsg(DWORD Msg, DWORD TimeStamp) {
    // 此方法中的this為方法1中的obj
}

我現(xiàn)在的需求是在方法1中獲得的js模塊的回調(diào)函數(shù)存儲(chǔ)在obj中或者任何在C++模塊能訪問到的地方,然后在方法2中收到消息之后通過方法1獲得的js模塊的回調(diào),將消息傳遞給js模塊;

我嘗試過在方法1中的obj里存儲(chǔ)callback和isolate,然后在方法2中通過this訪問callback和isolate,但是收到消息后程序崩潰了,發(fā)現(xiàn)isolate沒有問題,是callback被釋放了,后來(lái)也嘗試過Local <Function> onPlayNote;
類型換成Persistent <Function> onPlayNote;和Handle<Function> onPlayNote;也都是收到消息后崩潰;

現(xiàn)求問有沒有大神做過類似的功能?

========================================================================
經(jīng)過幾個(gè)小時(shí)后修改部分問題:

1:Persistent <Function> onPlayNote;不存儲(chǔ)在類的成員變量中,放到全局,而且必須使用Persistent;
2:發(fā)現(xiàn)的其他問題,ReceiveMsg方法我可以在當(dāng)前類對(duì)js公開的方法中調(diào)用,并且在js中調(diào)用詞方法后,js模塊的回調(diào)也是可以執(zhí)行的;
3:但實(shí)際中ReceiveMsg不是我手動(dòng)調(diào)用的,而是MIDI設(shè)備(例如能通過USB連接電腦的電子琴)上的按鍵觸發(fā)后通過windows系統(tǒng)的midiapi調(diào)用ReceiveMsg方法,此時(shí)ReceiveMsg中沒有任何js的環(huán)境;

========================================================================

回答
編輯回答
莫小染

也是遇到這樣的問題,現(xiàn)在直接放棄c++了

2017年4月13日 22:15
編輯回答
舊城人

已解決
收到的消息存儲(chǔ)在當(dāng)前對(duì)象的消息隊(duì)列,然后通過emit發(fā)送到j(luò)s模塊:

C++部分

// 在類的New方法中,用類的靜態(tài)方法實(shí)例化runloop中的異步函數(shù)
void MIDIDeviceHelperBridge::New(const FunctionCallbackInfo<Value>& args) {
    MIDIDeviceHelperBridge *obj = new MIDIDeviceHelperBridge();
    obj->isolate = isolate;
    obj->message_async.data = obj;
    uv_async_init(uv_default_loop(), &obj->message_async, MIDIDeviceHelperBridge::MIDIMessageCallback);
    ……
}

void MIDIDeviceHelperBridge::ReceiveMsg(DWORD Msg, DWORD TimeStamp) {
    uv_async_send(&this->message_async);
}

void MIDIDeviceHelperBridge::MIDIMessageCallback(uv_async_t *async) {
    MIDIDeviceHelperBridge *obj = static_cast<MIDIDeviceHelperBridge*>(async->data);
    Local<v8::Function> emitFunction = obj->handle()->Get(String::NewFromUtf8(obj->isolate, "emit")).As<v8::Function>();
    const unsigned argc = 4;
    char *CustomMessageName = "CustomMessageName";
    Local <Value> argv[argc] = {
        String::NewFromUtf8(obj->isolate, CustomMessageName),
        Number::New(obj->isolate, 1),
        Number::New(obj->isolate, 2),
        Number::New(obj->isolate, 3)
    };
    MakeCallback(obj->isolate, obj->handle(), emitFunction, argc, argv);
}

js部分:

const midiDeviceHelper = require(`xxx.node`);//路徑自己寫上就行
const EventEmitter = require('events').EventEmitter;
midiDeviceHelper.MIDIDeviceHelperBridge.prototype.__proto__ = EventEmitter.prototype;
const midiDeviceHelperBridge = new midiDeviceHelper.MIDIDeviceHelperBridge();
const CustomMessageName = "CustomMessageName"; //這個(gè)是上面argv里的第一個(gè)元素
midiDeviceHelperBridge.on(CustomMessageName, (a, b, c) => {
});
2017年12月29日 02:16
編輯回答
莫小染

請(qǐng)問MIDIDeviceHelperBridge類的handle()方法怎么實(shí)現(xiàn)的?
跪求完整的例子?@kingphone_he

2018年5月3日 06:41