Avoiding duplicate icon resources in a .NET (C#) project
Asked Answered
E

5

23

I'm using Visual C# 2008 Express. I'd like to use the same icon for the application (ie, the icon shown for the .exe), and for the main form. Unfortunately, VC# doesn't seem to be very smart about this, and insists on duplicating the icon data.

There doesn't seem to be a way of selecting an "already embedded" icon for use in the form or project icon (only selecting a file), and using the same file for both icons just embeds the file twice as far as I can see. It's not a big deal (hard drive space is cheap now, right?), but it bugs me.

Any idea how to avoid this? Is there a way to programatically load the executable's icon for use when the form is constructed, say? A couple of forum posts about similar things seem to suggest that .NET resources don't use the normal old Windows resource system -- is there a way from within the framework of getting at the old-style resources? Or do I have to bind the Win32 API functions to do it?

Enosis answered 27/2, 2009 at 21:55 Comment(3)
Why not use inheritance? Define the icon in one custom Form class and use it everywhere.Ladner
I remember my Delphi programming times... self.Icon = Application.Icon; :P ... too difficult in .NET :DUltraism
possible duplicate of Any easy way to use icons from resources?Hypoblast
L
7

Yeah, it's pretty annoying. But the problem with the proposed answer of Icon.ExtractAssociatedIcon is that it will retrieve the 32x32 icon, and then downsample to a 16x16 icon in your forms window or on the taskbar, which will look terrible unless your 32x32 icon is very cleverly constructed.

The way I'm doing it is with interop (put the first line in your form constructor):

this.Icon = ExtractSmallIconFromLibrary(Application.ExecutablePath);
...

public static Icon ExtractSmallIconFromLibrary(string file) {
    IntPtr[] reficon = new IntPtr[1];
    int nextracted = ExtractIconEx(file, 0, null, reficon, 1);
    if (nextracted < 1)
        return null;
    Icon unmanaged_icon = Icon.FromHandle(reficon[0]);
    Icon icon = (Icon)unmanaged_icon.Clone();
    DestroyIcon(unmanaged_icon.Handle);
    return icon;
}

[DllImport("Shell32", CharSet = CharSet.Auto)]
extern static int ExtractIconEx(
    [MarshalAs(UnmanagedType.LPTStr)] 
    string lpszFile,
    int nIconIndex,
    IntPtr[] phIconLarge,
    IntPtr[] phIconSmall,
    int nIcons
    );

[DllImport("user32.dll", CharSet = CharSet.Auto)]
extern static bool DestroyIcon(IntPtr handle);

But this isn't great, either, since you do want the 32x32 icon for things like the Alt-Tab icon list. So you really need to extract the entire icon, which is a bigger job. Maybe there's a straightforward way to combine the two icons into one. Or you can do like this codeproject program, which extracts the whole icon in the first place with a huge pile of code.

Luminary answered 10/3, 2009 at 21:27 Comment(0)
C
16

You're right, and it's rather annoying.

You have to load the icons yourself instead of relying on designer-generated code. Save the icon as a project resource, then load the resource into the form's Icon property in the form's constructor:

this.Icon = Properties.Resources.myIconResourceName;
Cud answered 27/2, 2009 at 21:57 Comment(5)
That doesn't solve it: embedding the icon as a project resource is just the same as setting it in the form designer -- it duplicates the icon data, which is what I'm trying to avoid.Enosis
It should already be embedded as a resource when you add the icon as the project icon, no?Cud
Not that I can see... the icon used for the executable seems to be entirely independent of the .NET resource system.Enosis
The icon associated with the executable file is stored as a normal win32 icon resource. This is the resource you see when you look in XN resource editor. That is different from the the .NET resource system (which does store resources in the executable, but not in the same format).Enosis
This answer is useful if, like me, you want to change the icon for a form at Runtime. You just need to add icons in project resources and change the form's icon when you load itSeedy
R
11

You're looking for Icon.ExtractAssociatedIcon. Call passing your executable:

var icon = Icon.ExtractAssociatedIcon(Assembly.GetExecutingAssembly().Location);
Resurrect answered 27/2, 2009 at 22:36 Comment(3)
Please explain why this is slow? If it's because it's using reflection that can be easily solved by using Application.ExecutablePath instead of Assembly.GetExecutingAssembly().Location.Enosis
Best solution! Which don't duplicate icon in resources and show best quality icon in taskbar. Thank you!Cherlynchernow
May be OK for you, but please be aware that this does not work for UNC paths.Geophyte
L
7

Yeah, it's pretty annoying. But the problem with the proposed answer of Icon.ExtractAssociatedIcon is that it will retrieve the 32x32 icon, and then downsample to a 16x16 icon in your forms window or on the taskbar, which will look terrible unless your 32x32 icon is very cleverly constructed.

The way I'm doing it is with interop (put the first line in your form constructor):

this.Icon = ExtractSmallIconFromLibrary(Application.ExecutablePath);
...

public static Icon ExtractSmallIconFromLibrary(string file) {
    IntPtr[] reficon = new IntPtr[1];
    int nextracted = ExtractIconEx(file, 0, null, reficon, 1);
    if (nextracted < 1)
        return null;
    Icon unmanaged_icon = Icon.FromHandle(reficon[0]);
    Icon icon = (Icon)unmanaged_icon.Clone();
    DestroyIcon(unmanaged_icon.Handle);
    return icon;
}

[DllImport("Shell32", CharSet = CharSet.Auto)]
extern static int ExtractIconEx(
    [MarshalAs(UnmanagedType.LPTStr)] 
    string lpszFile,
    int nIconIndex,
    IntPtr[] phIconLarge,
    IntPtr[] phIconSmall,
    int nIcons
    );

[DllImport("user32.dll", CharSet = CharSet.Auto)]
extern static bool DestroyIcon(IntPtr handle);

But this isn't great, either, since you do want the 32x32 icon for things like the Alt-Tab icon list. So you really need to extract the entire icon, which is a bigger job. Maybe there's a straightforward way to combine the two icons into one. Or you can do like this codeproject program, which extracts the whole icon in the first place with a huge pile of code.

Luminary answered 10/3, 2009 at 21:27 Comment(0)
G
3

I think in many cases including a duplicate icon is at the end of the day more efficient than trying to extract it from the unmanaged resource - considering you can't use Icon.ExtractAssociatedIcon for UNC paths.

Geophyte answered 15/2, 2012 at 9:16 Comment(0)
B
1

I had a similar problem.

I have and exe icon I want to reuse for subforms without increasing file size.

//From MyApp
MySubForm msf = new MySubForm();
msf.Icon = this.Icon;
msf.Show();

I don't know if this is useful, but I want to share it in anyway.

Beverage answered 30/7, 2010 at 14:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.