Как вызвать метод dll со структурой возврата? - PullRequest
2 голосов
/ 02 мая 2019

Я хочу создать приложение Electron, которое могло бы получить доступ к продукту цифровой видеозаписи (DVR) от Dahua. Я использую node-ffi для доступа к dhnetsdk.dll, предоставленному Dahua, и могу вызвать метод CLIENT_Init для инициализации SDK, но не могу вызвать метод CLIENT_LoginEx2 для входа в устройство DVR.

Проблема может быть в указателе C ++, но я не уверен. Код показан ниже.

  1. Метод CLIENT_LoginEx2, определенный в dhnetsdk.h
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. Реализация метода CLIENT_LoginEx2 в index.ts (по 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. Определение структуры данных NET_DEVICEINFO_Ex в index.ts (ссылка на структуру из 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 работает.

Скриншот 1

Когда я вызывал DVR.CLIENT_LoginEx2, приложение Electron зависало и возвращалось, как показано на скриншоте ниже.

DevTools был отключен от страницы. После перезагрузки страницы DevTools автоматически переподключится.

Снимок экрана 2

Я думал, что это может привести к неправильному определению структуры данных, но я действительно не знаю, как отследить, где ошибаться. Пожалуйста, дайте совет, очень ценю.

ОБНОВЛЕНИЕ 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 - это указатель для возврата кода ошибки из обратного вызова метода.

Полагаю, это может быть причиной сбоя, но не уверен, что кто-нибудь может помочь мне выяснить, в чем проблема, большое спасибо.

ОБНОВЛЕНИЕ 3 мая

Это может работать после некоторых усилий, я переопределил ffi.Library, как показано ниже Дело в том, что 'char *' нужно передать через ref.refType ('char') или 'string'; 'void *' нужно передать через ref.refType ('void') или 'указатель'; 'int *' нужно передать через 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 голосов
/ 06 мая 2019

Последнее обновление в моем посте с вопросом, я выяснил правильный способ определения ffi.Library.

Я делаю ошибки, вызванные неправильным пониманием языка Си. Дело в том, что 'char *' нужно передать через ref.refType ('char') или 'string'; 'void *' нужно передать через ref.refType ('void') или 'указатель'; 'int *' нужно передать через 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
...