我有一个应用程序,我为它制作了一个256 x 256 Windows Vista图标。
我想知道如何在用作应用程序图标的ico文件中使用256x256 PNG文件并将其显示在表单上的图片框中。
我使用的是VB.NET,但C#中的答案很好。我想我可能不得不使用反射。
我不确定这在Windows XP中是否可行,并且可能需要Windows Vista API
答案 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。