Inno Setup:卸载期间从安装程序读取文件

Mav*_*ick 4 inno-setup pascalscript

卸载时使用以下代码

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;
Run Code Online (Sandbox Code Playgroud)

我收到错误:

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

我还尝试ExtractTemporaryFile在调用之前使用LoadFromFile卸载过程中不支持的功能。

ExtractTemporaryFile('WizardSmallImageFile.bmp');
Run Code Online (Sandbox Code Playgroud)

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

我上面的代码构建了一个带有自定义面板的自定义表单。就像这里:Inno Setup 仅卸载一些组件

Mar*_*ryl 7

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

\n

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

\n
\n

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

\n\n
[Files]\nSource: "WizardSmallImageFile.bmp"; DestDir: "{app}";\n\n[Code]\n\nfunction InitializeUninstall(): Boolean;\nbegin\n  ...\n  BitmapImage := TBitmapImage.Create(...);\n  ...\n  BitmapImage.Bitmap.LoadFromFile(\n    ExpandConstant(\'{app}\\WizardSmallImageFile.bmp\'));\n  ...\nend;\n
Run Code Online (Sandbox Code Playgroud)\n
\n

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

\n

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

\n

请注意,该代码使用从Inno Setup 预处理器\xe2\x80\x93 调用的 PowerShell 代码。仅在编译时需要 PowerShell,而不是在运行/安装时需要。

\n

将此代码添加到您的部分前面的某个位置[Code]

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

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

\n
\'4D5A50000200000004000F00FFFF0000B800000....\'\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n

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

\n
LoadBitmapFromBinaryString(\n  BitmapImage.Bitmap, {#FileToBinaryString("C:\\path\\WizModernSmallImage.bmp")});\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n
LoadBitmapFromBinaryString(\n  BitmapImage.Bitmap, \'4D5A50000200000004000F00FFFF0000B800000....\');\n
Run Code Online (Sandbox Code Playgroud)\n
\n

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

\n

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

\n

该代码需要 Inno Setup 的 Unicode 版本。无论如何,在 21 世纪,你不应该使用 Ansi 版本。尽管讽刺的是,在 Ansi 版本中实现这一点会容易得多。请参阅我对在 Inno Setup 中编写二进制文件的回答,CryptStringToBinary了解与 Inno Setup 的 Ansi 和 Unicode 版本兼容的使用。尽管在 Ansi 版本中,您实际上可以使用二进制字符串,而不是十六进制字符串。

\n