🪑

Node.jsでnode-ffiを利用してDLL側からコールバックする方法

2022/03/18に公開

目的

Node.jsでnode-ffiを利用してC++で作ったDLL側からコールバックしてNode.js側のfunctionを実行する。

node-ffiとは

Node.jsからC++などで作成したダイナミックライブラリの関数を実行することができる。

基本的な利用方法は以下を参照

https://github.com/node-ffi/node-ffi

Node側の実装

var ffi = require("ffi-napi");

var function_ptr_1 = ffi.Function("void", ["int"]);
var library_file = "test.dll";

// DLL側を呼び出すfunctionの定義
var dll = ffi.Library(library_file, {
    testCallback: ["string", ["string", function_ptr_1]],
});

// DLL側のtestCallbackの実行
var ret = dll.testCallback("TEST", function(callbackVal) {
  // callbackValはcallback_function_in_nodeの引数
    console.log("callbackVal=", callbackVal);
});
	
// retはtestCallback自体の戻り値
console.log(ret);

C++のDLL側の実装

test.h
#pragma once

#define EXPORT __declspec(dllexport)
extern "C" {
    typedef void(*callback_function_in_node)(int);
    EXPORT const char* testCallback(const char* params, callback_function_in_node callback);
}
test.cpp
#include "pch.h"
#include "test.h"

EXPORT const char* testCallback(const char* params, callback_function_in_node callback)
{
    // コールバックの実施
    // 引数がNodeのcallbackValとしてコールバックされる。
    callback(100);
    callback(101);
    callback(102);
    
    // functionとしての結果の返却
    static std::string ret = "OK";
    return ret.c_str();
}

まとめ

これによりイベントのコールバックをDLL側から実施することができる。
デバイスの監視してDLL側から状態変化をNode.js側にイベントとして通知するような用途での利用も可能。

注意点

スレッドの考え方がNodeとDLL側では異なるので、Node.js側のコールバック内ではあまり重い処理は行わないほうがよさそうである。
長い処理をする場合は無名のasyncで非同期化しまったほうがよいかも?

var ret = Lib.testCallback("TEST", function(callbackVal) {
    function(callbackVal) {
        (async() => {
            // 長めの処理
        })();
    });

注意点II

コールバックファンクションの生存期間に注意する必要がある。
DLL側で関数ポインタを保持し任意のタイミングでコールバックする場合は、次のようにスコープ外で消失しないように作成したffi.Callbackを保持する。

var callback_a = ffi.Callback("void", [], function() {
    // 処理
});

// この場合はライブラリ関数定義の引数に'pointer'を指定する。
var dll = ffi.Library(library_file, {
    testCallback: ["string", ["string", "pointer"]],
});

// calback_aを引数でわたす。
testCallback("test",callback_a);

Discussion