A small polish thing, but it would be nice to see a proper icon under my application in the Windows Add or Remove Programs dialog. Should be as simple as creating the DisplayIcon registry key. I would imagine most folks would want this icon to be the same one as the desktop and start menu shortcuts. This StackOverflow post describes essentially what I'm looking for (but it is obviously for a different installer):
http://stackoverflow.com/questions/16204889/how-to-change-the-icon-in-add-or-remove-programs
If you set the nuget package icon uri it does this already.
@jeremy-bridges you can define the icon in your .nuspec file under package\metadata\iconUrl it must be on a public Webserver and its fetched at installation time not packaging time.
So don't remove the icon from the webserver after you created your package.
Also there seems to be some sort of tricky caching involved here so try it on a new computer if it doesn't work.
A related issue is when the app is installed when no internet is available. In that case, the icon will not be set.
It would be great if we could use embedded icon.
This is an old issue, but I'm sure it remain problematic for some. I fix this issue by setting the DisplayIcon value of the Uninstall registry key during "--squirrel-install, --squirrel-updated" startup event.
The value can be path to an icon of your choice or just set it to the application executable path this uses the embedded app icon. In my case I just bundle the icon with my app and just copy it to installation root to mimic the normal squirrel behavior which downloads the nuget to local machine.
Hope this helps and maybe become an option in future releases.
Has anyone managed to do this on an offline environment? @Mjinx , are you talking about the Custom Squirrel Events? (https://github.com/Squirrel/Squirrel.Windows/blob/develop/docs/using/custom-squirrel-events.md). Could you post a sample code?
My solution was offline on an electron app and Yes using the builtin events.
I don't have access to the code but basically I listen to first run event ("--squirrel-firstrun") and updated the uninstall registry value icon ("\Software\Microsoft\Windows\CurrentVersion\Uninstall{{APPNAME}}\DisplayIcon") with the icon that I shipped with my product. (you can also point the path to your .exe it will use display the icon as is on the file i believe, but needs testing.)
I have tried this C# code but for some reason the key always returns null :
```c#
// under static void main:
using (var mgr = new UpdateManager(@"AppLocation"))
{
SquirrelAwareApp.HandleEvents(
onInitialInstall: v => mgr.CreateShortcutForThisExe(),
onFirstRun: () => AddIconToRegistry(),
onAppUpdate: v => mgr.CreateShortcutForThisExe(),
onAppUninstall: v => mgr.RemoveShortcutForThisExe());
}
private static void AddIconToRegistry()
{
Log.Information("Setting key value for icon in registry");
using(RegistryKey baseKey = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser,RegistryView.Registry64))
using (RegistryKey myKey = baseKey.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Uninstall\AppName", true))
{
Log.Information("Current exe path : " + Assembly.GetExecutingAssembly().GetName().CodeBase.Replace("file:///",""));
if (myKey is null)
{
Log.Warning("Key not found");
}
else
{
myKey.SetValue("DisplayIcon", Assembly.GetExecutingAssembly().GetName().CodeBase.Replace("file:///", ""), RegistryValueKind.String);
Log.Information("Key value for icon is set successfully");
}
}
}
```
Any help is appreciated
Use CreateSubKey instead of OpenSubKey to create the missing key. It looks like the key is not yet created when the method is executed. Also you might want to consider setting the uninstaller icon in onInitialInstall and onAppUpdate (if you only use onInitialInstall the path to the executable might become invalid after installing a few updates).
Using the squirrel events is a great idea but the real solution would be to stop using the deprecated 'iconUrl' nuget attribute and start using the 'icon' nuget attribute
This requires a small change to the squirrel code
It looks like this would be a simple change in CreateUninstallerRegistryEntry in Squirrel.Windows\src\Squirrel\UpdateManager.InstallHelpers.cs to check the <icon> if an <iconUrl> isn't provided (for the sake of compatibility).
It looks like a straightforward enough fix. The first nuance I see is that it converts to an ico (from png or ico) but the <icon> option only supports png and jpeg. But that code is already written to bail out aggressively in case of an error, so even partial support for an <icon> seems valuable.
Here's a part of the fix:
In function CreateUninstallerRegistryEntry:
`if (!string.IsNullOrEmpty(zp.Icon))
{
try
{
var appDirName = $"app-{latest.Version}";
var iconFileInfo = new FileInfo(zp.Icon);
var iconFileName = iconFileInfo.Name;
var iconFullPath = Path.Combine(rootAppDirectory, appDirName, iconFileName);
key.SetValue("DisplayIcon", iconFullPath, RegistryValueKind.String);
this.Log().Info("Setting display icon");
}
catch (Exception ex)
{
this.Log().InfoException("Couldn't write uninstall icon, don't care", ex);
}
}
else
{
var targetPng = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".png");
var targetIco = Path.Combine(rootAppDirectory, "app.ico");
if (zp.IconUrl != null && !File.Exists(targetIco)) {
try {
using (var wc = Utility.CreateWebClient()) {
await wc.DownloadFileTaskAsync(zp.IconUrl, targetPng);
using (var fs = new FileStream(targetIco, FileMode.Create)) {
if (zp.IconUrl.AbsolutePath.EndsWith("ico")) {
var bytes = File.ReadAllBytes(targetPng);
fs.Write(bytes, 0, bytes.Length);
} else {
using (var bmp = (Bitmap)Image.FromFile(targetPng))
using (var ico = Icon.FromHandle(bmp.GetHicon())) {
ico.Save(fs);
}
}
key.SetValue("DisplayIcon", targetIco, RegistryValueKind.String);
}
}
} catch(Exception ex) {
this.Log().InfoException("Couldn't write uninstall icon, don't care", ex);
} finally {
File.Delete(targetPng);
}
}
}`
But this requires further changes to the LocalPackage.cs file and some more, in order to introduce the new Icon property.
I want to create a PR but not authorized to do so
Most helpful comment
A related issue is when the app is installed when no internet is available. In that case, the icon will not be set.
It would be great if we could use embedded icon.