js通过ffi调用so

一、安装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<stdio.h>
//两数相乘
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中,编译一个回调函数,代码如下,

#include<stdio.h>
 void (*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环境的时候被自己劝退了,如果有大神有什么解决方法可以分享一下。

 

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AndyWei147

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值