Nodejs调用dll/so文件的方法

Nodejs调用dll/so文件的方法

扫盲贴,高手飘过 本人之前发过一贴,关于nodejs引用dll的 http://cnodejs.org/topic/51c2ba5c73c638f3703e4185#534faa9c1969a7b22a3da918 贴文里的方法不是很优美,觉得写addon调用不完善,一直想计划写一个中间件模块出来; 考虑: 1、获得dll的所有接口函数,写成json格式文件api.json; 2、根据1得到的json格式文件api.json生成addon的c++代码addon.cc, 3、执行使用node-gyp和配置文件将addon.cc编译成addon.node【注意一定要使用vs2010,使用其他版本VS失败,不知原因】 4、调用addon.node文件里的接口函数即可;

后来才注意到有个ffi模块比较好,试了一下,不错;建议使用这个模块调用,还有更好的方法请坛友指出 例一:调用系统的user32.dll函数 MessageBoxW,有4个参数,输出一个整数 ,

var FFI = require('ffi');

function TEXT(text){ return new Buffer(text, ‘ucs2’).toString(‘binary’); }

var user32 = new FFI.Library(‘user32’, { ‘MessageBoxW’: [ ‘int32’, [ ‘int32’, ‘string’, ‘string’, ‘int32’ ] ] });

var OK_or_Cancel = user32.MessageBoxW( 0, TEXT(‘I am Node.JS!’), TEXT(‘Hello, World!’), 1 ); console.log(OK_or_Cancel);

例二、调用当前目录下的libTest.dll,里面有一个函数factorial,输入一个整数,输出一个整数

var FFI = require('ffi');
var func = new FFI.Library('libTest', {
   'factorial': 
   [
      'int32', [ 'int32']
   ]
});

var n = func.factorial(5);
console.log(n);

三、说明

可以注意到FFI.Library的第二个参数是json,里面列出了需要用到的函数名,输入参数格式,输出结果的格式;

'int32', [ 'int32', 'string', 'string', 'int32' ]

这里就表明,

(1)输出结果为整数; (2)后面的数组表示有4个参数,参数格式依次是整数,字符串,字符串,整数 ( 3 ) 字符串要转化一下才可以使用; 注意看例一; new Buffer(text, ‘ucs2’).toString(‘binary’); ffi模块同样能调用*inux 下的so文件


16 回复

Node.js 调用 DLL/so 文件的方法

扫盲贴,高手飘过

在之前的帖子中,我分享了如何通过 Node.js 引用 DLL 文件。但那个方法并不完美,我一直在寻找一种更优雅的方式来实现这一功能。经过一番探索,我发现了一个名为 ffi 的模块,它能够更方便地调用 DLL 或 so 文件中的函数。

使用 ffi 模块调用 DLL/so 文件

ffi 模块允许你在 Node.js 中直接调用动态链接库(DLL 或 so 文件)中的函数。下面是一些示例代码,帮助你理解如何使用 ffi 模块来调用这些函数。

示例一:调用 Windows 系统中的 user32.dll

假设我们要调用 user32.dll 中的 MessageBoxW 函数,该函数用于显示消息框。MessageBoxW 函数接受四个参数,并返回一个整数。

const ffi = require('ffi');

// 将文本转换为 UCS-2 编码的缓冲区
function TEXT(text) {
    return new Buffer(text, 'ucs2').toString('binary');
}

// 加载 user32.dll 并定义 MessageBoxW 函数
const user32 = new ffi.Library('user32', {
    'MessageBoxW': ['int32', ['int32', 'string', 'string', 'int32']]
});

// 调用 MessageBoxW 函数
const result = user32.MessageBoxW(
    0, // hWnd (父窗口句柄)
    TEXT('I am Node.JS!'), // lpText (消息内容)
    TEXT('Hello, World!'), // lpCaption (消息框标题)
    1 // uType (消息框类型)
);

console.log(`MessageBox 返回值: ${result}`);
示例二:调用自定义 DLL 文件中的函数

假设我们有一个自定义的 DLL 文件 libTest.dll,其中包含一个名为 factorial 的函数,该函数接受一个整数并返回其阶乘值。

const ffi = require('ffi');

// 加载 libTest.dll 并定义 factorial 函数
const libTest = new ffi.Library('./libTest', {
    'factorial': ['int32', ['int32']]
});

// 调用 factorial 函数
const result = libTest.factorial(5);

console.log(`5 的阶乘是: ${result}`);

说明

  1. 加载动态链接库:使用 ffi.Library 方法加载 DLL 或 so 文件,并指定需要调用的函数。
  2. 定义函数签名ffi.Library 的第二个参数是一个对象,描述了每个函数的签名(即输入参数和返回值类型)。
  3. 参数转换:某些函数可能需要特定的参数格式,例如字符串需要转换为特定编码的缓冲区(如 TEXT 函数所示)。

通过上述示例,你可以看到 ffi 模块提供了简单而强大的方式来调用 DLL 或 so 文件中的函数。如果你有更多的需求或遇到问题,欢迎继续交流!


顶楼主,学习了,理解中。 请问需要npm install ffi 吗?

npm install ffi 不了啊,做何解

肯定是要npm install ffi的。安装这个模块有点麻烦,需要先安装vs2010,其他版本可能不行

请分享一下swig,谢谢 @wenbob

请问如何调用c++的静态文件?就是c++的函数只有申明,没有实现,但有lib文件,在c++上测试时ok的,但为node写组件就出现编译错误:“无法解析的外部符号…”

楼主方便给个QQ交流一下么~

#include <node.h> #include <v8.h> #include <node_buffer.h> #include <windows.h> #include <iostream>

using std::cout; using std::endl; using namespace v8;

Handle<Value> strTransfer(const Arguments& args) { HandleScope scope; ::HINSTANCE hInst = NULL; //假设limysql.dll路劲是正确的 hInst = ::LoadLibrary(“D:\libmysql.dll”); if (!hInst) { cout << “abc” << endl; } else { cout << “efg” << endl; }

Local<String> str = String::New("hello");
return scope.Close(str);

}

void Init(Handle<Object> exports) { exports->Set(String::NewSymbol(“compress”), FunctionTemplate::New(strTransfer)->GetFunction()); }

NODE_MODULE(compress,Init);

//test.js var addon = require(’./build/Release/compress’); console.log(addon.compress());

经过测试,输出是: abc hello

说明LoadLibrary没有加载dll成功,但如果写一个c++程序,直接输出的话,输出的是:efg 求帮忙,为什么node调用c++写的包含dll文件的addon时,LoadLibrary函数失败?

node-gyp build 后 出现的情况 如果cpp文件不包含v8.h node.h 单单写个main函数入口就编译成功 vs2010 win764位环境1.jpg2.jpg

注意配置好环境变量,vc的目录需要调整,当时整理这个环境还折腾了不少时间的

一般我会写Golang来调用,再编译成可执行文件。 可能不是最好的方案,但是是坑比较少的方案。 Golang可以指定平台/位数,静态链接编译,大小一般1到2MB。

要调用DLL或SO文件中的函数,可以使用Node.js的ffi-napi库。该库允许你在Node.js中直接调用C/C++编写的动态链接库。下面将展示如何使用ffi-napi来调用DLL或SO文件中的函数。

示例代码

Windows DLL (.dll)

假设你有一个名为example.dll的DLL文件,其中包含一个名为add的函数,该函数接受两个整数并返回它们的和。

const ffi = require('ffi-napi');
const ref = require('ref-napi');

// 定义返回值类型和参数类型
const intType = ref.types.int;

const example = new ffi.Library('./example', {
    'add': [intType, [intType, intType]]
});

const result = example.add(10, 20);
console.log(`Result: ${result}`); // 输出:Result: 30

Linux SO (.so)

假设你有一个名为example.so的共享对象文件,其中包含一个名为add的函数,该函数接受两个整数并返回它们的和。

const ffi = require('ffi-napi');
const ref = require('ref-napi');

// 定义返回值类型和参数类型
const intType = ref.types.int;

const example = new ffi.Library('./example', {
    'add': [intType, [intType, intType]]
});

const result = example.add(10, 20);
console.log(`Result: ${result}`); // 输出:Result: 30

说明

  • ffi.Library函数用于加载指定路径下的动态链接库,并指定库中可用的函数及其参数和返回值类型。
  • ref库用于定义C语言的数据类型,如整型int等。
  • 调用时只需通过example.add(10, 20)的方式调用DLL或SO文件中的函数。

这种方法不需要额外编写C++代码或者编译生成Node.js的addon,非常方便快捷。

回到顶部