Inno Setup:在卸载过程中从安装程序读取文件



卸载过程中使用以下代码

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 仅卸载某些组件。

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

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


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

[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 预处理器调用的 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预处理器宏在编译时(或更准确地说,在预处理时)将文件转换为十六进制字符串,如下所示:

'4D5A50000200000004000F00FFFF0000B800000....'

在运行时,十六进制字符串与一些函数一起使用WriteBinaryStringToStreamStreamFromBinaryStringLoadBitmapFromBinaryStringSaveBinaryStringToFile

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

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

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

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

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

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

该代码需要 Unicode 版本的 Inno Setup。无论如何,在21世纪,您都不应该使用Ansi版本。虽然具有讽刺意味的是,在Ansi版本中实现它会更容易。请参阅我在Inno Setup中编写二进制文件的答案,以使用与Inno Setup的Ansi和Unicode版本兼容的CryptStringToBinary。虽然在 Ansi 版本中,您实际上可以使用二进制字符串而不是十六进制字符串。

最新更新