Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c#

winforms

icons

I have tried the solution in Stack Overflow questions Custom icon for ClickOnce application in 'Add or Remove Programs' and Is there a way to change the icon of a ClickOnce application in 'Add or Remove Programs'?.

So, here are my implementations. Both of them compile and no exceptions are thrown when the code is run. I am publishing the ClickOnce setup files to a webserver and then installing it after uninstalling from computer. When I go to control panel Add or Remove Programs, I still see the generic icon for my program. Everywhere else I do not have issues and my icon shows up just fine.

/*  METHOD ONE */
private static void SetAddRemoveProgramsIcon()
{
    //Only run if deployed
    if (System.Deployment.Application.ApplicationDeployment.IsNetworkDeployed && ApplicationDeployment.CurrentDeployment.IsFirstRun)
    {
        try
        {
            Assembly code = Assembly.GetExecutingAssembly();
            AssemblyDescriptionAttribute asdescription =
                (AssemblyDescriptionAttribute)Attribute.GetCustomAttribute(code, typeof(AssemblyDescriptionAttribute));
            string assemblyDescription = asdescription.Description;

            //The icon is included in this program
            string iconSourcePath = Path.Combine(System.Windows.Forms.Application.StartupPath, "forico4.ico");

            if (!File.Exists(iconSourcePath))
                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");
                if (myValue != null && myValue.ToString() == "admin")
                {
                    myKey.SetValue("DisplayIcon", iconSourcePath);
                    break;
                }
            }
        }
        catch (Exception ex)
        {
            System.Windows.Forms.MessageBox.Show(ex.Message.ToString());
        }
    }
}
*/

/*  METHOD TWO */
private static void SetAddRemoveProgramsIcon()
{
    //only run if deployed
    if (System.Deployment.Application.ApplicationDeployment.IsNetworkDeployed
       && ApplicationDeployment.CurrentDeployment.IsFirstRun)
    {
        try
        {
            string iconSourcePath = Path.Combine(System.Windows.Forms.Application.StartupPath, "forico4.ico");
            if (!File.Exists(iconSourcePath))
                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");
                if (myValue != null && myValue.ToString() == "GoldMailAkamai")
                {
                    myKey.SetValue("DisplayIcon", iconSourcePath);
                    break;
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
        }
    }
}
*/
like image 961
Brandon Bearden Avatar asked Apr 26 '13 04:04

Brandon Bearden


2 Answers

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());
        }
    }
}
like image 119
Brandon Bearden Avatar answered Sep 21 '22 00:09

Brandon Bearden


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'.

like image 29
user3674642 Avatar answered Sep 20 '22 00:09

user3674642