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, '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 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:\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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏开发之途

Android AIDL使用详解

5092
来自专栏java学习

Spring4+Spring MVC+MyBatis整合思路

这个很简单,只需要web容器中注册org.springframework.web.context.ContextLoaderListener,并指定spring...

1073
来自专栏互联网开发者交流社区

SpringBoot与Web开发

1464
来自专栏cloudskyme

jbpm5.1介绍(8)

Junit测试或流程 下面的示例中使用的是或流程,看如下流程图 ? 判断节点的值是大于0,大于10还是大于20 看测试程序 public void testI...

3305
来自专栏一个会写诗的程序员的博客

《Spring Boot 2.0 极简教程》附录 I : Spring 5.0 新特性《Spring Boot 2.0 极简教程》附录 I : Spring 5.0 新特性

因为Spring Boot 2.0 是基于Spring Framework 5.0而开发的,所以我们这里对 Spring 5.0的新功能特性做一个简单的介绍。 ...

1843
来自专栏Java与Android技术栈

使用 Kotlin + WebFlux/RxJava 2 实现响应式以及尝试正式版本的协程WebFluxRxJava 2Kotlin 1.3 的 Coroutines总结

在前一篇文章《使用 Kotlin + Spring Boot 进行后端开发》中,曾介绍过尝试使用 Kotlin 来做后端开发。这一次,尝试 WebFlux 以及...

2031
来自专栏JavaEdge

IoC容器系列的设计与实现(基于Spring5.0.4)1 Spring的IoC容器系列2 Spring IoC容器的设计3 BeanFactory的应用场景4 BeanFactory容器的设计原理5

3466
来自专栏小灰灰

Spring之RestTemplate中级使用篇

前面一篇介绍了如何使用RestTemplate发起post和get请求,然而也只能满足一些基本的场景,对于一些特殊的如需要设置请求头,添加认证信息等场景,却没有...

2661
来自专栏犀利豆的技术空间

撸码的福音--变量名生成器的实现

最近换工作以后,结结实实的写了几个月的业务。需求完结以后,就找找自己喜欢的东西写写,换个口味。

4172
来自专栏Java技术栈

Spring Boot 单元测试详解+实战教程

Spring Boot 提供了许多实用工具和注解来帮助测试应用程序,主要包括以下两个模块。

2413

扫码关注云+社区

领取腾讯云代金券