如何在Electron中调用Dll
如何在Electron中调用Dll
客户端有些硬件的接口需要调试,是在电脑上连了一些硬件的设备,比如打印机、扫描仪或者进行串口通信等等。单靠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不一样,那么调用方式也不一样。我是用到了这两个模块,ffi和edge,使用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 Integeruint8 Unsigned 8-bit Integerint16 Signed 16-bit Integeruint16 Unsigned 16-bit Integerint32 Signed 32-bit Integeruint32 Unsigned 32-bit Integerint64 Signed 64-bit Integeruint64 Unsigned 64-bit Integerfloat Single Precision Floating Point Number (float)double Double Precision Floating Point Number (double)pointer Pointer Typestring Null-Terminated String (char *)常见的C语言类型byte unsigned charchar charuchar unsigned charshort shortushort unsigned shortint intuint unsigned intlong longulong unsigned longlonglong longulonglong unsigned long longsize_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,按下面几步执行即可- npm install -g node-gyp
- 安装Python2.7和 Visual Studio Build Tools 或者VS2017
- npm config set msvs_version 2017
- node-gyp –python 你当前Python安装的路径
- cd node_modules/edge
- node-gyp configure
- node-gyp build
- 如果觉得麻烦可以直接使用electron-edge-js就不用自己重新编译了,如果还是不行,就再装一下electron-rebuild然后执行
.\node_modules\.bin\electron-rebuild.cmd

浙公网安备 33010602011771号