Skip to content

Commit ffb109b

Browse files
authored
Slim uninstaller copies and enable stub compression (#118)
* Slim uninstaller copies and enable stub compression * Ensure metadata accompanies slim uninstaller
1 parent d67b75d commit ffb109b

File tree

3 files changed

+82
-16
lines changed

3 files changed

+82
-16
lines changed

src/DotnetPackaging.Exe.Installer/Core/Installer.cs

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,7 @@ private static void RegisterUninstaller(string targetDir, InstallerMetadata meta
2020
return;
2121
}
2222

23-
try
24-
{
25-
var metadataPath = Path.Combine(targetDir, "metadata.json");
26-
var json = JsonSerializer.Serialize(meta);
27-
File.WriteAllText(metadataPath, json);
28-
}
29-
catch
30-
{
31-
// Best effort
32-
}
23+
PersistMetadata(targetDir, meta);
3324

3425
// Strategy:
3526
// 1. Copy full installer as Uninstall.exe to "Uninstall" subdirectory to avoid DLL locks/conflicts with main app
@@ -39,14 +30,20 @@ private static void RegisterUninstaller(string targetDir, InstallerMetadata meta
3930
{
4031
var uninstallDir = Path.Combine(targetDir, "Uninstall");
4132
Directory.CreateDirectory(uninstallDir);
33+
PersistMetadata(uninstallDir, meta);
4234

4335
var uninstallerPath = Path.Combine(uninstallDir, "Uninstall.exe");
44-
File.Copy(Environment.ProcessPath, uninstallerPath, overwrite: true);
36+
var slimUninstallerResult = UninstallerBuilder.CreateSlimCopy(Environment.ProcessPath, uninstallerPath);
37+
if (slimUninstallerResult.IsFailure)
38+
{
39+
Log.Warning("Slim uninstaller creation failed: {Error}. Using full installer copy instead.", slimUninstallerResult.Error);
40+
File.Copy(Environment.ProcessPath, uninstallerPath, overwrite: true);
41+
}
4542
Log.Information("Uninstaller copied to: {Path}", uninstallerPath);
46-
43+
4744
WindowsRegistryService.Register(
48-
meta.AppId,
49-
meta.ApplicationName,
45+
meta.AppId,
46+
meta.ApplicationName,
5047
meta.Version,
5148
meta.Vendor,
5249
targetDir,
@@ -59,6 +56,20 @@ private static void RegisterUninstaller(string targetDir, InstallerMetadata meta
5956
}
6057
}
6158

59+
private static void PersistMetadata(string directory, InstallerMetadata meta)
60+
{
61+
try
62+
{
63+
var metadataPath = Path.Combine(directory, "metadata.json");
64+
var json = JsonSerializer.Serialize(meta);
65+
File.WriteAllText(metadataPath, json);
66+
}
67+
catch
68+
{
69+
// Best effort
70+
}
71+
}
72+
6273
private static Result<string> ResolveMainExe(string targetDir, InstallerMetadata meta)
6374
{
6475
return Result.Try(() =>
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using CSharpFunctionalExtensions;
2+
using Serilog;
3+
4+
namespace DotnetPackaging.Exe.Installer.Core;
5+
6+
internal static class UninstallerBuilder
7+
{
8+
public static Result<string> CreateSlimCopy(string installerPath, string uninstallerPath)
9+
{
10+
return Result.Try(() =>
11+
{
12+
var destinationDirectory = Path.GetDirectoryName(uninstallerPath);
13+
if (destinationDirectory is null)
14+
{
15+
throw new InvalidOperationException($"Cannot determine directory for '{uninstallerPath}'.");
16+
}
17+
18+
Directory.CreateDirectory(destinationDirectory);
19+
20+
var payloadStart = PayloadExtractor.GetAppendedPayloadStart(installerPath);
21+
if (payloadStart.HasValue)
22+
{
23+
CopyWithoutPayload(installerPath, uninstallerPath, payloadStart.Value);
24+
Log.Information("Uninstaller written without embedded payload at {Path}", uninstallerPath);
25+
return uninstallerPath;
26+
}
27+
28+
File.Copy(installerPath, uninstallerPath, overwrite: true);
29+
Log.Information("Embedded payload not found, copied installer to {Path}", uninstallerPath);
30+
return uninstallerPath;
31+
}, ex => $"Failed to create uninstaller: {ex.Message}");
32+
}
33+
34+
private static void CopyWithoutPayload(string sourcePath, string destinationPath, long bytesToCopy)
35+
{
36+
using var source = File.OpenRead(sourcePath);
37+
using var destination = File.Create(destinationPath);
38+
39+
var buffer = new byte[81920];
40+
var remaining = bytesToCopy;
41+
42+
while (remaining > 0)
43+
{
44+
var toRead = (int)Math.Min(buffer.Length, remaining);
45+
var read = source.Read(buffer, 0, toRead);
46+
if (read == 0)
47+
{
48+
throw new EndOfStreamException("Unexpected end of file while copying installer payload.");
49+
}
50+
51+
destination.Write(buffer, 0, read);
52+
remaining -= read;
53+
}
54+
}
55+
}

src/DotnetPackaging.Exe/ExePackagingService.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -356,13 +356,13 @@ private async Task<Result<Maybe<string>>> TryResolveLocalStub(string rid)
356356
Configuration = "Release",
357357
SingleFile = true,
358358
Trimmed = false,
359-
MsBuildProperties = new Dictionary<string, string>
359+
MsBuildProperties = new Dictionary<string, string>
360360
{
361361
["IncludeNativeLibrariesForSelfExtract"] = "true",
362362
["IncludeAllContentForSelfExtract"] = "true",
363363
["PublishTrimmed"] = "false",
364364
["DebugType"] = "embedded",
365-
["EnableCompressionInSingleFile"] = "false"
365+
["EnableCompressionInSingleFile"] = "true"
366366
}
367367
};
368368

0 commit comments

Comments
 (0)