如何在卸载程序中保留卸载文件?

And*_*ork 4 inno-setup

有没有办法让卸载文件(JPEG,DLL,PNG ...)留在里面unins000.exe?如果是,请显示代码.

Mir*_*ral 7

没有办法做到这一点.Inno卸载引擎不能包含嵌入文件.

但另一方面也没有特别需要它 - 你可以安装它需要的任何文件(如果你想保持整洁,请安装到app文件夹的子文件夹中).

将安装文件嵌入到安装程序中的唯一真正原因是它使下载更容易.卸载程序没有这个借口.


Mar*_*ryl 5

Inno Setup 不支持将文件嵌入到卸载程序中。

但是,通过一些创造力,您可以将文件嵌入到代码中(以某种常量的形式)。

不幸的是,Unicode Inno Setup 在处理二进制数据时非常有限,因为它倾向于尝试将所有内容转换为 UTF-8。但经过多次尝试,我最终得到了一些工作代码(使用从Inno Setup preprocessor调用的 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;
  L: Integer;
begin
  L := Length(S);
  SetLength(Buffer, (L div 4) + 1);
  Size := L div 2;
  if (CryptStringToBinary(S, L, CRYPT_STRING_HEX, Buffer, Size, 0, 0) = 0) or
     (Size <> L 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]
Run Code Online (Sandbox Code Playgroud)

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

'4D5A50000200000004000F00FFFF0000B8000....'
Run Code Online (Sandbox Code Playgroud)

在运行时,您可以将十六进制字符串与某些函数WriteBinaryStringToStream、StreamFromBinaryString或LoadBitmapFromBinaryString一起使用SaveBinaryStringToFile。


例如,要将位图加载到TBitmapImage,请使用:

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

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

LoadBitmapFromBinaryString(
  BitmapImage.Bitmap, '4D5A50000200000004000F00FFFF0000B8000....');
Run Code Online (Sandbox Code Playgroud)

要提取 DLL,请使用:

SaveBinaryStringToFile(
  ExpandConstant('{tmp}\InnoCallback.dll'),
  {#FileToBinaryString("InnoCallback.dll")});
Run Code Online (Sandbox Code Playgroud)

要使用在运行时提取的 DLL,您必须使用以下delayload标志:

function WrapTimerProc(callback:TTimerProc; paramcount:integer):longword;
  external 'wrapcallback@{tmp}\innocallback.dll stdcall delayload';
Run Code Online (Sandbox Code Playgroud)

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

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


该解决方案最初是针对问题Inno Setup: Reading a file from installer during uninstallation开发的。


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