在.NET程序开发中,经常需要将图像、图标和光标等资源嵌入到程序的汇编文件中。这样做的好处包括简化部署(减少需要管理的文件数量)、简化资源消耗(运行时文件不会丢失)等。本文将介绍一个名为“嵌入式图像提取器”的工具,它允许用户方便地查看、保存和复制嵌入在.NET程序中的图像资源。
在深入了解嵌入式图像提取器的工作方式之前,先来回顾一下什么是嵌入式资源。当创建一个程序的汇编文件时,可以在其中存储任意文件,比如BMP图像、XML文件等,这些文件被称为嵌入式资源。将资源嵌入到汇编文件中有几个好处,比如简化部署和资源消耗。
在Visual Studio.NET中,可以通过以下步骤轻松地将图像嵌入到程序的汇编文件中:
.NET框架为程序化检索嵌入式资源提供了支持。本文稍后将探讨这一功能的实现方式。
使用嵌入式图像提取器工具需要四个基本步骤:
提示:步骤1和2可以通过将目标汇编文件拖放到“Embedded Image Grabber.exe”上来合并。
负责从汇编文件中提取图像并在用户界面中显示的主要方法是LoadImagesFromAssembly。
private void LoadImagesFromAssembly(string assemblyPath)
{
// 尝试加载指定位置的汇编。
Assembly assembly = this.LoadAssembly(assemblyPath, true);
if (assembly == null) return;
this.currentAssembly = assembly;
// 如果有正在显示的图像,释放它们。
if (this.bindingSource.DataSource != null)
foreach (ImageInfo imgInfo in this.bindingSource.DataSource as List)
imgInfo.Dispose();
// 绑定到汇编中嵌入的每个图像的列表。
this.bindingSource.DataSource = this.ExtractImagesFromAssembly(this.currentAssembly);
}
如上方法所示,ImageGrabberForm使用BindingSource组件来协调数据绑定。BindingNavigator、DataGridView、PropertyGrid和PictureBox都绑定到绑定源,这使得GUI的同步图像导航功能非常容易实现。
private List ExtractImagesFromAssembly(Assembly assembly)
{
List imageInfos = new List();
foreach (string name in assembly.GetManifestResourceNames())
{
using (Stream stream = assembly.GetManifestResourceStream(name))
{
// 将资源视为图标。
try
{
Icon icon = new Icon(stream);
imageInfos.Add(new ImageInfo(icon, name));
continue;
}
catch (ArgumentException)
{
stream.Position = 0;
}
// 将资源视为光标。
try
{
Cursor cursor = new Cursor(stream);
imageInfos.Add(new ImageInfo(cursor, name));
continue;
}
catch (ArgumentException)
{
stream.Position = 0;
}
// 将资源视为图像。
try
{
Image image = Image.FromStream(stream);
// 如果图像是动画GIF,则不要将其添加到集合中,因为Image类无法处理它们,并且在显示图像时会抛出异常。
FrameDimension frameDim = new FrameDimension(image.FrameDimensionsList[0]);
bool isAnimatedGif = image.GetFrameCount(frameDim) > 1;
if (!isAnimatedGif)
imageInfos.Add(new ImageInfo(image, name));
else
image.Dispose();
continue;
}
catch (ArgumentException)
{
stream.Position = 0;
}
// 将资源视为资源文件。
try
{
// 流中的嵌入式资源不是图像,所以通过ResourceReader读取它并从中提取值。
using (IResourceReader reader = new ResourceReader(stream))
{
foreach (DictionaryEntry entry in reader)
{
if (entry.Value is Icon)
{
imageInfos.Add(new ImageInfo(entry.Value, name));
}
else if (entry.Value is Image)
{
imageInfos.Add(new ImageInfo(entry.Value, name));
}
else if (entry.Value is ImageListStreamer)
{
// 使用ImageListStreamer加载ImageList,并存储它包含的每个图像的引用。
using (ImageList imageList = new ImageList())
{
imageList.ImageStream = entry.Value as ImageListStreamer;
foreach (Image image in imageList.Images)
imageInfos.Add(new ImageInfo(image, name));
}
}
}
}
}
catch (Exception)
{
}
}
}
return imageInfos;
}