如何用struct return调用dll方法?

时间:2019-05-02 15:17:34

标签: node-ffi

我想构建一个电子应用程序,可以访问大化的数字视频录制(DVR)产品。我使用node-ffi访问大华提供的dhnetsdk.dll,可以调用CLIENT_Init方法初始化SDK,但无法调用CLIENT_LoginEx2方法登录DVR设备。

问题可能是C ++指针,但我不确定。代码如下所示。

    在dhnetsdk.h中定义的
  1. CLIENT_LoginEx2方法
CLIENT_NET_API LLONG CALL_METHOD CLIENT_LoginEx2(
  const char *pchDVRIP, 
  WORD wDVRPort, 
  const char *pchUserName, 
  const char *pchPassword, 
  EM_LOGIN_SPAC_CAP_TYPE emSpecCap, 
  void* pCapParam, 
  LPNET_DEVICEINFO_Ex lpDeviceInfo, 
  int *error = 0
);
  1. 在index.ts中实现CLIENT_LoginEx2方法(通过node-ffi)
const DVR = ffi.Library('dhnetsdk.dll', {
  // SDK
  CLIENT_Init: [ref.types.bool, ['pointer', ref.types.int64]],
  CLIENT_GetSDKVersion: [ref.types.uint, []],
  // Login
  CLIENT_LoginEx2: [
    ref.types.int64, [
      ref.types.char, 
      ref.types.ushort, 
      ref.types.char, 
      ref.types.char, 
      ref.types.int, 
      'void *', 
      ref.refType(NET_DEVICEINFO_Ex), 
      ref.types.int
    ]
  ],
});
  1. 在index.ts中定义NET_DEVICEINFO_Ex数据结构(dhnetsdk.dll中的结构引用)

index.ts

const NET_DEVICEINFO_Ex = StructType({
  'sSerialNumber': ArrayType(ref.types.byte, DH_DEV_SERIALNO_LEN),
  'nAlarmInPortNum': ref.types.int,
  'nAlarmOutPortNum': ref.types.int,
  'nDiskNum': ref.types.int,
  'nDVRType': ref.types.int,
  'nChanNum': ref.types.int,
  'byLimitLoginTime': ref.types.byte,
  'byLeftLogTimes': ref.types.byte,
  'bReserved': ArrayType(ref.types.byte, 2),
  'nLockLeftTime': ref.types.int,
  'Reserved': ArrayType(ref.types.char, 24),
});

dhnetsdk.h

typedef struct
{
    BYTE                sSerialNumber[DH_SERIALNO_LEN];
    int                 nAlarmInPortNum;
    int                 nAlarmOutPortNum;
    int                 nDiskNum;
    int                 nDVRType;
    int                 nChanNum;
    BYTE                byLimitLoginTime;
    BYTE                byLeftLogTimes;
    BYTE                bReserved[2];
    int                 nLockLeftTime;
    char                Reserved[24];
} NET_DEVICEINFO_Ex, *LPNET_DEVICEINFO_Ex;
  1. 实际致电
let lpDeviceInfo = ref.alloc(NET_DEVICEINFO_Ex);
let error = 0;
console.log(DVR.CLIENT_Init(disConnectCallback, 0));
console.log(DVR.CLIENT_LoginEx2('192.168.1.100', 37777, 'admin', 'dh123456', 0, null, lpDeviceInfo, error));
console.log(DVR.CLIENT_GetSDKVersion());

当我运行代码时,DVR.CLIENT_Init和DVR.CLIENT_GetSDKVersion的结果如下图所示返回,我可以确认sdk正常工作。

Screenshot 1

当我调用DVR.CLIENT_LoginEx2时,Electron应用程序崩溃并返回了以下屏幕截图。

  

DevTools已从页面断开连接。页面重新加载后,   DevTools将自动重新连接。

Screenshot 2

我认为这可能是由错误的数据结构定义引起的,但是我真的不知道如何跟踪错误的地方。请提供建议,非常感谢。

5月3日更新

SDK手册编写了一个片段,以演示如何按以下方式调用CLIENT_LoginEx2

LLONG lLoginHandle = CLIENT_LoginEx2(szDevIp, nPort, szUserName, szPasswd,
EM_LOGIN_SPEC_CAP_TCP, NULL, &stDevInfo, &nError);

它显示了最后两个参数需要传递指针,参数&stDevInfo是指向NET_DEVICEINFO_Ex数据结构的结构的指针,参数&nError是从方法回调返回错误代码的指针。

我想这可能是崩溃的原因,但不确定,任何人都可以帮助我弄清楚是什么问题,谢谢。

5月3日更新

经过一些努力它可能会起作用,我将ffi.Library重新定义如下 关键是需要通过ref.refType('char')或简称为'string'传递的'char *';简而言之,需要通过ref.refType('void')或'pointer'传递'void *'; 需要通过ref.refType('int')或简称为'int *'传递。

const NetDeviceInfoEx = StructType({
  'sSerialNumber': ArrayType(ref.types.byte, DH_DEV_SERIALNO_LEN),
  'nAlarmInPortNum': ref.types.int,
  'nAlarmOutPortNum': ref.types.int,
  'nDiskNum': ref.types.int,
  'nDVRType': ref.types.int,
  'nChanNum': ref.types.int,
  'byLimitLoginTime': ref.types.byte,
  'byLeftLogTimes': ref.types.byte,
  'bReserved': ArrayType(ref.types.byte, 2),
  'nLockLeftTime': ref.types.int,
  'Reserved': ArrayType(ref.types.char, 24),
});
const NetDeviceInfoExPtr = ref.refType(NetDeviceInfoEx);

CLIENT_LoginEx2: [
  ref.types.int64, [
    'string', 
    ref.types.ushort, 
    'string', 
    'string', 
    ref.types.int, 
    'pointer', 
    NetDeviceInfoExPtr, // or ref.refType('NetDeviceInfoEx')
    'int*'
  ]
],

以下代码实际上称为DLL 请注意,我们需要定义输出参数变量以从返回的DLL中获取消息。

// Output Parameters
var lpDeviceInfo = ref.alloc(NetDeviceInfoEx);
var error = ref.alloc('int');

var v = DVR.CLIENT_LoginEx2('192.168.1.100', 37777, 'admin', 'dh123456', EM_LOGIN_SPAC_CAP_TYPE.EM_LOGIN_SPEC_CAP_TCP.value, null, lpDeviceInfo, error);
console.log(v);
console.log(lpDeviceInfo.deref());

因为我没有DVR设备来测试代码,所以无法确认它是否有效。明天进入办公室后,我会对其进行测试。

1 个答案:

答案 0 :(得分:0)

我的问题帖子中的最新更新是,我找出了定义ffi.Library的正确方法。

我由于对C语言的误解而犯错。关键是需要通过ref.refType('char')或简称为'string'传递的'char *';简而言之,需要通过ref.refType('void')或'pointer'传递'void *'; 需要通过ref.refType('int')或简称为'int *'传递。

所以我重写了下面的代码,终于可以了。

const NetDeviceInfoEx = StructType({
  'sSerialNumber': ArrayType(ref.types.byte, DH_DEV_SERIALNO_LEN),
  'nAlarmInPortNum': ref.types.int,
  'nAlarmOutPortNum': ref.types.int,
  'nDiskNum': ref.types.int,
  'nDVRType': ref.types.int,
  'nChanNum': ref.types.int,
  'byLimitLoginTime': ref.types.byte,
  'byLeftLogTimes': ref.types.byte,
  'bReserved': ArrayType(ref.types.byte, 2),
  'nLockLeftTime': ref.types.int,
  'Reserved': ArrayType(ref.types.char, 24),
});
const NetDeviceInfoExPtr = ref.refType(NetDeviceInfoEx);

  /**
   * LLONG CLIENT_LoginEx2(
   *   const char *pchDVRIP,
   *   WORD wDVRPort,
   *   const char *pchUserName,
   *   const char *pchPassword,
   *   EM_LOGIN_SPAC_CAP_TYPE emSpecCap,
   *   void* pCapParam,
   *   LPNET_DEVICEINFO_Ex lpDeviceInfo,
   *   int *error
   * );
   */
  CLIENT_LoginEx2: [ref.types.int64, ['string', ref.types.ushort, 'string', 'string', ref.types.int, 'pointer', NetDeviceInfoExPtr, 'int*']],

以下代码实际上调用了DLL。请注意,我们需要定义输出参数变量以从返回的DLL中获取消息。

// Output Parameters
var lpDeviceInfo = ref.alloc(NetDeviceInfoEx);
var error = ref.alloc('int');

var v = DVR.CLIENT_LoginEx2('192.168.1.100', 37777, 'admin', 'dh123456', EM_LOGIN_SPAC_CAP_TYPE.EM_LOGIN_SPEC_CAP_TCP.value, null, lpDeviceInfo, error);
console.log(v); // it will return device ID by DLL defined
console.log(lpDeviceInfo.deref()); // it will return NetDeviceInfoEx struct data