Has a any way to make the uninstall files (JPEG, DLL, PNG...) stay inside of the unins000.exe
? If so, please show a code.
问题:
回答1:
There is not any way to do this. The Inno uninstall engine cannot contain embedded files.
But on the other hand there's no particular need for it to do so either -- you can just install any files that it requires (into a subfolder of the app folder, if you want to keep things tidy).
The only real reason that the setup files are embedded into the installer is that it makes downloads easier. The uninstaller doesn't have that excuse.
回答2:
Inno Setup has no native support for embedding files.
But with some creativity, you can embed the files into the code (in a form of some constant).
Unfortunately the Unicode Inno Setup is quite limited when dealing with binary data as it tends to try to convert everything to UTF-8. But after numerous tries I've ended up with some working code (with use of a PowerShell code invoked from Inno Setup preprocessor).
Add this code somewhere to the front of your [Code]
section:
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;
begin
SetLength(Buffer, (Length(S) div 4) + 1);
Size := Length(S) div 2;
if (CryptStringToBinary(S, Length(S), CRYPT_STRING_HEX, Buffer, Size, 0, 0) = 0) or
(Size <> Length(S) 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]
And then you can use the FileToBinaryString
preprocessor macro to convert a file on compile-time (or more precisely, when pre-processing) to a hex string like:
'4D5A50000200000004000F00FFFF0000B8000....'
On runtime, you use the hex string with some of the functions WriteBinaryStringToStream
, StreamFromBinaryString
, LoadBitmapFromBinaryString
or SaveBinaryStringToFile
.
So for example to load a bitmap to TBitmapImage
, use:
LoadBitmapFromBinaryString(
BitmapImage.Bitmap, {#FileToBinaryString("C:\path\WizModernSmallImage.bmp")});
On compile-time, this gets converted to a code like:
LoadBitmapFromBinaryString(
BitmapImage.Bitmap, '4D5A50000200000004000F00FFFF0000B8000....');
To extract a DLL, use:
SaveBinaryStringToFile(
ExpandConstant('{tmp}\InnoCallback.dll'), {#FileToBinaryString("InnoCallback.dll")});
To use a DLL extracted on runtime, you have to use the delayload
flag:
function WrapTimerProc(callback:TTimerProc; paramcount:integer):longword;
external 'wrapcallback@{tmp}\innocallback.dll stdcall delayload';
The pre-processor/Pascal compiler has a limit of about 100M characters for a string. Though you will actually first hit a [compile-time] memory limit of the PowerShell script for files larger than about 20-30 MB. Though even for smaller sizes (larger than few MBs), the compile-time time performance of the PowerShell script is bad. The script can be optimized significantly though.
Due to the hex encoding, the size of the installer increases twice as much. This could be improved by using some more efficient encoding, like Base64 (CRYPT_STRING_BASE64
). The code section is not even compressed too, comparing to files included with the [Files]
section (not a problem for images as these are compressed already, but makes a difference with DLLs for example).
This solution was initially developed for the question Inno Setup: Reading a file from installer during uninstallation.
The code requires the Unicode version of Inno Setup. With latest Inno Setup 6, it's the only version available anyway. Even if you use Inno Setup 5, you should not use the Ansi version anyway, in the 21st century. Though ironically, implementing this in the Ansi version would be way easier. See my answer to Writing binary file in Inno Setup for a use of the CryptStringToBinary
that's compatible with both Ansi and Unicode version of Inno Setup. Though in the Ansi version you can actually do with a binary string, instead of a hex string.