Использование 256 x 256 значков Windows Vista в приложении - PullRequest
10 голосов
/ 21 октября 2008

У меня есть приложение, для которого я сделал значок Windows Vista 256 x 256.

Мне было интересно, как я смогу использовать PNG-файл размером 256x256 в файле ico, который используется в качестве значка приложения, и показать его в графическом окне на форме.

Я использую VB.NET, но ответы на C # в порядке. Я думаю, что мне, возможно, придется использовать отражение.

Я не уверен, возможно ли это даже в Windows XP и может потребоваться API-интерфейсы Windows Vista

Ответы [ 5 ]

9 голосов
/ 22 декабря 2009

Сегодня я сделал очень хорошую функцию для извлечения растровых изображений 256x256 из значков Vista .

Как и вы, Натан В., я использую его для отображения большого значка в виде растрового изображения в поле «О программе». Например, этот код получает значок Vista в виде изображения PNG и отображает его в PictureBox 256x256:

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!

Вместо этого вам лучше использовать весь класс IconExtractor , созданный Цудой Кагею (http://www.codeproject.com/KB/cs/IconExtractor.aspx).. Вы можете немного упростить этот класс, чтобы уменьшить его. Используйте 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, чтобы получить желаемый значок перспективы. Итак, использование моего ExtractVistaIcon () здесь намного быстрее и проще:)

3 голосов
/ 21 октября 2008

Найдена информация здесь . Чтобы получить большой значок Vista, вам нужно использовать метод Shell32 SHGetFileInfo. Я скопировал соответствующий текст ниже, конечно, вы захотите заменить переменную имени файла на «Assembly.GetExecutingAssembly (). Location».

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, поэтому мы преобразуем Icon в растровое изображение, используя метод ToBitmap ().

Но, прежде всего, есть 3 элемента управления, которые необходимо добавить в форму: кнопка btnExtract, которая имеет «Extract Icon» для своего свойства Text, picIconSmall, которая является PictureBox, и picIconLarge, который также является PictureBox. Это потому, что мы получим два размера иконок. Теперь дважды щелкните btnExtract в представлении «Дизайн» Visual Studio, и вы попадете на событие 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();

    }
}

ОБНОВЛЕНИЕ: найдено больше информации здесь .

2 голосов
/ 01 февраля 2012

Имея ту же проблему отображения изображения 256 * 256 * 32 из файла ICO в графическом окне, я нашел решение из 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;
    }
2 голосов
/ 21 октября 2008

Ни один из приведенных выше ответов не касается значков Vista - только маленькие (32x32) и большие (48x48)

Существует библиотека, которая обрабатывает значки Vista здесь

... это выглядит довольно сложно из-за формата двухканального альфа-канала.

Я постараюсь сделать краткий ответ в vb .net, но это может занять некоторое время.

0 голосов
/ 21 октября 2008

Посмотрите на функции Windows , доступные . Также есть обзор , в котором упоминаются запросы для разных размеров значков. Существует Dream.In.Code ветка форума для использования API в C #, а также Pinvoke.net ссылка .

...