在本文中,将探讨一个用于从EXE或DLL文件中提取图标的.NET类库。虽然通常使用Win32 API函数ExtractIconEx()来实现这一目的,但它只能提取少数几种图标变体。本文介绍的库能够提取所有变体并将它们分割成单独的对象。
大约六年前,发布了第一篇文章版本。老实说,最近对此感到有些羞愧。它几乎没有讨论任何内容,代码肮脏、有bug、效率低下等等。因此,重写了整篇文章、示例代码和库本身。重要的更新包括:
这个库由两个类组成:
IconExtractor是这个库的主要类。它与一个文件关联,并允许从中提取图标。IconUtil是一个杂项实用工具类。这是一个相当小而简单的库。因此,下面的简短示例涵盖了所有方法和属性。
C# using System; using System.Drawing; using TsudaKageyu; //------------------------------------------------------------------------------ // Usage of IconExtractor class: // Construct an IconExtractor object with a file. IconExtractor ie = new IconExtractor(@"D:\sample.exe"); // Get the full name of the associated file. string fileName = ie.FileName; // Get the count of icons in the associated file. int iconCount = ie.Count; // Extract icons individually. Icon icon0 = ie.GetIcon(0); Icon icon1 = ie.GetIcon(1); // Extract all the icons in one go. Icon[] allIcons = ie.GetAllIcons(); //------------------------------------------------------------------------------ // Usage of IconUtil class: // Split the variations of icon0 into separate icon objects. Icon[] splitIcons = IconUtil.SplitIcon(icon0); // Convert an icon into bitmap. Unlike Icon.ToBitmap() it preserves the transparency. Bitmap bitmap = IconUtil.ToBitmap(splitIcon[1]); // Get the bit count of an icon. int bitDepth = IconUtil.GetBitCount(splitIcon[2]);
这个库使用的基本策略是:
直接操作Icon对象的内部数据是困难和有风险的(这是可能的,但有点像黑魔法)。然而,一旦像下面这样转换成.ico文件,其结构就是Windows开发者的常识。
C# // Construct an Icon object. Icon icon0 = new Icon(...); // Convert an Icon object to an .ico file in memory. MemoryStream ms = new MemoryStream(); icon0.Save(ms); // Manipulate the in-memory file. // Convert the in-memory file into an Icon object. Icon icon1 = new Icon(ms);
实际上,Icon对象在内部存储了自己的文件图像。因此,这个库直接访问内部缓冲区,而不是保存到MemoryStream。
.ico文件是一些图片的归档。它由图片数量、每张图片的简要信息和实际图片组成。它有一个相当直接的格式,所以很容易操作。下面的图片大致说明了.ico文件的结构以及如何分割和合并它。
如果需要更详细的信息,请参考微软的官方文档。
C# private byte[] GetDataFromResource(IntPtr hModule, IntPtr type, IntPtr name) { // Load the binary data from the specified resource. IntPtr hResInfo = NativeMethods.FindResource(hModule, name, type); if (hResInfo == IntPtr.Zero) throw new Win32Exception(); IntPtr hResData = NativeMethods.LoadResource(hModule, hResInfo); if (hResData == IntPtr.Zero) throw new Win32Exception(); IntPtr pResData = NativeMethods.LockResource(hResData); if (pResData == IntPtr.Zero) throw new Win32Exception(); uint size = NativeMethods.SizeofResource(hModule, hResInfo); if (size == 0) throw new Win32Exception(); byte[] buf = new byte[size]; Marshal.Copy(pResData, buf, 0, buf.Length); return buf; }