Node.js使用ffi-napi,ref-napi,ref-array-napi,ref-struct-napi调用动态库
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Node.js使用ffi-napi,ref-napi,ref-array-napi,ref-struct-napi调用动态库相关的知识,希望对你有一定的参考价值。
参考技术A使用electron开进行桌面程序的开发,似乎成了WEB前端开发人员转桌面程序开发的首选。近期有一些使用在electron中使用加密锁的需求,学习了一下在Node.js中通过ffi-napi模块调用动态链接库,把几款加密锁产品的动态库使用javascript封装了一下,实现了electron中使用加密锁功能。
开发过程中遇到了一些问题,踩了一些坑,这里总结记录一下。这里使用接口函数参数类型比较复杂的ROCKEY-ARM的动态链接库来进行开发。
NOTE: javascript封装的ROCKEY-ARM接口模块源码,我已经分享出来,如果只是需要electron或者Node.js工程中使用ROCKEY-ARM的网友,可以直接使用。
首先需要在node.js项目中安装调用动态链接库时需要依赖的模块 ffi-napi,ref-napi,ref-array-napi,ref-struct-napi 。
下面大概介绍一下这几个模块的用途:
向 飞天诚信 购买ROCKEY-ARM加密锁产品,可以获得ROCKEY-ARM的SDK,可以获得Windows和Linux的动态链接库,文件名一般为Dongle_d.和libRockeyARM.so.0.3。
ffi-napi支持Windows,Linux系统,所以.dll和.so都可以支持,在不同的操作系统下去加载不同的动态库文件就可以了。加载动态库的方法如下:
Library()第一个参数是.dll的路径,Linux系统是.so的路径。第二个参数rockeyInterface是动态库导出函数的声明,ROCKEY-ARM的导出函数比较多,我单独拿出来定义。具体下面会讲到。
首先从ROCKEY-ARM中找几个参数简单的函数来声明一下。
首先看一下上面几个接口用到的数据类型有:DONGLE_HANDLE,DWORD,DONGLE_HANDLE ,int,BYTE 这几种。
再看下ffi-napi支持的ref-napi支持的数据类型有以下类型:
参数这里应该用长度一致的数据类型,可以有以下匹配。
声明的写法如下:
一个json,key是动态库导出函数名,比如\'Dongle_Open\',value是个列表,第一个元素是返回值,第二个元素是参数。其中参数还是个列表。这个ref-napi中有适合类型的,直接写称具体类型即可,比如返回值DWORD和传入的长度int,我这里都用\'int\'。其他的参数我额外定义了句柄ryHandle、句柄的指针ptrHandle、字节的指针ptrByte。其中ryHandle,ptrryHandle,ptrByte的定义如下:
DONGLE_HANDLE本质是void *类型, void* 类型最开始的时候妄图定义一个void的数组,然后用void数组来表示void ,然后发现报断言错误,数组不支持void类型。所以就直接用无符号数来表示void指针,在64位系统是8字节,32位系统是4字节,使用uint类型就可以了。DONGLE_HANDLE 。
在ROCKEY-ARM的函数中也有很多带参数的接口,比如:
拿以上两个函数接口举例,Dongle_Enum中的第一个参数是一个指向DONGLE_INFO结构体的指针,运行后返回设备信息的列表,使用ROCKEY-ARM的时候需要通过枚举函数获得设备信息列表,然后比较产品ID或者硬件ID决定打开哪一个设备。为了方便从枚举函数返回的设备信息中方便的解析出产品ID或者硬件ID等信息,需要把DONGLE_INFO* pDongleInfo这个参数声明成一个结构体数组。Dongle_RsaGenPubPriKey()函数中有RSA_PUBLIC_KEY ,RSA_PRIVATE_KEIY 两个结构体指针参数,因为在这里一般用户并不需要解析RSA密钥中的n,d,e等分量,可以直接做作为一个字节数组,直接声明成上面的ptrByte类型即可。所以在声明如下:
调用ffi-napi声明的函数,主要是给自己定义的数据类型赋初值以及获得自定义参数的返回值。下面分别说明。
这里的int*,是让函数返回设备的数量,或者传入输入数据的长度或者传出输出数据的长度,所以只要定义一个长度为1的int数组即可,如下:
给传入的数据赋值,只要给下标为0的元素赋值即可。
这个参数是枚举函数传出枚举到设备信息的列表,枚举到多少设备,就传出多少个DONGLE_INFO,所以需要传入足够数量的的DONGLE_INFO,如下:
这个参数一般是作为传入传出数据的缓冲区的,所以创建数组的时候,需要创建足够长的空间,如下:
开发的过程中,踩到一些坑耽误了不少时间,这里总结一下。
ROCKEY-ARM的结构体是按字节对齐的,ref-struct-napi没有找到设置字节对齐的方法。当时声明的结构体如下:
测试的时候会发现定义的结构体和ROCKEY-ARM定义的结构体对齐方式不一样,于是把m_Birthday和m_HID两个成员从ref.types.uint64,拆分成左右两个uint32,这样就可以让结构体对齐方式和ROCKEY-ARM的一致。使用m_Birthday和m_HID的时候,需要讲左右两个uint32拼接一些,稍微麻烦一点,但是在没找到配置StructType对齐方的情况,保证结果正确,还是可以接受的。
Node.js模块封装及使用
Node.js中也有一些功能的封装,类似C#的类库,封装成模块这样方便使用,安装之后用require()就能引入调用.
一、Node.js模块封装
1.创建一个名为censorify的文件夹
2.在censorify下创建3个文件censortext.js、package.json、README.md文件
1)、在censortext.js下输入一个过滤特定单词并用星号代替的函数。
var censoredWorlds=["sad","bad","mad"]; var custormCensoredWords=[]; function censor(inStr) { for(idx in censoredWorlds) { inStr=inStr.replace(censoredWorlds[idx],"****"); } for(idx in custormCensoredWords) { inStr=inStr.replace(custormCensoredWords[idx],"****"); } return inStr; } function addCensoreWorld(world) { custormCensoredWords.push(world); } function getCensoreWorlds() { return censoredWorlds.concat(custormCensoredWords); } exports.censor=censor; exports.addCensoreWorld=addCensoreWorld; exports.getCensoreWorlds=getCensoreWorlds;
2)、在package中配置清单信息 例如版本 名称和main指令等。
{ "author":"cuiyanwei", "name":"censority", "version":"0.1.1", "description":"Censors words out of text", "main":"censortext", "dependencies":{ "express":"latest" }, "enginee":{ "node":"*" } }
3)、创建的README.md文件主要是描述说明
3.使用命令行创建封装模块
使用命令行导航到censorify文件夹下,然后使用命令 npm pack 封装生成tgz文件,这样就封装了一个模块。
二、封装模块的使用
封装模块的使用有两种方法 :发布到NPM注册表、本地使用,这里只记录下本地使用的方法.
1.创建名readwords文件夹
2.命令行导航到readwords文件夹下,然后安装已经封装好的模块,如果是已经发布到NPM注册表的直接 npm install 名字,如果是在本地 npm install tgz文件路径。
3.安装完成后会在readwords文件夹下生成包含censority子文件夹的node_modules文件夹
4.新建readwords.js文件测试(注意代码console、封装模块的函数别写错了)
var censor=require("censority"); console.log(censor.getCensoreWorlds()); console.log(censor.censor("Some very sad,bad and mad text")); censor.addCensoreWorld("gloomy"); console.log(censor.getCensoreWorlds()); console.log(censor.censor("A very goolmy day."));
5.使用
用命令行node readwords.js来调用readwords.js查看结果
以上是关于Node.js使用ffi-napi,ref-napi,ref-array-napi,ref-struct-napi调用动态库的主要内容,如果未能解决你的问题,请参考以下文章
仅使用 Node.js 与将 Node.js 与 Apache/Nginx 一起使用