I have a file, "multi.ico" which has four images of different sizes (16x16, 24x24, 32x32, 48x48). Each sized image is a different color, to make it easy to tell which size we are dealing with. This file has been inserted into a resource file "Multi.resources" using Lutz Roeder's .NET Resourcer.

I need to be able to extract the ico from the .resource file in such a way as to keep all four sizes of images. Ideally, I would like to have a wrapper class around the .ico object which allows selection of the image of the proper size.

I have a working wrapper that I can use with a standalone .ico file (multi.ico), but my company system has a lot of images, and we need to be able to bundle them into .resource files for distribution.

Can anybody show me a piece of code which would take Multi.resources (file attached) and extract the 16x16 version of multi.ico (for id purposes it is a blue number 16). I can't seem to get at anything but the yellow 32x32 sized image.

BTW, the attached file needs to be renamed to .resources, I just renamed it because .resources was not on the list of approved file extensions...

Dani AI

Generated

Short version: read the raw ICO bytes out of the .resources file, parse the ICO directory, extract the image blob that corresponds to 16x16, then either wrap that blob into a single-entry .ico or load it as an Image. The reason the existing wrapper returns only the yellow 32x32 is that a multi-image Icon object (or Icon.ToBitmap()) will present a single representation, not let callers pick an arbitrary entry from the ICO group.

The following compact C# approach does the work: use ResourceReader.GetResourceData to obtain the raw .ico bytes, parse the ICONDIR + ICONDIRENTRY table, slice out each image blob and write single-entry .ico files (or convert PNG/BMP blobs directly). The code below demonstrates the parsing and extraction core (target: .NET Framework; requires System.Drawing for optional conversion to PNG):

using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Resources;
using System.Drawing;
using System.Drawing.Imaging;

class IconDirEntry {
  public byte Width, Height, ColorCount, Reserved;
  public ushort Planes, BitCount;
  public uint BytesInRes, ImageOffset;
}

void ExtractIconsFromResources(string resourcesPath, string resourceKey, string outDir)
{
  using (var rr = new ResourceReader(resourcesPath)) {
    var en = rr.GetEnumerator();
    while (en.MoveNext()) {
      var key = (string)en.Key;
      if (!key.Equals(resourceKey, StringComparison.OrdinalIgnoreCase)) continue;
      string typeName;
      byte[] icoBytes = rr.GetResourceData(key, out typeName);
      ParseAndSave(icoBytes, outDir, Path.GetFileNameWithoutExtension(resourceKey));
      break;
    }
  }
}

void ParseAndSave(byte[] icoBytes, string outDir, string baseName)
{
  using (var ms = new MemoryStream(icoBytes))
  using (var br = new BinaryReader(ms)) {
    br.ReadUInt16(); br.ReadUInt16(); ushort count = br.ReadUInt16();
    var entries = new List<IconDirEntry>();
    for (int i = 0; i < count; i++) {
      var e = new IconDirEntry {
        Width = br.ReadByte(), Height = br.ReadByte(), ColorCount = br.ReadByte(), Reserved = br.ReadByte(),
        Planes = br.ReadUInt16(), BitCount = br.ReadUInt16(), BytesInRes = br.ReadUInt32(), ImageOffset = br.ReadUInt32()
      };
      entries.Add(e);
    }
    for (int i = 0; i < entries.Count; i++) {
      var e = entries[i];
      ms.Position = e.ImageOffset;
      byte[] img = br.ReadBytes((int)e.BytesInRes);
      int w = e.Width == 0 ? 256 : e.Width;
      string outIco = Path.Combine(outDir, $"{baseName}_{w}x{(e.Height==0?256:e.Height)}.ico");
      using (var outMs = new MemoryStream()) using (var bw = new BinaryWriter(outMs)) {
        bw.Write((ushort)0); bw.Write((ushort)1); bw.Write((ushort)1);
        bw.Write(e.Width); bw.Write(e.Height); bw.Write(e.ColorCount); bw.Write(e.Reserved);
        bw.Write(e.Planes); bw.Write(e.BitCount); bw.Write(e.BytesInRes); bw.Write((uint)(6 + 16));
        bw.Write(img);
        File.WriteAllBytes(outIco, outMs.ToArray());
      }
      // optional: convert to PNG if the image blob is PNG:
      if (img.Length > 8 && img[0]==0x89 && img[1]==0x50 && img[2]==0x4E && img[3]==0x47)
        using (var msImg = new MemoryStream(img)) using (var bmp = Image.FromStream(msImg)) bmp.Save(Path.ChangeExtension(outIco,"png"), ImageFormat.Png);
    }
  }
}

Troubleshooting notes and caveats: ’s rename-to-.resources step is required before opening the file. If ResourceReader enumerator does not show the expected key, list all keys first to find the exact name. Treat Width/Height==0 as 256. If the resource appears as a serialized Icon object, GetResourceData still yields the raw ICO bytes; prefer GetResourceData over relying on the object returned by enumerator.Value. If the attachment fails to open (as noted), confirm the .resources file is intact and was not trimmed by the forum; test locally by opening with ResourceReader or a resource editor. On non-Windows platforms, System.Drawing may need native dependencies.

Recommended Answers

All 2 Replies

this attachment is damaged and can't be open

have you checked

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.