一、安装ffi(依赖gcc环境)
项目开源地址:GitHub - node-ffi/node-ffi: Node.js Foreign Function Interface
1、安装:npm install node-gyp
2、安装:npm install node-ffi,如果用这个方法安装失败,则在package.json添加如下配置,然后在项目根路径执行npm install,如下图:
3、检查ffi是否安装成功,新建测试文件,ffitest.js
const ffi = require('ffi-napi'); console.log("ffi >>> " , ffi);
4、执行命令:node ffitest.js,如果不报错,则安装成功
5、准备一个so文件,用c语言写一个程序,创建有个文件ffitest.c,编写代码如下
#include//两数相乘 int add(int a , int b) { printf("add call n"); return a + b; }
然后编译成so文件,编译命令:gcc -shared ffitest.c -o ffitest.so -fPIC -ldl,然后就会得到文件,ffitest.so
二、js调用so
1、调用无参函数
(1)在ffitest.c编写有个无参的函数,然后重新编译,代码如下:
char* getMsg() { printf("getMsg call n"); return "hello world"; }
(2)编写js代码,编写文件ffitest.js,代码如下:
const ffi = require('ffi-napi'); const libs = ffi.Library("./ffitest.so" , { "getMsg":["string" , []] }); const msg = libs.getMsg(); console.log("getMsg return = " , msg);
(3)执行命令:node ffitest.js,输出如下信息,说明调用成功:
2、调用有参值传递参数函数,以传int为例子,
(1)在ffitest.c编译一个参数类型为int的函数且重新编译,代码如下:
int add(int a , int b) { printf("add call n"); return a + b; }
(2)在ffitest.js,编写代码如下:
const ffi = require('ffi-napi'); const libs = ffi.Library("./ffitest.so" , { "getMsg":["string" , []], "add":["int" , ["int" , "int"]] }); const msg = libs.getMsg(); console.log("getMsg return = " , msg); const result = libs.add(1 , 1); console.log("add = " , result);
(3)node ffitest.js,输出如下信息,说明调用成功
3、调用地址传参的的函数
(1)在ffitest.c中,添加一个add2,这是没有返回值,通过地址传值的方式将返回值输出,具体代码如下:
void add2(int a , int b , int* out) { printf("add2 call n"); int c = a + b; *out = c; }
(2)修改ffitest.js,代码如下:
const ffi = require('ffi-napi'); const libs = ffi.Library("./ffitest.so" , { "getMsg":["string" , []], "add":["int" , ["int" , "int"]], "add2":["void" , ["int" , "int" , "string"]] }); const msg = libs.getMsg(); console.log("getMsg return = " , msg); const result = libs.add(1 , 1); console.log("add = " , result); //申请4个字节,因为一个int占4个字节 //此时返回的是16进制小端模式byte类型,需要我们自己转成十进制 let out = new Buffer.alloc(4); libs.add2(100 , 2000 , out); const res = byte2int(out); console.log("add2 = " , res); process.on('exit' , function(){ callback; }); function byte2int(param){ let result = param[3] << 24; result = result + (param[2] << 16); result = result + ( param[1] << 8); result = result + param[0]; return result; }
此时返回的是byte类型,是小端模式,此时需要自己手动转int,具体怎么转可以参考上面byte2int()函数,涉及到值专递,可以参考上面的方法处理。
4、常用参数对应关系
char* ---------------- string
int -------------------- int
在处理值传递时比较麻烦,目前也有相关参数处理的方法具体请参考GitHub - TooTallNate/ref: Turn Buffer instances into "pointers",此处不做介绍
但是以上参数的基本够用了
三、回调函数的处理
在实际的项目中,往往有时候需要用到回调函数,c语言利用函数指针实现回调,但是js是可以直接把函数作为参数,相比来说js实现回调就方便得多。下面介绍如何实现调用回调函数:
(1)在ffitest.c中,编译一个回调函数,代码如下,
#includevoid (*MY_CALL_BACK)(char* msg); //两数相乘 int add(int a , int b) { printf("add call n"); return a + b; } void add2(int a , int b , int* out) { printf("add2 call n"); int c = a + b; *out = c; } char* getMsg() { printf("getMsg call n"); return "hello world"; } //设置回调函数 void SetCallBack(void* callback) { MY_CALL_BACK = callback; } //执行回调函数 void testCallBacl() { MY_CALL_BACK("this is a callback"); printf("*****************n"); }
(2)修改ffitest.js文件,在设置回到函数的时候,需要传入的是指针,参数我们可以穿pointer,具体代码如下:
const ffi = require('ffi-napi'); const libs = ffi.Library("./ffitest.so" , { "getMsg":["string" , []], "add":["int" , ["int" , "int"]], "add2":["void" , ["int" , "int" , "string"]], "SetCallBack":["void" , ["pointer"]], "testCallBacl":["void" , []] }); const msg = libs.getMsg(); console.log("getMsg return = " , msg); const result = libs.add(1 , 1); console.log("add = " , result); //申请4个字节,因为一个int占4个字节 //此时返回的是16进制小端模式byte类型,需要我们自己转成十进制 let out = new Buffer.alloc(4); libs.add2(100 , 2000 , out); const res = byte2int(out); console.log("add2 = " , res); //回调函数 const callback = ffi.Callback("void" , ['string'] , function(data){ console.log("call back data: " , data); }); //设置回调函数 libs.SetCallBack(callback); //执行这个函数,然后会调用回调函数 libs.testCallBacl(); //byte转int function byte2int(param){ let result = param[3] << 24; result = result + (param[2] << 16); result = result + ( param[1] << 8); result = result + param[0]; return result; }
四、总结,通过ffi库,可以利用c语言或者c++对程序进行扩展。这里只是对调用so的例子,调用dll代码是一样的,只是我在安装windows环境的时候被自己劝退了,如果有大神有什么解决方法可以分享一下。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)