nodejs使用node-ffi-napi 访问dll文件

本文介绍如何使用Electron和Node.js的ffi-napi库来访问CAN卡,并提供了详细的步骤和示例代码,包括结构体定义和DLL函数调用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

用electron做一个访问can卡的上位机

使用node-ffi-napi 访问dll文件,需要安装
ffi-napi
ref-napi
ref-array-napi
ref-struct-napi

安装ffi需要用到node-gyp,安装教程可以百度下

以下是使用过程


需要调用的dll的.h头文件如下,此dll是用来访问can卡的
#ifndef CONTROLCAN_H
#define CONTROLCAN_H

//#include <cvidef.h>	//使用CVI平台开发,请使用该语句。
//接口卡类型定义
#define VCI_USBCAN1		3
#define VCI_USBCAN2		4
#define VCI_USBCAN2A		4
#define VCI_USBCAN_E_U 		20
#define VCI_USBCAN_2E_U 	21

//函数调用返回状态值
#define	STATUS_OK					1
#define STATUS_ERR					0
//1.ZLGCAN系列接口卡信息的数据类型。
typedef  struct  _VCI_BOARD_INFO{   
		USHORT	hw_Version;
		USHORT	fw_Version;
		USHORT	dr_Version;
		USHORT	in_Version;
		USHORT	irq_Num;
		BYTE	can_Num;
		CHAR	str_Serial_Num[20];
		CHAR	str_hw_Type[40];
		USHORT	Reserved[4];
} VCI_BOARD_INFO,*PVCI_BOARD_INFO; 
//2.定义CAN信息帧的数据类型。
typedef  struct  _VCI_CAN_OBJ{
	UINT	ID;
	UINT	TimeStamp;
	BYTE	TimeFlag;
	BYTE	SendType;
	BYTE	RemoteFlag;//是否是远程帧
	BYTE	ExternFlag;//是否是扩展帧
	BYTE	DataLen;
	BYTE	Data[8];
	BYTE	Reserved[3];
}VCI_CAN_OBJ,*PVCI_CAN_OBJ;
//3.定义初始化CAN的数据类型
typedef struct _VCI_INIT_CONFIG{
	DWORD	AccCode;
	DWORD	AccMask;
	DWORD	Reserved;
	UCHAR	Filter;
	UCHAR	Timing0;	
	UCHAR	Timing1;	
	UCHAR	Mode;
}VCI_INIT_CONFIG,*PVCI_INIT_CONFIG;
/ new add struct for filter /
typedef struct _VCI_FILTER_RECORD{
	DWORD ExtFrame;	//是否为扩展帧
	DWORD Start;
	DWORD End;
}VCI_FILTER_RECORD,*PVCI_FILTER_RECORD;
 
#define EXTERNC		extern "C"
EXTERNC DWORD __stdcall VCI_OpenDevice(DWORD DeviceType,DWORD DeviceInd,DWORD Reserved);
EXTERNC DWORD __stdcall VCI_CloseDevice(DWORD DeviceType,DWORD DeviceInd);
EXTERNC DWORD __stdcall VCI_InitCAN(DWORD DeviceType, DWORD DeviceInd, DWORD CANInd, PVCI_INIT_CONFIG pInitConfig);
EXTERNC DWORD __stdcall VCI_ReadBoardInfo(DWORD DeviceType,DWORD DeviceInd,PVCI_BOARD_INFO pInfo);
EXTERNC DWORD __stdcall VCI_SetReference(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd,DWORD RefType,PVOID pData);
EXTERNC ULONG __stdcall VCI_GetReceiveNum(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd);
EXTERNC DWORD __stdcall VCI_ClearBuffer(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd);
EXTERNC DWORD __stdcall VCI_StartCAN(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd);
EXTERNC DWORD __stdcall VCI_ResetCAN(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd);
EXTERNC ULONG __stdcall VCI_Transmit(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd,PVCI_CAN_OBJ pSend,ULONG Len);
EXTERNC ULONG __stdcall VCI_Receive(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd,PVCI_CAN_OBJ pReceive,ULONG Len,INT WaitTime);
/*------------------------------------------------其他补充函数及数据结构描述------------------------------------------------*/
//USB-CAN总线适配器板卡信息的数据类型1,该类型为VCI_FindUsbDevice函数的返回参数。
typedef  struct  _VCI_BOARD_INFO1{
	USHORT	hw_Version;
	USHORT	fw_Version;
	USHORT	dr_Version;
	USHORT	in_Version;
	USHORT	irq_Num;
	BYTE	can_Num;
	BYTE	Reserved;
	CHAR	str_Serial_Num[8];
	CHAR	str_hw_Type[16];
	CHAR	str_Usb_Serial[4][4];
} VCI_BOARD_INFO1,*PVCI_BOARD_INFO1;
//USB-CAN总线适配器板卡信息的数据类型2,该类型为VCI_FindUsbDevice函数的返回参数。为扩展更多的设备
typedef  struct  _VCI_BOARD_INFO2{
	USHORT	hw_Version;
	USHORT	fw_Version;
	USHORT	dr_Version;
	USHORT	in_Version;
	USHORT	irq_Num;
	BYTE	can_Num;
	BYTE	Reserved;
	CHAR	str_Serial_Num[8];
	CHAR	str_hw_Type[16];
	CHAR	str_Usb_Serial[10][4];
} VCI_BOARD_INFO2,*PVCI_BOARD_INFO2;

#define EXTERNC		extern "C"
EXTERNC DWORD __stdcall VCI_GetReference2(DWORD DevType,DWORD DevIndex,DWORD CANIndex,DWORD Reserved,BYTE *pData);
EXTERNC DWORD __stdcall VCI_SetReference2(DWORD DevType,DWORD DevIndex,DWORD CANIndex,DWORD RefType,BYTE *pData);
EXTERNC DWORD __stdcall VCI_ConnectDevice(DWORD DevType,DWORD DevIndex);
EXTERNC DWORD __stdcall VCI_UsbDeviceReset(DWORD DevType,DWORD DevIndex,DWORD Reserved);
EXTERNC DWORD __stdcall VCI_FindUsbDevice(PVCI_BOARD_INFO1 pInfo);
EXTERNC DWORD __stdcall VCI_FindUsbDevice2(PVCI_BOARD_INFO2 pInfo);
#endif

nodejs中安装完成下列包

  "dependencies": {
    "ffi-napi": "^3.1.0",
    "ref-array-napi": "^1.2.1",
    "ref-napi": "^3.0.1",
    "ref-struct-napi": "^1.1.1"
  }

然后在js文件中使用 (注释和解释在代码中)

dll 的调用需要注意dll是64位还是32位的
这里nodejs是64位的,dll也是64位的
否则可能报错

let ffi = require("ffi-napi");
let ref = require('ref-napi');
let ArrayType = require('ref-array-napi');
let StructType = require('ref-struct-napi');

let _VCI_BOARD_INFO = StructType({    //定义结构体
    hw_Version: ref.types.ushort,
    fw_Version: ref.types.ushort,
    dr_Version: ref.types.ushort,
    in_Version: ref.types.ushort,
    irq_Num: ref.types.ushort,
    can_Num: ref.types.byte,
    str_Serial_Num: ArrayType(ref.types.char, 20),    //char str_Serial_Num[20]
    str_hw_Type: ArrayType(ref.types.char, 40),
    Reserved: ArrayType(ref.types.ushort, 4),
})
let _VCI_CAN_OBJ = StructType({  //定义结构体
    ID: ref.types.uint,
    TimeStamp: ref.types.uint,
    TimeFlag: ref.types.byte,
    SendType: ref.types.byte,
    RemoteFlag: ref.types.byte,
    ExternFlag: ref.types.byte,
    DataLen: ref.types.byte,
    Data: ArrayType(ref.types.byte, 8),
    Reserved: ArrayType(ref.types.byte, 3),
})

let _VCI_INIT_CONFIG = StructType({  //定义结构体
    AccCode: ref.types.ulong,        //DWORD 就是 ref.types.ulong
    AccMask: ref.types.ulong,
    Reserved: ref.types.ulong,
    Filter: ref.types.uchar,
    Timing0: ref.types.uchar,
    Timing1: ref.types.uchar,
    Mode: ref.types.uchar,
})
let _VCI_FILTER_RECORD = StructType({  //定义结构体
    ExtFrame: ref.types.ulong,
    Start: ref.types.ulong,
    End: ref.types.ulong,
})
let _VCI_BOARD_INFO1 = StructType({  //定义结构体
    hw_Version: ref.types.ushort,
    fw_Version: ref.types.ushort,
    dr_Version: ref.types.ushort,
    in_Version: ref.types.ushort,
    irq_Num: ref.types.ushort,
    can_Num: ref.types.byte,
    Reserved: ref.types.byte,
    str_Serial_Num: ArrayType(ref.types.char, 8),
    str_hw_Type: ArrayType(ref.types.char, 16),
    str_Usb_Serial: ArrayType(ArrayType(ref.types.char, 4), 4), //char str_Usb_Serial[4][4]
})
let _VCI_BOARD_INFO2 = StructType({   //定义结构体
    hw_Version: ref.types.ushort,
    fw_Version: ref.types.ushort,
    dr_Version: ref.types.ushort,
    in_Version: ref.types.ushort,
    irq_Num: ref.types.ushort,
    can_Num: ref.types.byte,
    Reserved: ref.types.byte,
    str_Serial_Num: ArrayType(ref.types.char, 8),
    str_hw_Type: ArrayType(ref.types.char, 16),
    str_Usb_Serial: ArrayType(ArrayType(ref.types.char, 10), 4),   //char str_Usb_Serial[10][4]
})
let canDLL = ffi.Library('./ControlCAN.dll', {   //定义方法
    'VCI_OpenDevice': [ref.types.ulong, [ref.types.ulong, ref.types.ulong, ref.types.ulong]],
    'VCI_CloseDevice': [ref.types.ulong, [ref.types.ulong, ref.types.ulong]],
    'VCI_StartCAN': [ref.types.ulong, [ref.types.ulong, ref.types.ulong, ref.types.ulong]],
    'VCI_ReadBoardInfo': [ref.types.ulong, [ref.types.ulong, ref.types.ulong, ref.refType(_VCI_BOARD_INFO)]],
    'VCI_InitCAN': [ref.types.ulong, [ref.types.ulong, ref.types.ulong, ref.types.ulong, ref.refType(_VCI_INIT_CONFIG)]],
    'VCI_FindUsbDevice': [ref.types.ulong, [ref.refType(_VCI_BOARD_INFO1)]]
})

//打开设备
let open = canDLL.VCI_OpenDevice(3, 0, 0)  //传入3个DWORD
console.log(open)
//设备初始化
let initconfig = new _VCI_INIT_CONFIG({  //创建一个结构体并赋值
    AccCode: 0x80000008,
    AccCode: 0xFFFFFFFF,
    Filter: 0,
    Timing0: 0x03,
    Timing1: 0x1c,
    Mode: 0
});
let init = canDLL.VCI_InitCAN(3, 0, 0, initconfig.ref())  //传入3个dword和一个结构体指针
console.log(init)

//打开can
let startCan = canDLL.VCI_StartCAN(3, 0, 0);
console.log(startCan)
//打印接口卡信息
let boardinfo = new _VCI_BOARD_INFO();   //创建一个结构体
canDLL.VCI_ReadBoardInfo.async(3, 0, boardinfo.ref(), (err, result) => {
    if (err) {
        console.log(err)
    }
    console.log(result)  //调用成功后,返回1
    console.log("--------------接口卡信息------------------------")
    console.log('hw_Version:'+boardinfo.hw_Version)   //访问结构体内的值
    console.log('fw_Version:'+boardinfo.fw_Version)	
    console.log('dr_Version:'+boardinfo.dr_Version)
    console.log('in_Version:'+boardinfo.in_Version)
    console.log('irq_Num:'+boardinfo.irq_Num)
    console.log('can_Num:'+boardinfo.can_Num)
    console.log(boardinfo.str_Serial_Num)
    console.log(boardinfo.str_hw_Type)
    console.log(boardinfo.Reserved)
})
Node.js是一个使用JavaScript编写的服务器端应用程序环境,它使用事件驱动、非阻塞I/O模型,使得它非常适合在高并发场景下运行。在一些场景下,我们需要将Node.js与一些C/C++语言开发的库进行交互,这时我们可以通过node-ffi或者node-ffi-napi这样的扩展来实现。这些扩展可以让我们在Node.js中加载动态链接库(DLL),并且调用DLL中的函数。 Node.js中加载DLL的方式和C/C++中一样,需要通过动态链接库的名称来加载对应的DLL文件。一般来说,我们可以在Node.js中使用node-ffi模块来完成动态链接库的加载和函数的调用。 node-ffi提供了一个简单而直接地API,允许我们从JavaScript代码中调用动态链接库函数。要使用node-ffi,我们需要做以下几步: 1. 安装node-ffi:在命令行中运行npm install ffi --save命令。 2. 加载DLL文件使用ffi.Library方法来加载DLL文件。 3. 定义函数类型:使用ffi.Function和ffi.Callback方法来定义函数类型。 4. 调用DLL函数:使用ffi.Library对象中的函数名来调用DLL函数。 下面是一个使用node-ffi模块加载DLL文件并调用DLL函数的示例: ``` const ffi = require('ffi'); // 加载DLL文件 const win32 = ffi.Library('kernel32', { 'GetLastError': [ 'int32', [] ] }); // 调用DLL函数,返回错误码 console.log(win32.GetLastError()); ``` 在这个示例中,我们使用ffi.Library方法加载了kernel32.dll文件,然后使用GetLastError函数名来调用DLL函数。注意:在不同的操作系统环境中DLL文件名称和函数名可能有所不同。 总的来说,Node.js通过node-ffinode-ffi-napi这些扩展提供了一种简单、快捷的方式来将JavaScript代码与C/C++语言开发的库进行交互。使用这些扩展,我们可以轻松地在Node.js中加载DLL文件,调用DLL函数,实现更为复杂的应用程序。
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值