【问题标题】:Inno Setup: Reading a file from installer during uninstallationInno Setup:在卸载期间从安装程序中读取文件
【发布时间】:2016-05-31 17:38:54
【问题描述】:

在卸载过程中使用以下代码

BitmapImage := TBitmapImage.Create(InstallTopPanel);
BitmapImage.AutoSize := True;
BitmapImage.Bitmap.LoadFromFile(
  ExpandConstant( '{tmp}\WizardSmallImageFile.bmp') );
BitmapImage.Parent := InstallTopPanel;
BitmapImage.Top := (InstallTopPanel.ClientHeight - 58) / 2;
BitmapImage.Left := InstallTopPanel.ClientWidth - 55 - 10;

我收到一个错误:

异常:无法打开文件。 C:\users\xyz\AppData\Local\Temp\is-U3Q8P.tmp\WizardSmallImageFile.Bmp。 找不到文件。

在我调用LoadFromFile 之前,我也尝试使用ExtractTemporaryFile,这在卸载期间不受支持。

ExtractTemporaryFile('WizardSmallImageFile.bmp');

那么,问题来了,如何在卸载过程中查看图像或特别是WizardSmallImageFile

我上面的代码构建了一个带有自定义面板的自定义表单。喜欢这里:Inno Setup Uninstall some components only

【问题讨论】:

    标签: inno-setup pascalscript


    【解决方案1】:

    正确,ExtractTemporaryFile 从安装程序中提取文件。因此它不能在卸载程序中工作,因为安装程序不再可用。

    另外请注意,无论如何您都无法从安装程序中提取WizardSmallImageFile directive 所引用的文件。您必须添加自己的副本。


    如果您在卸载过程中需要使用某些文件,则必须在安装程序中安装它,然后在卸载程序中使用已安装的副本。

    [Files]
    Source: "WizardSmallImageFile.bmp"; DestDir: "{app}";
    
    [Code]
    
    function InitializeUninstall(): Boolean;
    begin
      ...
      BitmapImage := TBitmapImage.Create(...);
      ...
      BitmapImage.Bitmap.LoadFromFile(
        ExpandConstant('{app}\WizardSmallImageFile.bmp'));
      ...
    end;
    

    如果你不想安装文件,你可以将图像数据嵌入到代码中。

    不幸的是,Unicode Inno Setup 在处理二进制数据时非常有限,因为它倾向于尝试将所有内容都转换为 UTF-8。但经过多次尝试,我最终得到了一些工作代码。

    请注意,该代码使用从 Inno Setup preprocessor 调用的 PowerShell 代码 - 仅在编译时需要 PowerShell,在运行/安装时不需要。

    将此代码添加到 [Code] 部分前面的某处:

    function CryptStringToBinary(
      sz: string; cch: LongWord; flags: LongWord; binary: string; var size: LongWord;
      skip: LongWord; flagsused: LongWord): Integer;
      external 'CryptStringToBinaryW@crypt32.dll stdcall';
    
    const
      CRYPT_STRING_HEX = $04;
    
    procedure WriteBinaryStringToStream(S: string; Stream: TStream);
    var
      Buffer: string;
      Size: LongWord;
      Len: Integer;
    begin
      Len := Length(S);
      SetLength(Buffer, (Len div 4) + 1);
      Size := Len div 2;
      if (CryptStringToBinary(S, Len, CRYPT_STRING_HEX, Buffer, Size, 0, 0) = 0) or
         (Size <> Len div 2) then
      begin
        RaiseException('Error decoding binary string');
      end;
      
      Stream.WriteBuffer(Buffer, Size);
    end;  
    
    function StreamFromBinaryString(S: string): TStream;
    begin
      Result := TStringStream.Create('');
      WriteBinaryStringToStream(S, Result);
      Result.Position := 0;
    end;
    
    procedure LoadBitmapFromBinaryString(Bitmap: TBitmap; S: string);
    var
      Stream: TStream;
    begin
      Stream := StreamFromBinaryString(S);
      try
        Bitmap.LoadFromStream(Stream);
      finally
        Stream.Free;
      end;
    end;
    
    procedure SaveBinaryStringToFile(FileName: string; S: string);
    var
      Stream: TStream;
    begin
      Stream := TFileStream.Create(FileName, fmCreate);
      try
        WriteBinaryStringToStream(S, Stream);
      finally
        Stream.Free;
      end;
    end;
    
    #define FileToBinaryString(str FileName) \
      Local[4] = ExtractFileName(FileName), \
      Local[0] = AddBackslash(GetEnv("TEMP")) + Local[4] + ".pas", \
      Local[1] = \
        "-ExecutionPolicy Bypass -Command """ + \
        "Write-Host 'Generating code for " + Local[4] + "'; " + \
        "$bytes = [System.IO.File]::ReadAllBytes('" + FileName + "'); " + \
        "$s = '''' + (($bytes | foreach { $_.ToString('X2') }) -join '') + ''''; " + \
        "Set-Content -Path '" + Local[0] + "' -Value $s;" + \
        """", \
      Exec("powershell.exe", Local[1], SourcePath, , SW_HIDE), \
      Local[2] = FileOpen(Local[0]), \
      Local[3] = FileRead(Local[2]), \
      FileClose(Local[2]), \
      DeleteFileNow(Local[0]), \
      Local[3]
    

    然后您可以使用 FileToBinaryString preprocessor macro 在编译时(或更准确地说,在预处理时)将文件转换为十六进制字符串,例如:

    '4D5A50000200000004000F00FFFF0000B800000....'
    

    在运行时,您可以将十六进制字符串与 WriteBinaryStringToStreamStreamFromBinaryStringLoadBitmapFromBinaryStringSaveBinaryStringToFile 的某些函数一起使用。

    在您的情况下,您将使用:

    LoadBitmapFromBinaryString(
      BitmapImage.Bitmap, {#FileToBinaryString("C:\path\WizModernSmallImage.bmp")});
    

    在编译时,这会被转换为如下代码:

    LoadBitmapFromBinaryString(
      BitmapImage.Bitmap, '4D5A50000200000004000F00FFFF0000B800000....');
    

    预处理器/Pascal 编译器对一个字符串有大约 100M 个字符的限制。尽管对于大于约 20-30 MB 的文件,您实际上会首先达到 PowerShell 脚本的 [编译时] 内存限制。尽管即使对于较小的大小(大于几 MB),PowerShell 脚本的编译时性能也很差。不过,该脚本可以得到显着优化。

    由于十六进制编码,安装程序的大小增加了两倍。这可以通过使用一些更有效的编码来改进,比如 Base64 (CRYPT_STRING_BASE64)。与 [Files] 部分中包含的文件相比,代码部分甚至没有被压缩(对于图像来说这不是问题,因为它们已经被压缩,但例如对 DLL 有影响)。

    代码需要 Inno Setup 的 Unicode 版本。在 21 世纪,无论如何你都不应该使用 Ansi 版本。尽管具有讽刺意味的是,在 Ansi 版本中实现这一点要容易得多。有关与 Ansi 和 Unicode 版本的 Inno Setup 兼容的 CryptStringToBinary 的使用,请参阅 my answer to Writing binary file in Inno Setup。尽管在 Ansi 版本中,您实际上可以使用二进制字符串而不是十六进制字符串。

    【讨论】:

      猜你喜欢
      • 2018-04-11
      • 2018-05-04
      • 1970-01-01
      • 2017-08-15
      • 1970-01-01
      • 2016-06-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多