1.前言
在之前的文章中,实现了Electron-vue在不同系统打包成安装程序。但这只是前端build/package之后的文件打包,虽然服务端的编译之后的exe文件也可以放到一起打包,并且可以去启动服务端程序。然而不能与服务端通信的话,那么这个程序存在的意义就不大。所以在这片文章中会讲一下怎么在安装之后,启动应用程序调用服务端程序,同时获取服务端的输出值/返回值。
由于不是传统意义上的前后端通信(常见的前后端通信,只需要使用http/https进行通信即可,request发送请求,response返回请求结果,然后解析一顿操作即可),然而当服务端打成exe可执行文件时,就需要使用command去通信,同时需要借助进程之间的通信方式来处理。
2.nodejs实现exe调用
2.1主进程-子进程-渲染进程
我们知道在现在的前端项目构建中,尤其是工程化的项目创建,都会依赖nodejs
,而nodejs
本身也足够强大提供一些常用的方法来为js服务。而在我们的Electron-vue
项目中,也是需要使用nodejs
提供的一些服务去实现进程之间的通信。
在这里,主要分为主进程,子进程和渲染进程。
主进程与子进程之间的通信(也就是exe的调用和执行),使用的是nodejs
提供的child_process
实现。而主进程和渲染进程之间的通信使用的是electron中的BrowserWindow
来实现。
主要的逻辑处理是:
- 主进程通过nodejs提供的child_process来实现启动子进程,并且获取子进程的输出值
- 主进程获取子进程输出值,将输出值传给渲染进程
- 渲染进程获取到主进程的数据值,将其呈现在页面上,比如子进程是否启动成功等
2.2主进程启动子进程
主进程启动子进程可借助于nodejs
的child_process
提供的方法。
child_process
提供了七中方式去创建子进程
异步方式
- child_process.exec(command[, options][, callback])
- child_process.execFile(file[, args][, options][, callback])
- child_process.fork(modulePath[, args][, options])
- child_process.spawn(command[, args][, options])
同步方式
- child_process.execFileSync(file[, args][, options])
- child_process.execSync(command[, options])
- child_process.spawnSync(command[, args][, options])
我们知道同步方式会导致进程阻塞,所以我们一般会使用异步进程的方式去处理。
从命令的方式也可以看出其中exec
,spawn
的参数是command
也就是命令行的形式去执行,而我的需求也是使用命令行启动一个exe
文件基本上是start xxx.exe
。所以在这里也只讲一下exec
和spawn
。
从官方的文档可以看得出,spawn
是最基本的,而exec
是对于spawn
的一种封装或者说是扩展,意思是说spawn
会基于命令command
直接执行,而exec
会衍生 shell
,然后在 shell
中执行 command
,并缓冲任何产生的输出。 传给 exec
函数的 command
字符串会被 shell
直接处理,特殊字符(因 shell 而异)需要被相应地处理
2.2.1 exec执行exe文件
child_process.exec(command[, options][, callback])
上面的一些参数可以参考exec command。在这里我只强调两个属性值,那就是timeout
和maxBuffer
。
为什么要强调这个属性呢?是因为我在使用exec执行命令的时候,exe
文件可以执行,但是无论如何怎么都进不去callback
函数,除非你设置timeout
参数不为0
。
为了开发方便便于debug
主程序的运行,借助于electron-log
输出主程序的日志文件,以便定位执行exe
程序时是否有输出返回值。
1、配置日志文件
安装electron-log
,因为日志只是为了方便我们在开发环境下做问题定位,所以,需要开发依赖,而不是生产依赖。
npm install electron-log --save-dev
2、在main/index.js
中配置日志
import log from 'electron-log'
//配置输出路径,这里为了方便我就直接输出到开发项目根目录下
log.transports.file.file = "D://my-project/agent.log";
3、创建exec方法
在main/index.js
文件中创建执行程序的方法
const cp = require('child_process')
function execPrograme() {
log.info("开始执行-----------------------------")
cp.exec(`start ${
__dirname}/main.exe`,(error, stdout, stderr) => {
if (error) {
log.error(`执行的错误: ${
error}`);
return;
}
log.info(`stdout: ${
stdout}`);
log.error(`stderr: ${
stderr}`);
})
log.info("结束执行-----------------------------")
}
然后观察一下控制台会发现vc code终端输出的数据仅有开始和结束的输出,而没有callback
中的输出,得不到我想要的exe执行结果。
而在日志文件中也可以看到:
这个问题我搜了半天一直在关注为什么exec
命令为什么走不进去callback
函数中,在网上找到一大堆方法,结果没有可行的,后来还是在官网api上找到了解决办法:
timeout 默认值: 0。
为0是什么意思呢?官网没有给出解释,而是给出了大于0的时候的解释
如果 timeout 大于 0,则当子进程运行时间超过 timeout 毫秒时,父进程会发送由 killSignal 属性(默认为 ‘SIGTERM’)标识的信号
看了看上面的解释,似乎还是没明白。大于0会发出killSignal
的信号,这个意思难道就是杀死进程?为0的话进程就一直在执行,导致没法进入到callback
回调函数中。
那如果我设置一个大于0的值呢?
function execPrograme() {
log.info("开始执行-----------------------------")
cp.exec(`start ${
__dirname}/main.exe`,