65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.98/5 (61投票s)

2006年3月25日

CPOL

4分钟阅读

viewsIcon

294457

downloadIcon

19055

一个允许您查看、保存和复制嵌入在任何程序集中的图像的工具。

Embedded Image Grabber tool

引言

本文介绍了一个简单的实用程序“嵌入式图像抓取器”,它允许您查看、保存和复制嵌入在程序集中的图像、图标和光标。该实用程序是针对 .NET 框架 v2.0 编译的,但如果需要,核心功能可以轻松移植到 v1.x。

背景

在查看“嵌入式图像抓取器”的工作原理之前,让我们花点时间回顾一下什么是嵌入式资源。当创建程序集时,可以将任意文件(如 BMP、XML 文件等)存储在其中。这些文件称为嵌入式资源。将资源嵌入到程序集中有几个好处,例如:

  • 简化部署(需要管理的更少的文件)。
  • 简化资源消耗(运行时不会丢失文件的可能性)。

您可以通过以下步骤轻松地将图像嵌入到 Visual Studio .NET 的程序集中:

  1. 将图像文件添加到项目中。
  2. 在“解决方案资源管理器”中,右键单击图像文件,然后从上下文菜单中选择“属性”。
  3. 在“属性”窗口中,为“生成操作”属性选择“嵌入的资源”。
  4. 将项目编译成程序集。

正如您可能想象到的,.NET 框架支持以编程方式检索嵌入式资源。我们将在文章后面探讨它的实现方式。

使用该实用程序

使用此工具需要四个基本步骤:

  1. 运行 Embedded Image Grabber.exe
  2. 单击“打开”按钮找到包含您要提取的图像的程序集。
  3. 通过窗口顶部的 BindingNavigator 导航到您感兴趣的图像。
  4. 单击“保存”或“复制”按钮将图像持久化到磁盘或剪贴板。

提示 - 可以通过将目标程序集拖放到 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 组件来协调数据绑定。BindingNavigatorDataGridViewPropertyGridPictureBox 都绑定到绑定源,这使得 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 导致应用程序崩溃的错误。
    • 更新了代码片段。
    • 提供了实用程序和源代码的新下载。
© . All rights reserved.