从程序集中提取嵌入的图像






4.98/5 (61投票s)
一个允许您查看、保存和复制嵌入在任何程序集中的图像的工具。
引言
本文介绍了一个简单的实用程序“嵌入式图像抓取器”,它允许您查看、保存和复制嵌入在程序集中的图像、图标和光标。该实用程序是针对 .NET 框架 v2.0 编译的,但如果需要,核心功能可以轻松移植到 v1.x。
背景
在查看“嵌入式图像抓取器”的工作原理之前,让我们花点时间回顾一下什么是嵌入式资源。当创建程序集时,可以将任意文件(如 BMP、XML 文件等)存储在其中。这些文件称为嵌入式资源。将资源嵌入到程序集中有几个好处,例如:
- 简化部署(需要管理的更少的文件)。
- 简化资源消耗(运行时不会丢失文件的可能性)。
您可以通过以下步骤轻松地将图像嵌入到 Visual Studio .NET 的程序集中:
- 将图像文件添加到项目中。
- 在“解决方案资源管理器”中,右键单击图像文件,然后从上下文菜单中选择“属性”。
- 在“属性”窗口中,为“生成操作”属性选择“嵌入的资源”。
- 将项目编译成程序集。
正如您可能想象到的,.NET 框架支持以编程方式检索嵌入式资源。我们将在文章后面探讨它的实现方式。
使用该实用程序
使用此工具需要四个基本步骤:
- 运行 Embedded Image Grabber.exe。
- 单击“打开”按钮找到包含您要提取的图像的程序集。
- 通过窗口顶部的
BindingNavigator
导航到您感兴趣的图像。 - 单击“保存”或“复制”按钮将图像持久化到磁盘或剪贴板。
提示 - 可以通过将目标程序集拖放到 Embedded Image Grabber.exe 上来合并步骤 1 和 2。
- 保存选项 - 保存嵌入的图标或光标时,您可以选择将其保存为原始文件类型或位图。另存为对话框将默认使用与嵌入式资源原始类型相对应的扩展名。
- 通过拖放打开 - 除了可以通过将程序集拖放到 Embedded Image Grabber.exe 上来打开已加载程序集的应用程序之外,您还可以在应用程序运行时通过拖放来加载程序集。只需将程序集拖放到窗体上,它包含的嵌入式图像就会被加载。
- “所有图像”选项卡 - 提供程序集中每个图像的网格视图。这使得搜索图像更快。
- 属性视图 - 一个
PropertyGrid
,显示有关当前图像的详细信息。单击工具栏上的最右侧按钮以显示/隐藏此视图。 - 上下文菜单 - 提供对保存、复制或显示/隐藏图像属性的快速访问。
- 视图选项 - 当选择“单个图像”选项卡时,工具栏上会有一个组合框,允许您更改当前图像的渲染方式(例如缩放、居中等)。
工作原理
负责从程序集中提取图像并在用户界面中显示它们的主要方法是 LoadImagesFromAssembly
。
private void LoadImagesFromAssembly( string assemblyPath )
{
// Try to load the assembly at the specified location.
Assembly assembly = this.LoadAssembly( assemblyPath, true );
if( assembly == null )
return;
this.currentAssembly = assembly;
// Dispose of the images currently being displayed, if any.
if( this.bindingSource.DataSource != null )
foreach( ImageInfo imgInfo in this.bindingSource.DataSource
as List<ImageInfo> )
imgInfo.Dispose();
// Bind to a list of every image embedded in the assembly.
this.bindingSource.DataSource =
this.ExtractImagesFromAssembly( this.currentAssembly );
}
如上文所示,ImageGrabberForm
使用 BindingSource
组件来协调数据绑定。BindingNavigator
、DataGridView
、PropertyGrid
和 PictureBox
都绑定到绑定源,这使得 GUI 的同步图像导航方面极其易于实现。
正如您可能猜到的,从程序集中提取图像的实际工作在 ExtractImagesFromAssembly
方法中。
private List<ImageInfo> ExtractImagesFromAssembly( Assembly assembly )
{
List<ImageInfo> imageInfos = new List<ImageInfo>();
foreach( string name in assembly.GetManifestResourceNames() )
{
using( Stream stream = assembly.GetManifestResourceStream( name ) )
{
// Treat the resource as an icon.
try
{
Icon icon = new Icon( stream );
imageInfos.Add( new ImageInfo( icon, name ) );
continue;
}
catch( ArgumentException )
{
stream.Position = 0;
}
// Treat the resource as a cursor.
try
{
Cursor cursor = new Cursor( stream );
imageInfos.Add( new ImageInfo( cursor, name ) );
continue;
}
catch( ArgumentException )
{
stream.Position = 0;
}
// Treat the resource as an image.
try
{
Image image = Image.FromStream( stream );
// If the image is an animated GIF, do not add it to the
// collection because the Image class cannot handle them and
// will throw an exception when the image is displayed.
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;
}
// Treat the resource as a resource file.
try
{
// The embedded resource in the stream is not an image, so
// read it into a ResourceReader and extract the values
// from there.
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 )
{
// Load an ImageList with the ImageListStreamer and
// store a reference to every image it contains.
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;
}
上面看到的代码会为指定的程序集中的每个命名资源打开一个流,然后要么从流中创建一个 Icon
,如果失败,则创建一个 Cursor
,或者一个 Image
,如果所有方法都失败,它将通过 System.Resources.ResourceReader
读取嵌入式资源的内容。资源读取器对于从资源文件(.resx)中提取图像、图标和存储在 ImageList
中的图像是必需的。ImageInfo
辅助类用于存储图像和相关信息。
历史
- 2006/3/25 - 创建文章。
- 2006/3/26 - 修正了图像保存代码。在保存图像之前,必须将其复制到
Bitmap
,否则可能会抛出异常。 - 2006/3/27 - 添加了从
ResourceReader
提取图像的代码。还添加了提取光标的代码。 - 4/1/2006
- 添加了关于实用程序中 其他功能 的部分。
- 修复了嵌入式动画 GIF 导致应用程序崩溃的错误。
- 更新了代码片段。
- 提供了实用程序和源代码的新下载。