Adding multiple Icons (Win32-Resource) to .NET-Application

Solution 1:

Windows doesn't know anything about managed resources, you need to add unmanaged resources to your executable. In parapura's screenshot, you need to select the Resource file radio button. That requires a .res file, a binary file that's created by running the Windows SDK rc.exe tool on a .rc file. The .rc file is a simple text file that contains resource statements, similar to this:

1 ICON "mainicon.ico"
2 ICON "alternative1.ico"
3 ICON "alternative2.ico"
1 24 "app.manifest"

Be sure to save this file into your project folder without utf-8 encoding, using Notepad is best. Create the required app.manifest file with Project + Add New Item, Application Manifest File. Add this .rc file to your project with Project + Add Existing Item. Double-click it and verify that you can see the icons and the manifest. Right-click the top node, Add Resource and click Version + New. Edit the version info, beware that it will no longer automatically match the attributes in AssemblyInfo.cs

You can compile the .rc file into a .res file with the Visual Studio Command prompt:

rc /r something.rc

Which produces the .res file you can use in the project property tab. Doing this is a pre-build event is advisable but a bit hard to get right. The number of ways this can go wrong are numerous, good luck.

Solution 2:

I've just created a simple tool to do exactly this without having to mess with .res files. It is a tiny utility which you can use as part of your Post-Build event and lets you add all icon files in a particular folder to your assembly. If we assume that you have a icons folder under your main project folder you can add the following post-build event:

C:\path\to\InsertIcons.exe $(TargetPath) $(ProjectDir)icons

A further description and a download can be found at http://einaregilsson.com/add-multiple-icons-to-a-dotnet-application/

Solution 3:

Based on answers which I see - it seems to be quite trivial, but unfortunately it's not so simple.

Let's start from .rc file - you can quickly write file like this: myown.rc:

1 ICON "..\\Folder1\\icon1.ico"
2 ICON "..\\folder2\\icon2.ico"
1 24 "myown.manifest"

But next question - where to get manifest file which you need for your application.

It's possible to extract currently used manifest file, using command line like this:

mt -inputresource:myOwnNetExe.exe;#1 -out:myown.manifest

And then you can use that manifest with your .net executable.

But this is not all. From project itself you cannot specify relative path to your .res file, you can use only absolute path. Save .csproj, and edit it manually with notepad - to make path relative. Project compiles just fine after that.

But into svn / git - you probably won't put .res file - only source code. Need to compile .rc to .res.

Best to do it in project / pre-build step.

But of course rc.exe cannot be launched, since it's C++ project, and we have C# project which does not care about C++ project pathes.

This can be walkarounded using pre-build step like this:

cd $(ProjectDir)
call "%VS100COMNTOOLS%\vsvars32.bat" >nul
rc /c 1252 /nologo myown.rc

Where you need to change VS100COMNTOOLS with visual studio with which you're compiling (I'm using Visual studio 2010).

And this is still not everything. If you want to change icon of your application on fly - in run-time - you might be interested in code snipet like this:

[DllImport("shell32.dll")]
public static extern IntPtr ExtractIcon(IntPtr hInst, string file, int nIconIndex);

[DllImport("user32.dll", SetLastError = true)]
static extern bool DestroyIcon(IntPtr hIcon);

/// <summary>
/// Sets icon from .exe or .dll into form object
/// </summary>
/// <param name="iIconIndex">Icon index to use.</param>
/// <param name="form">Form to assign to given icon</param>
/// <returns>true if ok, false if failed.</returns>
bool SetIcon( object form, int iIconIndex, String dllOrExe = null )
{
    if( dllOrExe == null )
        dllOrExe = System.Reflection.Assembly.GetExecutingAssembly().Location;

    IntPtr hIcon = ExtractIcon(IntPtr.Zero, dllOrExe, iIconIndex);

    if( hIcon == IntPtr.Zero )
        return false;

    Icon icon = (Icon) Icon.FromHandle(hIcon).Clone();
    DestroyIcon(hIcon);

    form.GetType().GetProperty("Icon").SetValue(form, icon);
    return true;
}

Where in form_load you can place call like this:

SetIcon(this, N);

Where N = 0 for first icon, N = 1 - for second and so on.

18.5.2016 Update My approach works ok - however - after this assembly file properties - version information disappears. It's possible to restore it back using typical C++ .rc file, but unfortunately for me it does then break assembly attributes somehow - task manager will display your exe as "Application". I've tried to harvest bit deeper if it's possible to somehow alter manifest / application title - but without any result. After that I've ended up using InsertIcons.exe solution proposed here as well.

So if you're not interested in file version information - you can use my solution, if you need file version and task manager should show your application correctly - use InsertIcons.exe. C# code snipet (SetIcon) here will work for you in any solution whatsoever.