tags:

views:

2978

answers:

4

Hi all,

I have an application which I have made a 256 x 256 vista icon for.

I was wondering how I would be able to use the 256x256 png in the ico file used as the application icon and show it in a picture box on a form.

I am using VB.net but answers in C# are fine. I'm thinking I may have to use reflection.

EDIT! Not sure if this is even possible in XP, may need vista APIs

Thanks.

+3  A: 

Found info here. To get the large Vista icon, you need to use Shell32's SHGetFileInfo method. I've copied the relevant text below, of course you'll want to replace the filename variable with "Assembly.GetExecutingAssembly().Location".

using System.Runtime.InteropServices;

A bunch of constants we will use in the call to SHGetFileInfo() to specify the size of the icon we wish to retrieve:

// Constants that we need in the function call
private const int SHGFI_ICON = 0x100;
private const int SHGFI_SMALLICON = 0x1;
private const int SHGFI_LARGEICON = 0x0;

The SHFILEINFO structure is very important as it will be our handle to various file information, among which is the graphic icon.

// This structure will contain information about the file
public struct SHFILEINFO
{
    // Handle to the icon representing the file
    public IntPtr hIcon;
    // Index of the icon within the image list
    public int iIcon;
    // Various attributes of the file
    public uint dwAttributes;
    // Path to the file
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
    public string szDisplayName;
    // File type
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
    public string szTypeName;
};

The final preparation for the unmanaged code is to define the signature of SHGetFileInfo, which is located inside the popular Shell32.dll:

// The signature of SHGetFileInfo (located in Shell32.dll)
[DllImport("Shell32.dll")]
public static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, int cbFileInfo, uint uFlags);

Now that we have everything prepared, it's time to make the call to the function and display the icon that we retrieved. The object that will be retrieved is an Icon type (System.Drawing.Icon) but we want to display it in a PictureBox so we'll convert the Icon to a Bitmap using the ToBitmap() method.

But first of all there are 3 controls you need to add to the form, a Button btnExtract that has "Extract Icon" for its Text property, picIconSmall which is a PictureBox and a picIconLarge which is also a PictureBox. That's because we will get two icons sizes. Now double click btnExtract in Visual Studio's Design view and you'll get to its Click event. Inside it is the rest of the code:

private void btnExtract_Click(object sender, EventArgs e)
{
    // Will store a handle to the small icon
    IntPtr hImgSmall;
    // Will store a handle to the large icon
    IntPtr hImgLarge;

    SHFILEINFO shinfo = new SHFILEINFO();

    // Open the file that we wish to extract the icon from
    if(openFile.ShowDialog() == DialogResult.OK)
    {
        // Store the file name
        string FileName = openFile.FileName;
        // Sore the icon in this myIcon object
        System.Drawing.Icon myIcon;

        // Get a handle to the small icon
        hImgSmall = SHGetFileInfo(FileName, 0, ref shinfo, Marshal.SizeOf(shinfo), SHGFI_ICON | SHGFI_SMALLICON);
        // Get the small icon from the handle
        myIcon = System.Drawing.Icon.FromHandle(shinfo.hIcon);
        // Display the small icon
        picIconSmall.Image = myIcon.ToBitmap();

        // Get a handle to the large icon
        hImgLarge = SHGetFileInfo(FileName, 0, ref shinfo, Marshal.SizeOf(shinfo), SHGFI_ICON | SHGFI_LARGEICON);
        // Get the large icon from the handle
        myIcon = System.Drawing.Icon.FromHandle(shinfo.hIcon);
        // Display the large icon
        picIconLarge.Image = myIcon.ToBitmap();

    }
}

UPDATE: found even more info here.

TheSoftwareJedi
@SoftwareJedi Thanks I'm trying it out now.
Nathan W
It uses WinAPI, and it will NOT work in WinXP.
SLA80
A: 

Have a look at the Windows icon functions that are available. There's also an overview that mentions querying for different icon sizes. There's a Dream.In.Code forum thread for using the APIs in C# as well as a Pinvoke.net reference.

Mark Cidade
+2  A: 

None of the above answers handle Vista Icons - only small (32x32) and large (48x48)

There is a library that handles Vista Icons here

...it looks quite complicated due to the dual-png alpha channel format.

I will try to make a concise answer in vb .net but it may take some time.

Damien
My solution (ExtractVistaIcon()) us based mostly on much simplier class "IconExtractor" (much simplier than "IconLib"). I have updated my answer, added info about getting 256x256 icon directly from executable.
SLA80
+3  A: 

Today, I made a very nice function for extracting the 256x256 Bitmaps from Vista icons.

Like you, Nathan W, I use it to display the large icon as a Bitmap in "About" box. For example, this code gets Vista icon as PNG image, and displays it in a 256x256 PictureBox:

picboxAppLogo.Image = ExtractVistaIcon(myIcon);

This function takes Icon object as a parameter. So, you can use it with any icons - from resources, from files, from streams, and so on. (Read below about extracting EXE icon).

It runs on any OS, because it does not use any Win32 API, it is 100% managed code :-)

// Based on: http://www.codeproject.com/KB/cs/IconExtractor.aspx
// And a hint from: http://www.codeproject.com/KB/cs/IconLib.aspx

Bitmap ExtractVistaIcon(Icon icoIcon)
{
    Bitmap bmpPngExtracted = null;
    try
    {
        byte[] srcBuf = null;
        using (System.IO.MemoryStream stream = new System.IO.MemoryStream())
            { icoIcon.Save(stream); srcBuf = stream.ToArray(); }
        const int SizeICONDIR = 6;
        const int SizeICONDIRENTRY = 16;
        int iCount = BitConverter.ToInt16(srcBuf, 4);
        for (int iIndex=0; iIndex<iCount; iIndex++)
        {
            int iWidth  = srcBuf[SizeICONDIR + SizeICONDIRENTRY * iIndex];
            int iHeight = srcBuf[SizeICONDIR + SizeICONDIRENTRY * iIndex + 1];
            int iBitCount   = BitConverter.ToInt16(srcBuf, SizeICONDIR + SizeICONDIRENTRY * iIndex + 6);
            if (iWidth == 0 && iHeight == 0 && iBitCount == 32)
            {
                int iImageSize   = BitConverter.ToInt32(srcBuf, SizeICONDIR + SizeICONDIRENTRY * iIndex + 8);
                int iImageOffset = BitConverter.ToInt32(srcBuf, SizeICONDIR + SizeICONDIRENTRY * iIndex + 12);
                System.IO.MemoryStream destStream = new System.IO.MemoryStream();
                System.IO.BinaryWriter writer = new System.IO.BinaryWriter(destStream);
                writer.Write(srcBuf, iImageOffset, iImageSize);
                destStream.Seek(0, System.IO.SeekOrigin.Begin);
                bmpPngExtracted = new Bitmap(destStream); // This is PNG! :)
                break;
            }
        }
    }
    catch { return null; }
    return bmpPngExtracted;
}

IMPORTANT! If you want to load this icon directly from EXE file, then you CAN'T use Icon.ExtractAssociatedIcon(Application.ExecutablePath) as a parameter, because .NET function ExtractAssociatedIcon() is so stupid, it extracts ONLY 32x32 icon!

Instead, you better use the whole IconExtractor class, created by Tsuda Kageyu (http://www.codeproject.com/KB/cs/IconExtractor.aspx). You can slightly simplify this class, to make it smaller. Use IconExtractor this way:

// Getting FILL icon set from EXE, and extracting 256x256 version for logo...
using (TKageyu.Utils.IconExtractor IconEx = new TKageyu.Utils.IconExtractor(Application.ExecutablePath))
{
    Icon icoAppIcon = IconEx.GetIcon(0); // Because standard System.Drawing.Icon.ExtractAssociatedIcon() returns ONLY 32x32.
    picboxAppLogo.Image = ExtractVistaIcon(icoAppIcon);
}

Note: I'm still using my ExtractVistaIcon() function here, because I don't like how IconExtractor handles this job - first, it extracts all icon formats by using IconExtractor.SplitIcon(icoAppIcon), and then you have to know the exact 256x256 icon index to get the desired vista-icon. So, using my ExtractVistaIcon() here is much faster and simplier way :)

SLA80
Looks promising but doesn't work for me. I'm running Windows 7. Are you certain that you've tested this code?
Damien
Actually, it extracts a 256x256 image from a Vista ico file which is good but it doesn't solve the problem of getting the full set of icon images from the executable..ExtractAssociatedIcon only extracts the 32x32 image from the file.
Damien
Yes, sorry, I forgot to update my solution. Now it is fixed, and updated with special note about getting the FULL icon set directly from EXE file.
SLA80
Have you got any idea why this routine does not work for different bit depths? For instance, if I amend the code to ignore the bit depth check, and try it with a 1bit 256x256 icon, I get a 'parameter not valid' exception when it tries to create the bitmap.
Jules
It works if I first create an icon stream and then convert it to a bitmap. Not sure why, but it works.
Jules
Jules, this code is configured to extracto only 32-bit version of 256x256 icon (iBitCount == 32). If you want to extract 1-bit 256x256 icon, you must change this code (iBitCount == 1), and it probably will work.
SLA80