InnoSetup,扩展环境变量(使用 {reg:...} 从注册表值中获取)

InnoSetup, expand environment variable (taken from registry value using {reg:...} )

我尝试从注册表设置默认安装路径:

DefaultDirName={reg:HKCU\Software\Microsoft\VisualStudio.0,VisualStudioLocation|{userdocs}\Visual Studio 2015}

我希望得到的目录路径是注册表值数据,是一个REG_EXPAND_SZ类型的值,那么我需要展开它的变量,我的例子中的reg值指向与我设置的默认值相同的路径,一旦 {userdocs} 常量在运行时被 InnoSetup 扩展,应该是这样的:

C:\Users\Administrator\Documents\Visual Studio 2015

但是我没有得到它作为目录路径:

C:\Users\Administrator\Desktop\%USERPROFILE%\Documents\Visual Studio 2015

我从“C:\Users\Administrator\Desktop”路径执行安装程序,所以这里似乎发生了两件事,第一是注册表值的路径是只是附加,第二个当然是 %USERPROFILE% 变量没有展开。

我怎样才能正确地做到这一点?

我在 Inno Setup 源代码中找不到 ExpandEnvironmentStrings 函数的任何用法,它指向一个事实(如果我错了请纠正我),即 Inno Setup 无法扩展这样的路径(没有函数,也不是常量),或者有一个我不知道的不同的 API 函数。当然 Inno Setup 支持这样的文件名,因为它们被传递给可以在内部扩展它们的系统函数。似乎没有函数或常量可以在脚本中完成。我的建议是像这样 hack

[Setup]
AppName=My Program
AppVersion=1.5
DefaultDirName={code:GetDefaultDirName}

[Code]
#ifdef UNICODE
  #define AW "W"
#else
  #define AW "A"
#endif

const
  RegKeyVS2015 = 'Software\Microsoft\VisualStudio.0';

function ExpandEnvironmentStrings(lpSrc: string; lpDst: string; nSize: DWORD): DWORD;
  external 'ExpandEnvironmentStrings{#AW}@kernel32.dll stdcall';

function ExpandEnvVars(const Input: string): string;
var
  BufSize: DWORD;
begin
  BufSize := ExpandEnvironmentStrings(Input, #0, 0);
  if BufSize > 0 then
  begin
    SetLength(Result, BufSize);
    if ExpandEnvironmentStrings(Input, Result, Length(Result)) = 0 then
      RaiseException(Format('Expanding env. strings failed. %s', [
        SysErrorMessage(DLLGetLastError)]));
  end
  else
    RaiseException(Format('Expanding env. strings failed. %s', [
      SysErrorMessage(DLLGetLastError)]));
end;

function GetDefaultDirName(Param: string): string;
begin
  if RegQueryStringValue(HKCU, RegKeyVS2015, 'VisualStudioLocation', Result) then
    Result := ExpandEnvVars(Result)
  else
    Result := ExpandConstant('{userdocs}\Visual Studio 2015');
end;

以下是我对 ElektroStudios 解决方案的改进版本:

它负责正确的字符串终止,而不依赖于 Win32 函数添加的 0 终止(我猜在 Pascal 代码中使用它并不好)。

[Code]
#ifdef UNICODE
#define AW "W"
#else
#define AW "A"
#endif

function ExpandEnvironmentStrings(lpSrc: String; lpDst: String; nSize: DWORD): DWORD;
external 'ExpandEnvironmentStrings{#AW}@kernel32.dll stdcall';

function ExpandEnvVars(const Input: String): String;
var
  Buf: String;
  BufSize: DWORD;
begin
  BufSize := ExpandEnvironmentStrings(Input, #0, 0);
  if BufSize > 0 then
  begin
    SetLength(Buf, BufSize);  // The internal representation is probably +1 (0-termination)
    if ExpandEnvironmentStrings(Input, Buf, BufSize) = 0 then
      RaiseException(Format('Expanding env. strings failed. %s', [SysErrorMessage(DLLGetLastError)]));
#if AW == "A"
    Result := Copy(Buf, 1, BufSize - 2);
#else
    Result := Copy(Buf, 1, BufSize - 1);
#endif
  end
  else
    RaiseException(Format('Expanding env. strings failed. %s', [SysErrorMessage(DLLGetLastError)]));
end;