一、背景

目前,公司针对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, 'SOFTWARE\Adobe\Premiere Pro\CurrentVersion', 'Plug-InsDir') then
begin
RegQueryStringValue(HKLM, 'SOFTWARE\Adobe\Premiere Pro\CurrentVersion', '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, 'Software\Adobe\Premiere Pro\9.0\PluginCache.64\zh_CN\PSIParser.dll\Com Module', 'ComFullPath') then
begin
RegQueryStringValue(HKEY_CURRENT_USER, 'Software\Adobe\Premiere Pro\9.0\PluginCache.64\zh_CN\PSIParser.dll\Com 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 Files\Adobe\Common\Plug-ins\7.0\MediaCore';
if RegValueExists(HKLM64, 'SOFTWARE\Adobe\Premiere Pro\CurrentVersion', 'Plug-InsDir') then
begin
RegQueryStringValue(HKLM64, 'SOFTWARE\Adobe\Premiere Pro\CurrentVersion', 'Plug-InsDir', sInstallPath)
end
Result := sInstallPath;
end;

那么,问题的原因是什么呢?实际上问题就出在Win64上。Windows 32bit和64bit版本的注册表稍微有不同。我们知道64bit系统上照样可以跑32bit的程序,因此在注册表上也有区分,特意为32bit程序作了兼容处理。32bit程序对注册表HKEY_LOCAL_MACHINE根下的项目操作都进行了重定向:读取HKEY_LOCAL_MACHINE\SOFTWARE下的键值都会重定向到HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\<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 -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 -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:\WINDOWS\System32目录下时,会自动重定向到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

6、http://windowsitpro.com/systems-management/whats-wow6432node-under-hkeylocalmachinesoftware-registry-subkey

7、http://stackoverflow.com/questions/32727865/how-to-install-sys-file-into-c-windows-system32-drivers-with-inno-setup

inno setup读取注册表遇到的一个坑的更多相关文章

  1. [Inno Setup]写入注册表时32位系统和64位系统的路由

    昨天下午组内一位同事跟说,他想在Inno Setup的安装包中写入一个注册表.目标位置是HKLM:\Software\下面创建自己的注册表项.然后说尝试了好几次都不行, 但是往HKCU下面写入却是OK ...

  2. Inno setup 操作注册表操作参数详解

    原文地址:http://www.dayanzai.me/inno-setup-tut.html [Registry] 段这个可选段用来定义一些你想用安装程序在用户系统中创建.修改或删除的注册表键/值. ...

  3. WinCE下读取注册表获得SD路径

    WinCE下读取注册表获得SD路径 [要点]WinCE注册表中[HKEY_LOCAL_MACHINE\System\StorageManager\Profiles\SDMemory\] 下键Folde ...

  4. C#读取注册表信息

    注册表是视窗系统的一个核心的数据库,在这个数据库中存放中与系统相关的各种参数,这些参数直接控制中系统的启动.硬件的驱动程序安装信息以及在视窗系统上运行的各种应用程序的注册信息等.这就意味着,如果注册表 ...

  5. 用javascript技术读取注册表中软件安装位置并启动本地软件

    1.首先读取注册表中本地软件安装的位置,如果未安装则无就跳转到下载页面. 2.启动软件,关闭页面. 3.如报错提示. <SCRIPT language=javascript>  <! ...

  6. Advanced Installer读取注册表时将Program Files读取为Program Files (x86)的解决办法

    原文:Advanced Installer读取注册表时将Program Files读取为Program Files (x86)的解决办法 今天同事在做安装包的时候,有一个读取注册表路径的需求,需要根据 ...

  7. 读取注册表获取Windows系统XP/7/8/10类型(使用wcscmp比较wchar[]内容)

    很多方案是采用GetVersion.GetVersionEx这两个API来查询操作系统的版本号来判断当前的操作系统是Windows系列中的哪个,在Win10没有出现前,这种方法是行的通的,但是Win1 ...

  8. c++读取注册表的实例

    // CRegisterTest.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <iostream> #in ...

  9. Java Windows下读取注册表的工具类

    import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import ...

随机推荐

  1. SDOI 2016 数字配对

    题目大意:给定n个数字以及每个数字的个数和权值,将满足条件的数字配对,使得总代价不小于0,且配对最多 最大费用最大流拆点,对于每个点,连一条由S到该点的边,容量为b,花费为0,再连一条到T的边 对于每 ...

  2. 微软开源.NET Core的执行引擎CoreCLR{转载}

    继去年12月宣布.NET Core开源之后,微软拥抱开源的决心又向前迈了一步,Microsoft于昨日在 .NET Framework Blog上 宣布开源.NET Core 的执行引擎 CoreCL ...

  3. TDDL分库分表规则

    规则如下: 判断一个ID在哪个库里的公式是:id % 4 / 2判断一个ID在哪个表里的公式是:id % 4 % 2 其中4表示总共有多少个分表,2表示总共有多少个数据库:上面这个例子,表示总共有2个 ...

  4. Guava Supplier实例

    今天想讲一下Guava Suppliers的几点用法.Guava Suppliers的主要功能是创建包裹的单例对象,通过get方法可以获取对象的值.每次获取的对象都为同一个对象,但你和单例模式有所区别 ...

  5. lua中实现异步资源读写

    同样还是更新方面的需求,当我们检测到版本是新安装的以后,要进行upd目录清除.如果使用os.execute执行 rm -rf ooxx 是非常快的但由于os.execute一旦报错,那整个lua进程就 ...

  6. C#编程模式之扩展命令

    C#编程模式之扩展命令 前言 根据上一篇的命令模式和在工作中遇到的一些实际情况,有了本篇文章,时时都是学习的一个过程,会在这个过程中发现许多好的模式或者是一种开发方式,今天写出来的就是我工作中常用到的 ...

  7. Pycharm5注册方式

    0x1 ,安装 0x2 , 调整时间到2038年. 0x3 ,申请30天试用 0x4, 退出pycharm 0x5, 时间调整回来. ##注册方法2### 注册方法:    在 注册时选择 Licen ...

  8. VS2013的 Browser Link 引起的问题

    环境:vs2013 问题:在调用一个WebApi的时候出现了错误: 于是我用Fiddler 4直接调用这个WebApi,状态码是200(正常的),JSon里却提示在位置9409处文本非法, 以Text ...

  9. Java学习笔记(03)

    一.回顾运算符: 一.控制语句 1.1 顺序结构 (最常见的) 特点:代码从上往下依次执行

  10. 研究一下javascript的模块规范(CommonJs/AMD/CMD)

    最近写react需要使用nodejs作为开发环境,需要通过npm安装一些第三方的依赖库,因此慢慢感觉到nodejs基础薄弱对我带来了一些不安全感,尤其是javascript模块这一块听到了很多概念,比 ...