[API Proposal]: Add Extract methods to Icon
See original GitHub issueBackground and motivation
System.Drawing.Icon
has methods for creating icons from managed resources and associated native icons. It doesn’t, however, allow you to get all native resource icons, or always specify the size that you require.
The Icon
constructors that take a path / stream only support .ico
files. They do allow choosing a particular size, but also try to pick a color depth based on the current display settings. Color depth matching is of limited use and these constructors are costly as they manually load the entire file, parsing it to create the “best-fit” icon. The full stream is kept in memory (this is particularly painful if you’re trying to load smaller icons).
System.Drawing.Icon.ExtractAssociatedIcon()
has some overlap with these new APIs. It will load the first icon from the file it’s given or fall back on whatever executable the file is associated with. The API used for this doesn’t allow specifying a size. (We could spin our own by calling FindExecutable
if we find a need in the future.)
API Proposal
namespace System.Drawing;
public class Icon
{
// The existing Extract method.
public static Icon ExtractAssociatedIcon(string! filePath);
// The following work with .ico files as well as PE files (.exe, .dll, etc.). The id is an index when positive or a resource id when negative.
// Retrieves the specified icon at the current system icon size setting (large by default).
public static Icon? ExtractIcon(string! filePath, int id, bool smallIcon = false);
// Allows retrieving the specified icon at a specific size (say 32x32). Icon sizes are always square.
public static Icon? ExtractIcon(string! filePath, int id, ushort size);
// Gets the icon count for the specified file.
public static int GetIconCount(string! filePath);
}
public static class SystemIcons
{
// Existing.
public static unsafe Icon GetStockIcon(StockIconId stockIcon, StockIconOptions options = StockIconOptions.Default);
// New. When asking for an explicit size (as opposed to just large/small) other options aren't available.
public static unsafe Icon GetStockIcon(StockIconId stockIcon, ushort size);
}
API Usage
// Get the first icon in regedit.exe at 32x32 size.
using Icon icon = Icon.ExtractIcon("regedit.exe", id: 0, size: 32);
// Get the icon from devenv.exe with the 1200 resource id, at the default large size
using Icon icon = Icon.ExtractIcon("devenv.exe", id: -1200);
Alternative Designs
Could potentially add additional constructors to Icon
, but behavior differences between string overloads would probably be confusing. For example, as the entire source file is not copied, resizing via Copy
is scaled, instead of reparsed.
Risks
Nothing notable.
Notes
- This API will scale to the requested size from available sizes. We’ll use
SHGetStockIconInfo
- I believe it tries to scale down from larger sizes. - We will not track the original source or update the copy constructors. When the full data is kept in the other APIs the original data is parsed again to try and find a matching size when using the copy constructors. With
Icon
s loaded through these APIs you’ll just get the current backing bitmap scaled if you change sizes with the copy constructors (as you would withHICON
constructedIcon
s andExtractAssociatedIcon
).
Issue Analytics
- State:
- Created 6 months ago
- Reactions:1
- Comments:24 (22 by maintainers)
Top GitHub Comments
@JeremyKuhne : Did you think about an information for the possible Icon sizes? The information about the sizes are inside of a MultiIcon file in the iconheader.
There is a very old interesting article in codeproject.com for MultiIcons with sources. See MultiIcons. I have done some bugfixes to the library in a Github project
Just a couple of new ones. We can’t migrate System.Drawing to CsWin32 until the Win32 metadata includes the gdiplusflat header. There is an active issue for it, I haven’t had time to try and get it working myself. There is also an issue with some of the shell APIs technically being platform specific. For our purposes we can use any definition, but there is no way to force pick one for AnyCPU in CsWin32.