inno setup读取注册表遇到的一个坑
一、背景
目前,公司针对PR开发的一个插件需要发布到64位系统上。该插件包括一个prm格式的文件和若干个DLL文件。其中,prm文件需要复制到PR公共插件目录下,DLL需要复制到Windows系统目录中去,这样插件才能正常的工作。公司现在要求发布插件时制作一个安装包,让用户点击安装包后自动将插件相关文件拷贝到相应目录去。本来用inno setup来做一个安装包,顶多就是一个多目录安装的问题。但是,公司发布的插件只能应用在Win64位平台,而且要求通过读取注册表来确定具体的安装目录。这是背景,也正是本文要说坑。
二、分析过程
本来拿到需求一想还挺简单的,本来就详细看过Inno setup的文档,读取注册表只需要调用RegQueryStringValue()即可读取注册表中指定项目。代码就这么写:
function GetInstallString(): String;
var
sInstallPath: String;
begin
sInstallPath := '';
if RegValueExists(HKLM, 'SOFTWAREAdobePremiere ProCurrentVersion', 'Plug-InsDir') then
begin
RegQueryStringValue(HKLM, 'SOFTWAREAdobePremiere ProCurrentVersion', 'Plug-InsDir', sInstallPath)
end
Result := sInstallPath;
end;
逻辑很简单:先判断这个键是否存在,存在就继续读取键值。编译成功生成安装包。可是一运行问题来了,根本就没有获取到键值。注册表打开着对着写的,路径也反复看了好几遍是错不了的。但是为什么就读取不到具体的键值呢?网上资料不多,大多使用的例子也是如上并无二般。那是为什么呢?那么其他的键值能否读到呢?还是说只是读取不到这个键值?于是变换着,换到一个其他的ROOT下,如HKEY_USERS或者HKEY_CLASSES_ROOT:
function GetInstallString(): String;
var
sInstallPath: String;
begin
sInstallPath := '';
if RegValueExists(HKEY_CURRENT_USER, 'SoftwareAdobePremiere Pro9.0PluginCache.64zh_CNPSIParser.dllCom Module', 'ComFullPath') then
begin
RegQueryStringValue(HKEY_CURRENT_USER, 'SoftwareAdobePremiere Pro9.0PluginCache.64zh_CNPSIParser.dllCom Module', 'ComFullPath', sInstallPath)
end
Result := sInstallPath;
end;
编译运行正常获得注册表中的键值。再换成HKEY_USERS下的其他键值同样获取成功。那么HKEY_LOCAL_MACHINE下的其他键值是否能获取得到呢?随便找了个键值项,发现也无法获得到键值。那么问题就来了:其他ROOT下的键值都可以获得成功,唯独HKEY_LOCAL_MACHINE下的键值获取不到,这是为什么呢?在网上搜了搜,看到了一个有用的帖子:http://stackoverflow.com/questions/4033976/inno-setup-doesnt-allow-access-to-all-registry-keys-why。通篇读下来,发现了一种解决方法,只要在ROOT名称后跟上64即可解决问题:
function GetInstallString(): String;
var
sInstallPath: String;
begin
sInstallPath := 'C:Program FilesAdobeCommonPlug-ins7.0MediaCore';
if RegValueExists(HKLM64, 'SOFTWAREAdobePremiere ProCurrentVersion', 'Plug-InsDir') then
begin
RegQueryStringValue(HKLM64, 'SOFTWAREAdobePremiere ProCurrentVersion', 'Plug-InsDir', sInstallPath)
end
Result := sInstallPath;
end;
那么,问题的原因是什么呢?实际上问题就出在Win64上。Windows 32bit和64bit版本的注册表稍微有不同。我们知道64bit系统上照样可以跑32bit的程序,因此在注册表上也有区分,特意为32bit程序作了兼容处理。32bit程序对注册表HKEY_LOCAL_MACHINE根下的项目操作都进行了重定向:读取HKEY_LOCAL_MACHINESOFTWARE下的键值都会重定向到HKEY_LOCAL_MACHINESOFTWAREWow6432Node<company><product>。那么这就很好解释了,Premiere Pro目前只能运行在64Bit系统上,因此在Wow6432Node下是不会存在记录的,读取相应键值自然会失败。
通过HKLM32和HKLM64明确指出读取的具体位置,就可以避免上述这种问题了。事实上,在inno setup的说明文档中还有另外一种方法可以尝试,也可以避免64bit系统产生的问题。具体代码:
var
OldState: Boolean;
ResultCode: Integer;
begin
// First verify that the user is running a supported 64-bit version
// of Windows, because calling EnableFsRedirection(False) will
// raise an exception otherwise.
if IsWin64 then
begin
// Turn off redirection, so that cmd.exe from the 64-bit System
// directory is launched.
OldState := EnableFsRedirection(False);
try
Exec(ExpandConstant('{cmd}'), '', '', SW_SHOW,
ewWaitUntilTerminated, ResultCode);
finally
// Restore the previous redirection state.
EnableFsRedirection(OldState);
end;
end;
end;
关键就是通过调用EnableFsRedirection()函数来禁用注册表操作转发行为。在调用之前先判断当前安装程序是否是运行在64位系统上。
Update 2016-3-7:
在64Bit系统上,将动态库文件拷贝到C:WINDOWSSystem32目录下时,会自动重定向到SysWOW64目录下,导致程序运行异常。这事可以通过设置在[Files]段设置Flags:64即可禁用目录重定向(参考链接)。
三、参考链接
1、http://www.jrsoftware.org/ishelp/index.php?topic=isxfunc_regquerystringvalue
2、http://www.jrsoftware.org/ishelp/index.php?topic=consts
3、http://stackoverflow.com/questions/4033976/inno-setup-doesnt-allow-access-to-all-registry-keys-why
4、http://www.jrsoftware.org/ishelp/index.php?topic=64bitlimitations
5、http://www.jrsoftware.org/ishelp/index.php?topic=isxfunc_enablefsredirection
- 如何使用Zookeeper实现HiveServer2的HA
- Python 统计个人加密货币资产
- 如何使用HAProxy实现HiveServer2负载均衡
- 机器学习之决策树算法
- 如何使用HAProxy实现Impala的负载均衡
- 解读GEO数据存放规律及下载,一文就够
- 从GEO数据库下载得到表达矩阵 一文就够
- 机器学习各类算法比较
- Kerberos环境下删除ZooKeeper服务注册信息问题分析
- 根据分组信息做差异分析- 这个一文不够的
- 如何使用Cloudera Manager启用YARN的HA
- 如何使用java代码通过JDBC连接Hive(附github源码)
- 差异分析得到的结果注释一文就够
- 如何使用Cloudera Manager禁用HDFS HA
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- CentOS6.5安装python3.7详细步骤
- Centos7.5配置IP地址的实现
- linux里daily_routine实例代码详解
- 类Linux环境安装jdk1.8及环境变量配置详解
- CentOS7yum安装PHP7.2的操作方法
- CentOS 7中 Apache Web 服务器安装配置教程
- Win10安装Linux系统的教程图解
- 浅谈ubuntu执行.sh文件几种方式区别
- CentOS7使用yum安装PostgreSQL和PostGIS的方法
- Linux Windows下设置定时执行任务的方法
- 详解ssh免密码登录配置方法(图示加命令)
- centos 7 修改sshd | 禁止 root登录及sshd端口脚本定义
- Linux 全能系统监控工具dstat的实例详解
- 解决-BASH: /HOME/JAVA/JDK1.8.0_221/BIN/JAVA: 权限不够问题
- linux常用工具vi/vim的使用完整版