The jonki

呼ばれて飛び出てじょじょじょじょーんき

node-ffiを使ってdllからNode.jsの関数を呼んでもらう

node-ffiというNode.jsの世界からダイナミックライブラリの関数を呼ぶ神アドオンがあるのですが、dll側からNode.jsの関数をコールバックとして呼べるか調べてみたのでサンプルを載せます。

環境
準備

Pythonなどを入れていたのはnode-gypでnode-ffiをビルドするためです。

$ npm install ffi
node-ffiの使い方

ここに書いてあります。ポインター、構造体、そして非同期による呼び出しもできるので、かなり便利なライブラリであることがわかります。作者はnode-gypを作った有名人でもあります。

ここには書いてありませんでしたが、dll側からNode.jsの関数を呼んでコールバックとして使う方法を探したところ、やはりありました。ffi.Functionを使えば関数のアドレスをdllのネイティブの世界に渡せるようです。

サンプル
  • dll側のコード
extern "C" {
  typedef void(*NodeCallback)(int);

  _declspec(dllexport) int twice(int a) {
    return a * 2;
  }

  _declspec(dllexport) int doSomething(NodeCallback callback, int x) {
    // do something
    x *= 2;

    callback(x);

    return 0;
  }
}
  • Node.js側のコード
var ffi = require('ffi');

var funcPtr = ffi.Function('void', ['int']);

var mylib = ffi.Library('MyDll', {
  'twice': ['int', ['int']],
  'doSomething': ['int', [funcPtr, 'int']]
});

var onResult = function(resultVal) {
  console.log('Result is', resultVal);
};

console.log('twice 10 =', mylib.twice(10));
mylib.doSomething(onResult, 10);
実行結果

1つ目のtwiceは与えた引数を2倍にして返す関数、2つ目は与えた値を2倍にしてNode.js側から渡された関数を通してCallbackする例です。めっちゃ便利ですね。

$ node client.js
twice 10 = 20
Result is 20
エラー対応

Node.jsが64bitなのにVisual StudioWin32のままdllを作ってしまった場合のエラー。DLLのパスが間違えている際もこのエラーが出ると思うので、ハマりどころかもしれません。Win32のまま使いたければNode.jsも32bit版をインストール必要がおそらくあります。

C:\XXX\Visual Studio 2010\Projects\MyDll\Debug\node_modules\ff
i\lib\dynamic_library.js:74
    throw new Error('Dynamic Linking Error: ' + err)
          ^
Error: Dynamic Linking Error: "MyDll.dll":
    at new DynamicLibrary (C:\XXX\Visual Studio 2010\Projects\
MyDll\Debug\node_modules\ffi\lib\dynamic_library.js:74:11)
    at Object.Library (C:\XXX\Visual Studio 2010\Projects\MyDl
l\Debug\node_modules\ffi\lib\library.js:45:12)
    at Object.<anonymous> (C:\XXX\Visual Studio 2010\Projects\
MyDll\Debug\client.js:5:17)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Function.Module.runMain (module.js:497:10)
    at startup (node.js:119:16)