如题所示,这个问题是我在electron项目中使用ffi调用动态链接库出现的,本机运行都好好的,打包构建然后放到别的机器上运行出错。
这个问题一度也让我很迷茫,如果是路径导致的这个问题,那么把路径指定正确就可以了,但是偏偏路径也是对的,就是报错。那应该是环境问题。
我为什么要使用ffi调用动态链接库?我的需求是这样的,需要编程实现tcpip方式连接诺德施瓦茨仪表设备cmw500,其实就是visa编程了,我开始通过c语言简单实现了这个功能,以为万事大吉了,然后写了一个动态链接库,让electron项目去调用,因为node方面对visa操作的很少,网上唯一可以搜到的一个链接就是github上的这个:https://github.com/petertorelli/ni-visa。我当时没有细看,也不确定这个方式可不可靠。
自己写的动态链接库代码:
pch.h
#ifdef PCH_H
#else
#define PCH_H extern "C" _declspec(dllimport)
// add headers that you want to pre-compile here
#include "framework.h"
#endif //PCH_H
PCH_H void QueryMeasureResult(char* source,char* cmd,char* result);
pch.cpp
// pch.cpp: source file corresponding to the pre-compiled header
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE)
#define _CRT_SECURE_NO_DEPRECATE
#endif
#define PCH_H extern "C" _declspec(dllexport)
#include "pch.h"
#include
#include
#include
#include "visa.h"
static ViSession defaultRM;
static ViSession instr;
static ViUInt32 retCount;
static ViUInt32 writeCount;
static ViStatus status;
static unsigned char buffer[100];
static char stringinput[512];
// When you are using pre-compiled headers, this source file is necessary for compilation to succeed.
void QueryMeasureResult(char* source,char* cmd,char* result) {
status = viOpenDefaultRM(&defaultRM);
if (status < VI_SUCCESS) {
viClose(defaultRM);
strcpy(result, "error");
return;
}
status = viOpen(defaultRM, source,VI_NULL, VI_NULL, &instr);
if (status < VI_SUCCESS) {
viClose(defaultRM);
strcpy(result, "error");
return;
}
status = viSetAttribute(instr, VI_ATTR_TMO_VALUE, 5000);
status = viSetAttribute(instr, VI_ATTR_ASRL_BAUD, 4800);
status = viSetAttribute(instr, VI_ATTR_ASRL_DATA_BITS, 8);
status = viSetAttribute(instr, VI_ATTR_ASRL_PARITY, VI_ASRL_PAR_NONE);
status = viSetAttribute(instr, VI_ATTR_ASRL_STOP_BITS, VI_ASRL_STOP_ONE);
status = viSetAttribute(instr, VI_ATTR_TERMCHAR_EN, VI_TRUE);
status = viSetAttribute(instr, VI_ATTR_TERMCHAR, 0xA);
strcpy(stringinput, cmd);
status = viWrite(instr, (ViBuf)stringinput, (ViUInt32)strlen(stringinput), &writeCount);
if (status < VI_SUCCESS) {
viClose(defaultRM);
strcpy(result, "error");
return;
}
status = viRead(instr, buffer, 100, &retCount);
if (status < VI_SUCCESS) {
strcpy(result, "error");
}
else {
//buffer = "0,2.415445E+001"
strcpy(result, (const char*)buffer);
}
viClose(defaultRM);
}
后来呢,使用自己编译的动态库操作visa,可以实现,而且本机开发环境没有问题,然后非常高兴的就以为这个问题解决了,打包,后来测试就出了问题,总是报如题所示的错误。
我的这段代码主要是使用visual studio 2017编译的,当时用c语言测试和node测试都没有问题。
visatest.js
var ffi = require("ffi")
var ref = require("ref")
var path = require("path")
var dllpath = path.resolve(__dirname,"visademo.dll")
console.log(dllpath)
const api = new ffi.Library(dllpath,{
QueryMeasureResult:["void",["string","string","string"]]
})
var source="TCPIP0::192.168.253.13::inst0::INSTR";
var cmd = "FETCh:GPRF:MEAS:POWER:PEAK:MAXimum?\n";
var result=Buffer.alloc(100);
result.type = ref.types.char;
api.QueryMeasureResult(source,cmd,result);
var str = result.readCString()
var arr = str.split(",");
var maximum = arr[1]
console.log(parseFloat(maximum),maximum)
/以上是我的代码和问题描述部分//
我以为是因为我的代码本身调用了visa库,如果它不用调用其他库,简单的动态链接库是不是就不会出现这个问题呢?实际上,我同样使用visual studio 2017编译,本机检查都没有问题,放到别的机器上就出问题了。看来问题就出在环境上。
后来偶然看到一个论坛上说,需要通过npm安装windows-build-tools,我试着在别的机器上安装,发现果然就不再报错了。
但是这么一来,只要是动态链接库调用,都需要在别的机器上安装这个东西,似乎太扯淡了,其实electron+ffi现在这么成熟了,不可能编写的程序最后还需要在运行机器上再安装一个windows-build-tools。
其实windows-build-tools安装,本质需要安装一个msbuild,同时安装了python,windows10 sdk,cmake等工具,而windows-build-tools需要下载vs_BuildTools.exe,并启动安装,其实就是使用visual studio install的安装,如果你安装过visual studio 2017的话,对这个非常熟悉。我的环境里面,需要安装上cmake,同时cmake又依赖windows10 sdk,而这个玩意就有2.4G这么大,所以别的系统上如果需要运行不报错的electron+ffi程序,还需要安装大概3G的环境,有些不切实际。
后来,我又看了一遍前面提到的node环境下操作visa的项目,其实它借助了nivisa1850full安装之后,在系统中生成的visa64.dll依赖库,同样是操作动态链接库,这个项目还有6颗星,我觉着可以一试,抱着试试看的态度,最后发现可以。
而且别的机器不用安装windows-build-tools环境也可以运行,但是别的机器需要安装nivisa依赖环境,这个是需要操作诺德施瓦茨仪表的。
比较意外的是,我自己也写了一个动态库,在命令行下编译,没有使用visual studio 2017 ide工具编译,最后打包,在别的机器上使用也没有问题。
我断定,我的动态链接库调用的问题是出在使用visual studio 2017 ide工具编码编译,至于为什么,我现在还没有搞清楚。我使用命令行编译,其实也是使用的visual studio 2017安装生成的开发人员编译工具,竟然就没有问题。