1. 首页
  2. 后端

Electron调用dll的新姿势

  Electron调用dll的新姿势

=================

之前旧的系统在浏览器中调用dll都是使用IE的activex控件实现。进而通过dll脚本和硬件发生交互。现在IE浏览器已经不在默认预制在系统中,且对windows的操作系统有限制,win10之前的版本才能正常访问。

在不断的业务迭代过程中,已经成了制约系统扩展的最大阻碍。调研后选择了electron-egg框架来进行业务功能尝试,主要是dll的嵌入调用和设备交互。

ElectronEgg

Electron调用dll的新姿势

作为一个对前端不是那么擅长的后端来说,electron-egg已经包装了大部分操作,且拥有非常详尽的中文开发文档。可以无缝切换,低成本代码的开发。

框架设计

Electron调用dll的新姿势

具体的业务逻辑还是放在后台服务中,electron只负责透传交互指令和硬件设备进行交互。这里调用dll用的js库是koffi。


Koffi

Koffi 是一个快速且易于使用的 Node.js C FFI 模块。实现了在Node中调用dll的功能。

koffi版本2.8.0

DLL配置

按照官方文档dll文件放置在extraSources文件中。

DLL加载

const lib = koffi.load('build/extraResources/dll/dcrf32.dll');

DLL调用

dll调用有两种方式。分别为经典语法和类c原型方式。

  • 经典语法

定义函数名,返回参数类型,入参类型constprintf=lib.func('printf','int', ['str','...']);

  • 类C语法

在类中定义方法类似,lib.func('int printf(const char *fmt, ...)');

推荐使用类C语法更加方便,不受参数的限制,更易于修改。

DLL调用类型

  1. 同步调用

本机函数,您就可以像调用任何其他 JS 函数一样简单地调用它。

const atoi = lib.func('int atoi(const char *str)');

let value = atoi('1257');
  1. 异步调用

有一些耗时的操作,可以使用异步调用回调的方式处理。

const atoi = lib.func('int atoi(const char *str)');

atoi.async('1257', (err, res) => {
    console.log('Result:', res);
})

JS类型值传递

JS基础类型时不支持值传递的,遇到需要传递指针变量时,直接调用是无法获取到变更后的值。相应的koffi也提供了非常方便的值包装。

  1. 数组包装

项目中采用比较方便的数组包装来进行值传递。包装基础对象到数组中,变更后取出第一位就能获取到变更后的值。

Electron调用dll的新姿势

需要定义返回的值的获取长度,防止出现只获取到部分的返回结果。

  1. 引用类型包装

把基础类型包装成引用对象。传递到函数中。

let cardsenr = koffi.alloc('int', 64);
let cardRet = dcCard(icdev, 0, cardsenr);

这种就更方便,调用后也不需要转换。在调用完后需要通过free方法进行内存释放。

  1. Buffer对象

如果遇到接收中文数据时,koffi可以结合Node中的Buffer进行对象传递。

let text = Buffer.alloc(1024);
let ret = read(text);

部分dll默认读出来的编码是gbk格式,需要将buffer对象转换成utf8格式的字符串进行展示。
就需要通过iconv组件进行gbk解码。

iconv.decode(text, 'gbk')

如果需要把utf8转成gbk,使用相反的方式就可以iconv.encode(build/photos/${id_number}.bmp, "gbk")

结构体调用

JS中只有引用对象,如果遇到结构体参数需要进行JS包装。

// C
typedef struct A {
    int a;
    char b;
    const char *c;
    struct {
        double d1;
        double d2;
    } d;
} A;
// JS
const A = koffi.struct('A', {
    a: 'int',
    b: 'char',
    c: 'const char *', // Koffi does not care about const, it is ignored
    d: koffi.struct({
        d1: 'double',
        d2: 'double'
    })
});

如果调用出现对齐不对的情况,可以使用pack方法进行手动对齐类型。

// This struct is 3 bytes long
const PackedStruct = koffi.pack('PackedStruct', {
    a: 'int8_t',
    b: 'int16_t'
});

常规dll的调用都可以轻易的在JS中实现。

Node后端

底层调用已经通过koffi来实现。后面就需要借助electron-egg框架能力进行业务代码指令透传。

service层

'use strict';

const { Service } = require('ee-core');

/**
 * 示例服务(service层为单例)
 * @class
 */
class ExampleService extends Service {

  constructor(ctx) {
    super(ctx);
  }

  /**
   * test
   */
  async test(args) {
    let obj = {
      status:'ok',
      params: args
    }

    return obj;
  }
}

ExampleService.toString = () => '[class ExampleService]';
module.exports = ExampleService;

定义我们需要交互的方法

controller层

'use strict';

const { Controller } = require('ee-core');
const Log = require('ee-core/log');
const Services = require('ee-core/services');

/**
 * example
 * @class
 */
class ExampleController extends Controller {

  constructor(ctx) {
    super(ctx);
  }


  /**
   * 所有方法接收两个参数
   * @param args 前端传的参数
   * @param event - ipc通信时才有值。详情见:控制器文档
   */

  /**
   * test
   */
  async test () {
    const result = await Services.get('example').test('electron');
    Log.info('service result:', result);

    return 'hello electron-egg';
  }
}

ExampleController.toString = () => '[class ExampleController]';
module.exports = ExampleController;

JS前端

通过ipc的方式,乡调用api一样调用node后端接口。

定义路由

import { ipc } from '@/utils/ipcRenderer';

const ipcApiRoute = {
  test: 'controller.d8.test',
  init: 'controller.d8.init',
  reset: 'controller.d8.reset',
  exit: 'controller.d8.exit',
  command:'controller.d8.command'
}

调用

ipc.invoke(this.ipcApiRoute.init).then(r => {
  // r为返回的数据
  if(r >= 0) {
    this.setInitRet(r);
    this.scrollToBottom("连接成功,返回码:" + r);
    this.connectStr = "连接成功";
    this.setDeviceStatus('success');
  } else {
    this.scrollToBottom("连接失败,返回码:" + r);
  }
})

通过ipc的invode方法调用方法即可。后续就可以愉快的编写我们的业务代码了。

参照

Koffi帮助文档

electron-egg帮助文档

原文链接: https://juejin.cn/post/7352075771534868490

文章收集整理于网络,请勿商用,仅供个人学习使用,如有侵权,请联系作者删除,如若转载,请注明出处:http://www.cxyroad.com/17270.html

QR code