获取Electron注册表信息

Published: 2018-08-08

Tags: Electron Rust

本文总阅读量

之前做Electron开机自启动(Windows)的时候用到了auto-launch包,当时程序安装路径是写死的,如果安装的时候手动选择路径,开机启动就会无效

今天按照计划填坑,修改从注册表获取程序安装路径,本篇文章记录两项内容——获取Electron程序信息的注册表位置,从注册表中获取Electron的安装路径

(一)获取Electron程序信息的注册表位置

程序使用electron-builder打包,之前一直很困惑,因为程序安装后创建的注册表位置是类似这样的路径

HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\50c1930e-15ef-5300-9367-f5b10795c0e4

问题随之出现,后边的50c1930e-15ef-5300-9367-f5b10795c0e4是builder随机生成的,我如果在之后获取这个值呢,刚开始的思路是在installer.nsh中看看能不能手动修改一下INSTALL_REGISTRY_KEY发现不行,偶然间查看文档,发现可以手动设置guid(文档不推荐手动设置,但是自己清楚想要做什么就完全没问题了)

"nsis": {
  "shortcutName": "desktop-icon-name",
  "guid": "50c1930e-15ef-5300-9367-f5b10795c0e4",
  "oneClick": false,
  "perMachine": true,
  "allowToChangeInstallationDirectory": true
}

设置好guid后,electron-builder在构建应用的时候就会使用这个指定值了

PS:这个UUID是v5版本的,如果你用Python,可以用下面命令来生成这个UUID

# -*- coding: utf-8 -*-
import uuid

print(uuid.uuid5(uuid.NAMESPACE_DNS, 'somestring'))

(二)从注册表中获取Electron的安装路径

知道了安装程序的注册表位置,修改注册表的东西就方便多了,比如换个控制面板里应用的版本号

Node.js获取/修改注册表值的库我找到两个——regeditwindows-registry

使用regedit代码如下:

获取安装路径:

regedit
    .list(['HKLM\\SOFTWARE\\50c1930e-15ef-5300-9367-f5b10795c0e4'])
    .on('data', function(entry) {
      console.log(entry.data);

      let program_location = entry.data.values.InstallLocation.value + '\\your-exe.exe';
      console.log(program_location);

    })
    .on('finish', function() {
      console.log('Get location from regedit finished~');
    });

这样写后不久打包测试,发现打成asar包的时候运行会报错,不贴图片了,报错与这个Issues很像

但是问题已经被解决关闭了,不知道为什么我还会遇到这个问题

于是想着看看有没有别获取注册表值的方式

var Key = require('windows-registry').Key;
var windef = require('windows-registry').windef;

var key = new Key(windef.HKEY.HKEY_LOCAL_MACHINE, 'SOFTWARE\\50c1930e-15ef-5300-9367-f5b10795c0e4', windef.KEY_ACCESS.KEY_ALL_ACCESS);
var value = key.getValue('InstallLocation');
console.log(value);

换用了windows-registry后,代码看起来清爽了不少,接着就出项了另一个问题,这个包用到了ffi... 于是在Electron下使用,妥妥的需要再编译一下

cd ./node_modules/ffi/ && node-gyp rebuild --target=2.0.0 --arch=ia32 --target_arch=ia32 --dist-url=https://atom.io/download/electron && cd ../ref && node-gyp rebuild --target=2.0.0 --arch=ia32 --target_arch=ia32 --dist-url=https://atom.io/download/electron

在项目根目录运行即可。可能遇到下载失败,请看(三),另外编译需要用到VS编译器,Python等

(三)编译需要用到的头文件等

编译过程中可能遇到网网络问题(ETIMEOUT 错误), 导致从https://atom.io/download/electron下载包失败,我看了下我的缓存目录,正好有几个版本(1.8.2, 1.8.7, 2.0.0, 2.0.2)

下载:【iojs-1.8.2.zip】 【iojs-1.8.7.zip】 【iojs-2.0.0.zip】 【iojs-2.0.2.zip】

下载后解压缩放在目录:C:\Users\Administrator\.node-gyp\

比如:C:\Users\Administrator\.node-gyp\iojs-2.0.2路径下能看到installVersion文件即可,然后编译就会使用缓存,另外,我发现一个目录C:\Users\Administrator\.electron-gyp

这个里面的内容与.node-gyp是一样的(我看了对应版本lib的md5),我不确定是谁用到了它,可能是electron-builder在打包时自动编译使用?未验证

(四)Rust写的修改注册表值的程序

为了增加软件的物种多样性(其实是闲的),我之前用Rust的包做了修改注册表的程序,有时间还是修改回npm包,省得还得用child_process...

控制面板里Electron程序显示内容是在Uninstall项里(Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\50c1930e-15ef-5300-9367-f5b10795c0e4

extern crate winreg;
use std::path::Path;
use winreg::RegKey;
use winreg::enums::*;

fn main() {

    let args: Vec<_> = std::env::args().collect();
    if args.len() < 3 {
        println!("Usage: {} <guid_string> <version>",
                 args[0]);
        std::process::exit(1);
    }

    let guid_string = &args[1];
    let version = &args[2];

    println!("guid_string: {}", guid_string);
    println!("version: {}", version);

    let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);
    let path = Path::new("Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall").join(guid_string);
    let key = hklm.create_subkey(&path).unwrap();


    println!("====== Before ======");
    let old_name: String = key.get_value("DisplayName").unwrap();
    println!("DisplayName = {}", old_name);
    let old_version: String = key.get_value("DisplayVersion").unwrap();
    println!("DisplayVersion = {}", old_version);

    let new_name: String = old_name.replace(&old_version, &version);

    key.set_value("DisplayName", &new_name).unwrap();
    key.set_value("DisplayVersion", version).unwrap();

    println!("====== After ======");
    let sz_val: String = key.get_value("DisplayName").unwrap();
    println!("DisplayName = {}", sz_val);
    let sz_val: String = key.get_value("DisplayVersion").unwrap();
    println!("DisplayVersion = {}", sz_val);
}

写在最后,使用regedit包方便很多,不用编译,也许再琢磨一下可以解决打包asar后报错的问题,先用它试试,可能我的环境导致的问题,以后有时间再看