NodejsがDllモジュールを呼び出す方法
11248 ワード
同社のプロジェクトはElectronjs.org/)を用いてpcアプリケーションを開発し、下位ハードウェアデバイスとの通信に関与するが、sdkパッケージは基本的にC++ダイナミックリンクライブラリdllによって実現される.
選択肢は2つあります.
以上の2つの方案はすべてdll呼び出しの問題を解決することができて、方案の選択型は個人のC++に対する掌握の程度を要して、C++の開発を熟知すれば、直接方案の2を選ぶことができて最も便利です.C++を全く知らない場合は、シナリオ1しか採用できません.
ペンの持ち主はC++がよく分からないので、最終的に最初の案を選びます.
二、node-ffiとは何ですか.
(www.npmjs.com/package/ffi…
Node-ffiは、純粋なJavaScriptを使用してダイナミックライブラリをロードおよび呼び出すnode addonであり、C++コードを書かずにダイナミックリンクライブラリのAPIインタフェースを呼び出すために使用できます.
ffiはいったい何をしたのか.本質的にはコンパイルされたNode addonであり、node_modules/ffi/build/Release/ffi_bindings.node、ffi_bindings.nodeはaddon ffiがnodejsとdllの橋渡しとして機能している.
次に、dllを簡単にロードするdemoの例を示します.
var ffi = require('ffi');
var libpath = path.join(_dirname, '/test.dll');
var testLib = ffi.Library(libpath, {
'start': ['bool', ['bool']]
});
testLib.start(true); // true
三、node-ffiのインストール
npm install ffi
次の図に示すように、node addonをコンパイルする環境がローカルにインストールされていない場合はエラーが発生します.
ffiを使用するにしても、node addonを直接書くにしても、node addonをコンパイルするステップが欠けていません.node addonをコンパイルするには、2つの方法があります.
1、node-gyp(www.npmjs.com/package/nod…).
npm install node-gyp
具体的なインストールの参考:github.com/nodejs/node...
まとめると以下の4点が必要です.
electronがアプリケーションを開発する場合、electronも同様にnodeオリジナルモジュールをサポートしますが、公式のnodeとは異なるV 8エンジンを使用しているため、オリジナルモジュールをコンパイルしたい場合は、electronのheadersの位置を手動で設定する必要があります.
Electron-rebuildは、複数のバージョンのnodeおよびelectronに対して、プリコンパイルバイナリプロトタイプモジュールを簡単にパブリッシュする方法を提供します.electronモジュールを再構築し、現在のelectronバージョンを識別し、headersのダウンロード、プロトタイプモジュールのコンパイルなどのステップを自動的に完了します.次のelectron-rebuildをロードして再コンパイルする例:
npm install --save-dev electron-rebuild
# "npm install" ,
./node_modules/.bin/electron-rebuild
# windows , :
.
ode_modules\.bin\electron-rebuild.cmd
詳細はelectronjs.org/docs/tutoriを参照してください...
ここでnodejsバージョンの問題に注意してください.nodejsプラットフォームはdllと一致しなければなりません.同じ32ビットまたは64ビットです.一致しないと、dllの呼び出しに失敗します.
ffiモジュールのインストールに成功すると、次のffi呼び出しdllのインスタンスアプリケーションを開始できます.
四、応用例
開発ニーズでは,C++に基づいて作成されたTCPデータ転送サービスのSDKを呼び出す必要がある.
まず、dllヘッダファイルインタフェース宣言のコードを見てみましょう.
#ifndef JS_CONNECTION_SDK
#define JS_CONNECTION_SDK
#ifdef JS_SDK
#define C_EXPORT __declspec(dllexport)
#else
#define C_EXPORT __declspec(dllimport)
#endif
extern "C"
{
typedef void(*ReceiveCallback) (int cmd, int seq, const char *data);
/* */
C_EXPORT void _cdecl SetReceiveCallback(ReceiveCallback callback);
/*
* option
*/
C_EXPORT void _cdecl SetOption(
const char* appKey,
const char* tk,
int lc,
int rm
);
/*
*
*/
C_EXPORT bool _cdecl CreateConnection();
/* */
C_EXPORT bool _cdecl SendData(int cmd, int seq, const char *data, unsigned int len);
/* */
C_EXPORT void _cdecl ReleaseConnection();
}
#endif
ffiはdllモジュールパッケージを呼び出し、コードは以下の通りです.
try {
const ffi = require('ffi');
const path = require('path');
const Buffer = require('buffer').Buffer;
const libpath = path.join(APP_PATH, '..', '..', '/testSDK.dll');
const sdkLib = ffi.Library(libpath, {
'CreateConnection': ['bool', []],
'SendData': ['bool', ['int', 'int', 'string', 'int']],
'ReleaseConnection': ['void', []],
'SetOption': ['void', ['string', 'string', 'int', 'int']],
'SetReceiveCallback': ['void', ['pointer']]
});
module.exports = {
createConnection: function(){
sdkLib.CreateConnection();
},
setReceiveCallback(cb) {
global.setReceiveCallback = ffi.Callback('void', ['int', 'int', 'string'], function(cmd, seq, data){
cb && cb(cmd, seq, data && JSON.parse(data));
});
sdkLib.SetReceiveCallback(global.setReceiveCallback);
},
sendData: function(cmd, seq, data){
data = JSON.stringify(data);
sdkLib.SendData(cmd, seq, data, data.replace(/[^\x00-\xff]/g, '000').length, 0);
},
releaseConnection: function(){
sdkLib.ReleaseConnection();
},
setOption: function (option) {
sdkLib.SetOption(
option.appKey,
option.tk,
option.lc,
option.rm
);
}
}
} catch (error) {
log.info(error);
}
ステップ1:ffiでdllインタフェースを登録する
const sdkLib = ffi.Library(libpath, {
'CreateConnection': ['bool', []],
'SendData': ['bool', ['int', 'int', 'string', 'int']],
'ReleaseConnection': ['void', []],
'SetOption': ['void', ['string', 'string', 'int', 'int']],
'SetReceiveCallback': ['void', ['pointer']]
});
ffi.Libraryメソッドでは、最初のパラメータがdllパスに入力され、第2のパラメータJSONオブジェクトが関連インタフェースを構成します.
keyはdllヘッダファイルから出力されるインタフェース、例えばC_EXPORT bool_cdecl CreateConnection()に対応する.
value array構成パラメータタイプ、array[0]登録インタフェース関数戻り値タイプ、array[1]登録インタフェース関数転送パラメータタイプ.
1、基礎パラメータタイプbool,char,short,int,longなど.
2、ポインタタイプ、refモジュールを導入する必要があります.以下の通りです.
var ref = require('ref');
var intPointer = ref.refType('char');
var doublePointer = ref.refType('short');
var charPointer = ref.refType('int');
var stringPointer = ref.refType('long');
var boolPointer = ref.refType('bool');
3、コールバック関数ポインタpointer、ffi.Callbackで作成できます.以下の通りです.
global.setReceiveCallback = ffi.Callback('void', ['int', 'int', 'string'], function(cmd, seq, data){
cb && cb(cmd, seq, data && JSON.parse(data));
});
sdkLib.SetReceiveCallback(global.setReceiveCallback);
コールバック関数パラメータタイプ構成はdllインタフェースパラメータタイプ構成と同じであり、ここでは多くは言わない.
ここで注意したいのは、コールバック関数がJavaScriptゴミ自動回収メカニズムによって回収される可能性があるので、ここではコールバック関数をグローバルオブジェクトglobalにマウントします.
ステップ2:インタフェース呼び出し
ffi.Library(libpath,{...})でインタフェースを登録すると、返されたsdkLibオブジェクトを介してドッキングしたインタフェースを直接呼び出すことができます.たとえば、次のようになります.
var bool = sdkLib.CreateConnection();
console.log(bool); // true or false;
var cmd = 0, seq = 0, data = {...};
var dataStr = JSON.stringify(data);
// JavaScript C++ *3
sdkLib.SendData(cmd, seq, data, data.replace(/[^\x00-\xff]/g, '000').length);
global.setReceiveCallback = ffi.Callback('void', ['int', 'int', 'string'], function(cmd, seq, data){
cb(cmd, seq, data && JSON.parse(data));
});
sdkLib.SetReceiveCallback(global.setReceiveCallback);
文章はこれで終わります.この文章を書く目標は主に自分がnodeを通じてdllを呼び出して無から有までの過程とピットの記録を記録することです.文章に間違いがあるところは、皆さんのご指摘を歓迎します.