在应用程序中使用256 x 256 Windows Vista图标

时间:2008-10-21 01:05:38

标签: c# vb.net png icons

我有一个应用程序,我为它制作了一个256 x 256 Windows Vista图标。

我想知道如何在用作应用程序图标的ico文件中使用256x256 PNG文件并将其显示在表单上的图片框中。

我使用的是VB.NET,但C#中的答案很好。我想我可能不得不使用反射。

我不确定这在Windows XP中是否可行,并且可能需要Windows Vista API

5 个答案:

答案 0 :(得分:9)

今天,我为从Vista图标中提取256x256位图做了一个非常好的功能。

和你一样,Nathan W,我用它在“关于”框中将大图标显示为位图。例如,此代码将Vista图标作为PNG图像,并将其显示在256x256 PictureBox中:

picboxAppLogo.Image = ExtractVistaIcon(myIcon);

此函数将Icon对象作为参数。因此,您可以将它与任何图标一起使用 - 来自资源,来自文件,来自流等。 (请阅读以下有关提取EXE图标的信息)。

它运行在任何操作系统上,因为它使用任何Win32 API,它是 100%托管代码: - )

// 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;
}

重要!如果您想直接从EXE文件加载此图标,那么不能使用 Icon.ExtractAssociatedIcon(Application.ExecutablePath)作为参数,因为.NET函数ExtractAssociatedIcon()是如此愚蠢,它只提取32x32图标!

相反,您最好使用由Tsuda Kageyu(http://www.codeproject.com/KB/cs/IconExtractor.aspx)创建的整个 IconExtractor 类。您可以稍微简化此类,以使其更小。以这种方式使用 IconExtractor

// 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);
}

注意:我仍然在这里使用我的ExtractVistaIcon()函数,因为我不喜欢 IconExtractor 如何处理这项工作 - 首先,它使用IconExtractor.SplitIcon(icoAppIcon)提取所有图标格式),然后你必须知道确切的256x256图标索引才能获得所需的vista图标。所以,在这里使用我的ExtractVistaIcon()更快更简单:)

答案 1 :(得分:3)

找到信息here。要获得大型Vista图标,您需要使用Shell32的SHGetFileInfo方法。我已经复制了下面的相关文本,当然你要用“Assembly.GetExecutingAssembly()。Location”替换filename变量。

using System.Runtime.InteropServices;

我们将在调用SHGetFileInfo()时使用一堆常量来指定我们想要检索的图标的大小:

// 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;

SHFILEINFO结构非常重要,因为它将处理各种文件信息,其中包括图形图标。

// 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;
};

非托管代码的最终准备是定义SHGetFileInfo的签名,该签名位于流行的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);

现在我们已准备好所有内容,是时候调用函数并显示我们检索到的图标了。将要检索的对象是Icon类型(System.Drawing.Icon),但我们希望在PictureBox中显示它,因此我们将使用ToBitmap()方法将Icon转换为Bitmap。

但首先需要添加3个控件才能添加到窗体中,Button btnExtract的Text属性为“Extract Icon”,picIconSmall是PictureBox,picIconLarge也是PictureBox。那是因为我们会得到两个图标大小。现在双击Visual Studio的“设计”视图中的btnExtract,您将进入其Click事件。其中是代码的其余部分:

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();

    }
}

更新:找到更多信息here

答案 2 :(得分:2)

上述答案都没有处理Vista图标 - 只有小型(32x32)和大型(48x48)

有一个库可以处理Vista图标here

......由于采用双png alpha通道格式,它看起来相当复杂。

我会尝试在vb .net中做一个简明的回答,但这可能需要一些时间。

答案 3 :(得分:2)

从图片框中的ICO文件显示256 * 256 * 32图像有同样的问题,我发现SAL80的解决方案效率最高(而且几乎正常)。但是,原始代码不支持存储为BMP的图像(大图标通常是PNG,但并不总是......)。

这是我的版本以供将来参考。创建位图的代码也稍微简单一些:

    /// <summary>
    /// Extracts  the large Vista icon from a ICO file 
    /// </summary>
    /// <param name="srcBuf">Bytes of the ICO file</param>
    /// <returns>The large icon or null if not found</returns>
    private static Bitmap ExtractVistaIcon(byte[] srcBuf)
    {
        const int SizeIcondir = 6;
        const int SizeIcondirentry = 16;

        // Read image count from ICO header
        int iCount = BitConverter.ToInt16(srcBuf, 4);

        // Search for a large icon
        for (int iIndex = 0; iIndex < iCount; iIndex++)
        {
            // Read image information from image directory entry
            int iWidth = srcBuf[SizeIcondir + SizeIcondirentry * iIndex];
            int iHeight = srcBuf[SizeIcondir + SizeIcondirentry * iIndex + 1];
            int iBitCount = BitConverter.ToInt16(srcBuf, SizeIcondir + SizeIcondirentry * iIndex + 6);

            // If Vista icon
            if (iWidth == 0 && iHeight == 0 && iBitCount == 32)
            {
                // Get image data position and length from directory
                int iImageSize = BitConverter.ToInt32(srcBuf, SizeIcondir + SizeIcondirentry * iIndex + 8);
                int iImageOffset = BitConverter.ToInt32(srcBuf, SizeIcondir + SizeIcondirentry * iIndex + 12);

                // Check if the image has a PNG signature
                if (srcBuf[iImageOffset] == 0x89 && srcBuf[iImageOffset+1] == 0x50 && srcBuf[iImageOffset+2] == 0x4E && srcBuf[iImageOffset+3] == 0x47)
                {
                    // the PNG data is stored directly in the file
                    var x = new MemoryStream(srcBuf, iImageOffset, iImageSize, false, false);
                    return new Bitmap(x); 
                }

                // Else it's bitmap data with a partial bitmap header
                // Read size from partial header
                int w = BitConverter.ToInt32(srcBuf, iImageOffset + 4);
                // Create a full header
                var b = new Bitmap(w, w, PixelFormat.Format32bppArgb);
                // Copy bits into bitmap
                BitmapData bmpData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.WriteOnly, b.PixelFormat);
                Marshal.Copy(srcBuf, iImageOffset + Marshal.SizeOf(typeof(Bitmapinfoheader)), bmpData.Scan0, b.Width*b.Height*4);
                b.UnlockBits(bmpData);
                return b;
            }
        }

        return null;
    }

答案 4 :(得分:0)

查看可用的Windows icon functions。还有一个overview提到查询不同的图标大小。有一个Dream.In.Code论坛帖子用于在C#中使用API​​以及Pinvoke.net reference