InnoSetup, expand environment variable (taken from

2020-04-11 05:30发布

问题:

I try to set the default install path from the registry:

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

The directory path that I expect to obtain is the registry value data, which is a value of type REG_EXPAND_SZ then I need to expand its variable(s), the reg value in my case points to the same path as the default value I set, which, once the {userdocs} constant is expanded at runtime by InnoSetup, should be this:

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

But instead of that i get this as directory path:

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

I execute the installer from "C:\Users\Administrator\Desktop" path, so here seems to happens two things, the first is that the path of the registry value is just appended, and the second is that of course the %USERPROFILE% variable is not expanded.

How I could do this properly?.

回答1:

I cannot find any use of the ExpandEnvironmentStrings function in Inno Setup source which points to a fact (and correct me if I am wrong), that Inno Setup cannot expand such path (there is no function, nor constant for that), or there's a different API function that I'm not aware of. Of course file names like that are supported in Inno Setup because they're passed to the system functions that can internally expand them. There just seems to be no function or constant that could do it in script. My suggestion is a hack like this:

[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\14.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;


回答2:

Here is my improved version of ElektroStudios' solution:

It takes care of correct string-termination and does not rely on the 0-termination added by the Win32-function (guess it's not good to use that in Pascal code).

[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;