NodeJS <-> Dll: как обрабатывать обратный вызов, вызванный вызовом метода C

В настоящее время мы пытаемся интегрировать DLL с NodeJS. DLL - это шина сообщений, которая запускает обратный вызов (если он зарегистрирован) при получении нового сообщения на шине. Прикрепленная dll имитирует это поведение: цикл выполняется бесконечно, и в каждом цикле вызывается метод C с обратным вызовом. Вот мой тестовый пример:

var ref = require("ref");
var ffi = require('ffi');
var events = require('events');
var util = require('util');

var voidPtr = ref.types.void;

// Expose the DLL to node JS
var apiLib = ffi.Library('../bin/dispatch', {
    // C method declaring the callback
    'dispatchRegisterCallback': [ 'int', ['pointer', 'int'] ],
    // C method which simply call dispatchRegisterCallback
    'dispatchCallCallback': [ 'int', []]
});

// Callback
var myCallbackPtr = ffi.Callback('void', ['string'], function (message) {
    console.log('callback: ' + message);
});

// Registering the callback on the C method, starts a loop and
// call the method that triggers the callback from the Dll each 1 second
// DOES NOT WORK
apiLib.dispatchRegisterCallback(myCallbackPtr, 1000);

// Make Node Loop
setInterval(function() {
    console.log("loop");
    // Triggering the callback from NodeJS : OK
    //apiLib.dispatchCallCallback();
}
, 100);
// Keep the callback pointer to avoid GC
process.on('exit', function() {
    myCallbackPtr
})

А вот и исходный код примера dll

#include "stdafx.h"
#include "dispatch.h"
#include <iostream>

pfnCallback * g_pfnCallback = NULL;

void CALLBACK timerFn(HWND hwnd, UINT uMsg, UINT timerId, DWORD dwTime)
{
       std::cout << "dispatch.dll, timerFn" << std::endl;
       if (g_pfnCallback != NULL)
       {
             std::cout << "dispatch.dll, timerFn : calling callback " << g_pfnCallback << std::endl;
             g_pfnCallback("callback call (timer)");
       }
}

/**
* Register a callback.
*/
int __stdcall dispatchRegisterCallback(pfnCallback fn, int interval)
{
       std::cout << "dispatch.dll, dispatchRegisterCallback" << std::endl;
       g_pfnCallback = fn;
       SetTimer(NULL, 0, interval, (TIMERPROC)timerFn);

       return true;
}

int __stdcall dispatchCallCallback()
{
       std::cout << "dispatch.dll, dispatchCallCallback" << std::endl;
       if (g_pfnCallback != NULL)
       {
             std::cout << "dispatch.dll, dispatchCallCallback : calling callback " << g_pfnCallback << std::endl;
             g_pfnCallback("callback call by dispatchCallCallback");
             return true;
       }
       return false;
}

Это действительно возможно с модулем FFI? Должен ли я использовать вместо этого модуль Edge или, возможно, дополнения?

1 ответ

SetTimer - это функция Windows.

Чтобы это работало, приложение должно иметь цикл сообщений Windows:

вам нужно отправлять сообщения в вызывающей ветке, даже если вы используете TimerProc вместо обработки WM_TIMER

Но node.js использует IOCP в Windows. Он не отправляет сообщения.

Один из способов справиться с этим - использовать uv_timer в вашем коде C++, но для этого dll нужно будет связать с node.exe (он экспортирует функции libuv, как это делают совместно используемые библиотеки) с помощью node-gyp.

Обратите внимание, что это решение является кроссплатформенным.

Но другой метод, который вы уже реализовали, используя setInterval это проще, а также кроссплатформенный.

setInterval использует ув_тимер под капотом.

Другое решение состоит в том, чтобы создать другой поток в вашей собственной библиотеке и вызвать функцию обратного вызова оттуда. Если вы планируете использовать SetTimer, он должен быть вызван в созданном потоке, и вам понадобится цикл сообщений Windows.

Другие вопросы по тегам