Electron调用dll的新姿势

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

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

ElectronEgg

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

框架设计

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


Koffi

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

koffi版本2.8.0

DLL配置

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

DLL加载

ini 复制代码
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 函数一样简单地调用它。

ini 复制代码
const atoi = lib.func('int atoi(const char *str)');

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

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

javascript 复制代码
const atoi = lib.func('int atoi(const char *str)');

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

JS类型值传递

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

  1. 数组包装

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

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

  1. 引用类型包装

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

ini 复制代码
let cardsenr = koffi.alloc('int', 64);
let cardRet = dcCard(icdev, 0, cardsenr);

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

  1. Buffer对象

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

js 复制代码
let text = Buffer.alloc(1024);
let ret = read(text);

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

js 复制代码
iconv.decode(text, 'gbk')

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

结构体调用

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

c 复制代码
// 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方法进行手动对齐类型。

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

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

Node后端

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

service层

javascript 复制代码
'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层

javascript 复制代码
'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后端接口。

定义路由

bash 复制代码
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'
}

调用

kotlin 复制代码
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帮助文档

相关推荐
man20171 小时前
【2024最新】基于springboot+vue的闲一品交易平台lw+ppt
vue.js·spring boot·后端
hlsd#2 小时前
关于 SpringBoot 时间处理的总结
java·spring boot·后端
路在脚下@2 小时前
Spring Boot 的核心原理和工作机制
java·spring boot·后端
幸运小圣2 小时前
Vue3 -- 项目配置之stylelint【企业级项目配置保姆级教程3】
开发语言·后端·rust
ZJ_.2 小时前
Electron 沙盒模式与预加载脚本:保障桌面应用安全的关键机制
开发语言·前端·javascript·vue.js·安全·electron·node.js
前端SkyRain3 小时前
后端Node学习项目-用户管理-增删改查
后端·学习·node.js
提笔惊蚂蚁3 小时前
结构化(经典)软件开发方法: 需求分析阶段+设计阶段
后端·学习·需求分析
丁总学Java3 小时前
使用 npm 安装 Yarn
前端·npm·node.js
理想不理想v3 小时前
执行npm run build -- --report后,生产report.html文件是什么?
java·前端·javascript·vue.js·webpack·node.js
老猿讲编程3 小时前
Rust编写的贪吃蛇小游戏源代码解读
开发语言·后端·rust