'Add or Remove Programs' icon for a C# ClickOnce application

I finally figured it out after looking at the registry and copying other application's settings. It is strange that you cannot reference the EXE file in a ClickOnce deployed application. At least I could not get it to work. So, I reverted to referencing the .ico instead. Make sure to read the comments!

using System.Deployment.Application;
using Microsoft.Win32;
//Call this method as soon as possible

private static void SetAddRemoveProgramsIcon()
{
    //Only execute on a first run after first install or after update
    if (ApplicationDeployment.CurrentDeployment.IsFirstRun)
    {
        try
        {
            // Set the name of the application EXE file - make sure to include `,0` at the end.
            // Does not work for ClickOnce applications as far as I could figure out... Note, this will probably work
            // when run from Visual Studio, but not when deployed.
            //string iconSourcePath = Path.Combine(System.Windows.Forms.Application.StartupPath, "example.exe,0");
            // Reverted to using this instead (note this will probably not work when run from Visual Studio, but will work on deploy.
            string iconSourcePath = Path.Combine(System.Windows.Forms.Application.StartupPath, "example.ico");
            if (!File.Exists(iconSourcePath))
            {
                MessageBox.Show("We could not find the application icon. Please notify your software vendor of this error.");
                return;
            }

            RegistryKey myUninstallKey = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Uninstall");
            string[] mySubKeyNames = myUninstallKey.GetSubKeyNames();
            for (int i = 0; i < mySubKeyNames.Length; i++)
            {
                RegistryKey myKey = myUninstallKey.OpenSubKey(mySubKeyNames[i], true);
                object myValue = myKey.GetValue("DisplayName");
                Console.WriteLine(myValue.ToString());
                // Set this to the display name of the application. If you are not sure, browse to the registry directory and check.
                if (myValue != null && myValue.ToString() == "Example Application")
                {
                    myKey.SetValue("DisplayIcon", iconSourcePath);
                    break;
                }
            }
        }
        catch(Exception mye)
        {
            MessageBox.Show("We could not properly setup the application icons. Please notify your software vendor of this error.\r\n" + mye.ToString());
        }
    }
}

I followed the same technique using VB and VS2013E. Steps:

  1. Right click the project node in Solution Explorer.
  2. Add Exisitng -> Logo.ico
  3. Observe that the file is added to the project tree.
  4. Right click this entry and select "Properties".
  5. "Copy to Output Directory" select "Copy always".

The steps ensured that the Logo.ico file is packaged in deployment. Code blocks are as follows:

Imports System.Deployment.Application.ApplicationDeployment
Imports System.Reflection
Imports System.IO
Imports Microsoft.Win32

Module ControlPanelIcon
    ' Call this method as soon as possible
    ' Writes entry to registry
    Public Function SetAddRemoveProgramsIcon() As Boolean
        Dim iName As String = "iconFile.ico" ' <---- set this (1)
        Dim aName As String = "appName" '      <---- set this (2)
        Try
            Dim iconSourcePath As String = Path.Combine(System.Windows.Forms.Application.StartupPath, iName)
            If Not IsNetworkDeployed Then Return False ' ClickOnce check
            If Not CurrentDeployment.IsFirstRun Then Return False
            If Not File.Exists(iconSourcePath) Then Return False
            Dim myUninstallKey As RegistryKey = Registry.CurrentUser.OpenSubKey("Software\Microsoft\Windows\CurrentVersion\Uninstall")
            Dim mySubKeyNames As String() = myUninstallKey.GetSubKeyNames()
            For i As Integer = 0 To mySubKeyNames.Length Step 1
                Dim myKey As RegistryKey = myUninstallKey.OpenSubKey(mySubKeyNames(i), True)
                Dim myValue As Object = myKey.GetValue("DisplayName")
                If (myValue IsNot Nothing And myValue.ToString() = aName) Then
                    myKey.SetValue("DisplayIcon", iconSourcePath)
                    Return True
                End If
            Next i
        Catch ex As Exception
            Return False
        End Try
        Return False
    End Function
End Module

Function call returns true if the intended action is performed. False otherwise. In the main Form, function call like this:

Sub New()
    ' This call is required by the Windows Form Designer.
    InitializeComponent()
    ' Add any initialization after the InitializeComponent() call.
    ' Modify registry to show icon in Control Panel - Add/Remove Programs
    ControlPanelIcon.SetAddRemoveProgramsIcon()
End Sub

Thanks to the contributors of this posting, and special thanks to Custom icon for ClickOnce application in 'Add or Remove Programs'.

Tags:

C#

Winforms

Icons