NodeJS和NW通过ffi调用dll/so动态库

字节银月师
• 阅读 17796

0x01. 使用的 npm 包

首先要安装 node-gyp, 用来重新编译依赖包。

npm instal -g node-gyp

然后主要用到下面三个包:

  • node-ffi -- 使用Javascript调用动态库

  • ref -- 用来定义数据类型,提供指针功能

  • ref-array -- 用Buffer来实现C语言中的 array 数据类型

npm install ffi   //这个命令会同时安装上 ref、ref-struct
npm instal ref-array

0x02. 测试NODEJS调用

要使用动态库中的函数,首先要对动态库里的函数进行声明。
比如在 Test.dll 库中,有两个函数如下:

void init(string name, int port);

string hello(int times);

js中进行声明的方法如下:

var ffi = require('ffi');
var Test = ffi.Library('Test.dll',{
    'init': ['void',['string','int']],
    'hello': ['string', ['int']]
});

#规则就是  
'函数名':['返回值数据类型':['参数数据类型',...,'参数数据类型']]

声明完成后,就可以进行调用了

Test.init('COM1', 9300);
Test.hello(5);

这里用简单的数据类型,来讲解调用动态库的大致流程。剩下比较复杂的地方在于如何模拟像 指针结构体数组 等比较复杂的数据类型。

0x03. 结构体、指针、数组的转化

1. 结构体

结构体需要用到'ref-struct'这个包。假设有以下结构体:

typedef struct {
    byte UID[16];       /*餐盘标签 UID,16 进制*/
    byte UType[6];      /*餐盘类型,10 进制*/
    int ProdNo;         /*菜品编码,10 进制*/
    int ProdPrice;      /*菜品价格,价格以分为单位,10 进制*/
} DishInfo;

int类型的好办,可以直接使用 ref包里含有的类型 ref.types.int
UIDUType是两个bype类型的数组,需要使用ref-array进行模拟。

var refStruct = require('ref-struct');
var refArray = require('ref-array');

var DishInfo = refStruct({
    'UID': refArray('byte', 16),
    'UType': refArray('byte', 6),
    'ProdNo': ref.types.int,
    'ProdPrice': ref.types.int
});

2. 指针和引用

假设动态库中有函数如下, 第二个参数为结构体指针, 第三个参数是一个int 引用。

int Read(int port, DishInfo * pInfo, int &Count);

在声明函数的时候,就需要指明指针和引用的数据类型。示例如下:

var ffi = require('ffi');
var ref = require('ref');
var refStruct = require('ref-struct');
var refArray = require('ref-array');

var DishInfo = refStruct({
    'UID': refArray('byte', 16),
    'UType': refArray('byte', 6),
    'ProdNo': ref.types.int,
    'ProdPrice': ref.types.int
});

//数据类型
var intPointer = ref.refType('int');
var DishInfoArrType = refArray(DishInfo);  //定义了DishInfo数组类型

var Test = ffi.Library('Test.dll',{
    'init': ['void',['string','int']],
    'hello': ['string', ['int']],
    'Read': ['int', ['int', DishInfoArrType, intPointer]]
});

//实例化
var count = ref.alloc('int');
var DishInfoArr = DishInfoArrType(3);

Test.Read(11, DishInfoArray, count);

//使用deref()获取引用的实际值
var actualCount = count.deref();

0x04. NW 适配

使用NodeJS直接调用没问题后,就可以使用 node-gyp 编译适配 NW 的包了, 这里只说明window环境下的使用方法。

1. 搭建编译环境

  1. 安装 Visual Studio 2015

    > ? [Windows Vista / 7 only] 需要安装 [.NET Framework 4.5.1](http://www.microsoft.com/en-us/download/details.aspx?id=40773)
    
  2. 安装 python 2.7 (不要装3.x.x,不支持),装完后运行

    npm config set python python2.7
  3. 设置visualstudio版本

    npm config set msvs_version 2015

2. 修改 win_delay_load.cc

打开 Github - nw.js repository, 然后切换自己使用的nw 版本分支。
NodeJS和NW通过ffi调用dll/so动态库

我这里选择的是 nw14, 然后找到 tools/win_delay_load_hook.cc, 下载替换掉 %APPDATA%\npm\node_modules\node-gyp\src\win_delay_load_hook.cc

3. node-gyp 重编译 ffi 和 ref

# --target 输入nw 版本号,这里实用的是 v0.14.3, arch为 ia32 或者 x64

cd node_modules/ffi
node-gyp configure --target=0.14.3 --arch=ia32
node-gyp build

cd node_modules/ref
node-gyp configure --target=0.14.3 --arch=ia32
node-gyp build

0x05. 参考资料

  1. 通过ffi在node.js中调用动态链接库(.so/.dll文件)

  2. Use Native Node Modules

  3. 厚颜无耻加上自己的博客 XD

点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
7个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
3年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Stella981 Stella981
3年前
SpringBoot整合Redis乱码原因及解决方案
问题描述:springboot使用springdataredis存储数据时乱码rediskey/value出现\\xAC\\xED\\x00\\x05t\\x00\\x05问题分析:查看RedisTemplate类!(https://oscimg.oschina.net/oscnet/0a85565fa
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Easter79 Easter79
3年前
SpringBoot整合Redis乱码原因及解决方案
问题描述:springboot使用springdataredis存储数据时乱码rediskey/value出现\\xAC\\xED\\x00\\x05t\\x00\\x05问题分析:查看RedisTemplate类!(https://oscimg.oschina.net/oscnet/0a85565fa
Wesley13 Wesley13
3年前
Java日期时间API系列36
  十二时辰,古代劳动人民把一昼夜划分成十二个时段,每一个时段叫一个时辰。二十四小时和十二时辰对照表:时辰时间24时制子时深夜11:00凌晨01:0023:0001:00丑时上午01:00上午03:0001:0003:00寅时上午03:00上午0
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
字节银月师
字节银月师
Lv1
伤见路旁杨柳春,一重折尽一重新。今年还折去年处,不送去年离别人。
文章
4
粉丝
0
获赞
0