如何在Electron中调用Dll

更新日期: 2019-06-08阅读: 3.5k标签: Electron

客户端有些硬件的接口需要调试,是在电脑上连了一些硬件的设备,比如打印机、扫描仪或者进行串口通信等等。单靠JS是完成不了了,我们决定通过把C++或者C#把这些功能打包成Dll,然后在Electron客户端中通过Node调用Dll来实现所需要的功能。


Dll类型

先简单说一下什么是Dll,Dll是动态链接库文件,也是一种代码库的形式,与静态链接库相比,它是在每次程序运行的时候去调用,而静态链接库指令都会被打包到最后的exe文件里,所以如果函数有什么变化那就需要重新生成exe,那动态链接库就不需要这么做了。生成Dll可以通过VS来完成,可以选择使用C#或者C++开发,C#开发界面的比较方便,如果你的功能需要弹出一些界面,那就要用C#编写相应的Dll。不过这里要注意了,用C#语言编写生成的Dll和用C++语言编写生成的Dll是不一样的,通过C#生成的Dll需要.net的开发环境,而C++生成的Dll就没有限制。


Node如何调用Dll

Electron里调用Dll其实就是node调用Dll,刚才说了,生成的Dll不一样,那么调用方式也不一样。我是用到了这两个模块,ffiedge,使用ffi调用C++生成的Dll,使用edge调用C#生成的Dll。

ffi调用Dll

比如我这里有个ffiTest.dll的文件,里面有个导出的函数叫做joinStr,就是暴露的方法,给定两个字符串,然后会返回这两个参数的拼接结果。注意C++生成的Dll要使用C风格extern “C”否则可能找不到对应的方法名。

var ffi = require('ffi');
var path = require('path');

var dllPath = path.resolve('ffiTest.dll');

var lib = ffi.Library(dllPath, {
    'joinStr': ['string', ['string', 'string']],
})

var result = lib.joinStr('hello', 'world');
console.log(result); //打印 helloworld

更详细的示例可以参考它的教程。ffi.Library里第二个参数是一个Json结构,key表示是方法名,value示一个数组,数组的第一个参数是返回值类型,第二个参数是方法的列表,如果返回值是空的话,那数组第一个参数应该是void。如果返回值或者参数类型不知道是什么类型就写void*。要使用ffi中的类型表示C/C++语言中的类型,对照表如下

基本类型
int8        Signed 8-bit Integer
uint8       Unsigned 8-bit Integer
int16       Signed 16-bit Integer
uint16      Unsigned 16-bit Integer
int32       Signed 32-bit Integer
uint32      Unsigned 32-bit Integer
int64       Signed 64-bit Integer
uint64      Unsigned 64-bit Integer
float       Single Precision Floating Point Number (float)
double      Double Precision Floating Point Number (double)
pointer     Pointer Type
string      Null-Terminated String (char *)

常见的C语言类型
byte        unsigned char
char        char
uchar       unsigned char
short       short
ushort      unsigned short
int         int
uint        unsigned int
long        long
ulong       unsigned long
longlong    long
ulonglong   unsigned long long
size_t      platform-dependent, usually pointer size

如果是指针类型,可以利用ref模块来表示

var ref = require('ref');
var refArray = require('ref-array');

var intPtr = ref.refType('int'); //int*类型
var charPtr = 'hello'; //char*可以用string表示

//如果是个字符数组
var refArray = require('ref-array');
var charPtrPtr = refArray(ref.types.char, 50); //50个大小的数组

假如参数或者返回值是一个结构体,那就需要借助ref-struct模块来表示

var ref = require('ref');
var FFI = require('ffi');
var Struct = require('ref-struct');

var TimeVal = Struct({
  'tv_sec': 'long',
  'tv_usec': 'long'
});
var TimeValPtr = ref.refType(TimeVal);
var lib = new FFI.Library(null, { 
    'gettimeofday': ['int', [TimeValPtr, 'pointer']]
});
var tv = new TimeVal();
lib.gettimeofday(tv.ref(), null);
console.log("Seconds since epoch: " + tv.tv_sec);
edge调用Dll

edge这个模块非常强大,不仅可以在node中编写C#的代码也可以在C#中调用node的代码,它要求有一个.net4.5或者更高版本的环境。C#编写的Dll要通过async修饰后才能被node调用,大致像是这样

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TestDll
{
    public class StartUp
    {
        public async Task<object> Invoke(object param)
        {
            return "Hello World!";  
        }
    }
}

这样会生成一个TestDll.dll的文件,在node中

var edge = require('edge');
var path = require('path');

var driver = edge.func({
    assemblyFile: path.resolve('TestDll.dll'),
    typeName: 'TestDll.StartUp',
    methodName: 'Invoke'
})

//还可以这么写,var driver = edge.func(path.resolve('TestDll.dll'))
//这么写默认方法名就是Invoke,C#中class的名字就是StartUp。如果不一致的话调用就会报错

driver(null, function(err,result) {
    if (err) {
        throw err; 
    } else {  
        console.log(result);
    }
});

利用edge其实可以在js直接编写C#的代码,那完全不用多个步骤还要去生成Dll了,但是这个项目里还依赖了别的Dll,这个语法还是有点懵,搞清楚后再试试直接写C#代码试试。


遇到的问题

过程总是那么地不顺利,即便知道了语法怎么写也会出现一些问题,总结了下大概是以下几种

  • win32 error 126 Dll文件的路径写错了,或者Dll有相关的依赖,依赖没有放在与入口Dll在同一级目录下
  • win32 error 127 ffi定义的函数名、返回值类型或者参数类型与Dll定义的不一致
  • win32 error 193 Dll与当前的操作系统不匹配,当前系统是64位的Dll是32位的
  • 在Electron的项目使用edge无法编译 edge是一个原生的模块需要用你当前安装node的版本重新编译,重新编译需要使用node-gyp,按下面几步执行即可

    1. npm install -g node-gyp
    2. 安装Python2.7 Visual Studio Build Tools 或者VS2017
    3. npm config set msvs_version 2017
    4. node-gyp --python 你当前Python安装的路径
    5. cd node_modules/edge
    6. node-gyp configure
    7. node-gyp build
    8. 如果觉得麻烦可以直接使用electron-edge-js就不用自己重新编译了,如果还是不行,就再装一下electron-rebuild然后执行.\node_modules\.bin\electron-rebuild.cmd

原文来自:https://segmentfault.com/a/1190000019402908




链接: https://fly63.com/article/detial/3574

使用 Electron 打包 Vue 项目

新建一个 Vue 项目,安装 electron-builder,Electron 的main进程和renderer进程分开进行开发,可以通过ipcMain和ipcRenderer进行通信,数据流清晰,开发简单。

electron阻止应用关闭

这里直接监听onbeforeunload事件,每当页面刷新或关闭时,都会触发这个事件。方法二:我们在main.js中监听close事件。定义一个flag标识是否可以关闭。如果不可以关闭,则阻止该事件。

electron_pcMain模块、ipcRenderer模块

ipcMain模块是EventEmitter类的一个实例。 当在主进程中使用时,它处理从渲染器进程(网页)发送出来的异步和同步信息。 从渲染器进程发送的消息将被发送到该模块。

electron将web应用构建跨平台桌面应用

Electron是由Github开发,用HTML,CSS和JavaScript来构建跨平台桌面应用程序的一个开源库。 Electron通过将Chromium和Node.js合并到同一个运行时环境中,并将其打包为Mac,Windows和Linux系统下的应用来实现这一目的。

Electron怎么启动并行的子任务

有些场景下比如要处理一大堆文件内容的查找,字符串的替换,文件的修改等一系列耗时操作的时候,如果放在主进程执行那必然会导致渲染进程的阻塞

Electron Windows增加托盘悬浮框功能

在做Electron Windows 桌面应用时候,做鼠标悬浮到托盘图标上时显示一个悬浮框(例如做消息提醒),但因为Windows没有提供托盘mouse-enter/mouse-leave事件,无法直接做这个功能,考虑到还有mouse-move事件,弄个间接的方式实现。

Electron应用打包、自动升级

使用electron builder打包只需要在vue.config.js中配置即可,这里需要注意的是,默认情况下electron builder打包出来的安装程序是不能修改安装目录的,需要allowToChangeInstallationDirectory这个配置设置为true

基于Electron开发Hosts切换工具的“踩坑”之旅

用过好几个Hosts切换工具,但总是有点这样那样的问题。最讨厌的莫过于切换完后,键盘都快按坏了,浏览器里面的Hosts就是不变,网上找了好多方法,但是感觉都并不完美,于是就有了这篇文章。

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!