IconLib: Icons Unfolded (MultiIcon and Windows Vista supported)

Introduction

At some moment in the last month, I needed to create an .ICO file on the fly with a couple images inside, preferable it was code in C#, The .NET framework 2.0 only supports HICON; that, basically, is one icon with just a single image in it. I was frustrated when I found no Icon Editor with source code; the only thing I found was closed commercial products charging from $19 to $39 and not exposing APIs at all. So, the only solution was to create my own library capable of creating and parsing ICO files.

I believe in open-source code and I though I could help the developer community by sharing this knowledge. Also open-source pushes companies and commercial products to go farther.

After the work was done, I read about ICL files (Icon Libraries), those can contain many icons inside a file, and I decided to support that too. The same happened with EXE/DLLs, but I still decided to support Windows Vista Icons. All this was really hard work and a lot of headaches because there is no much information exposed, so I spent a lot of time reversing-engineering and researching over the net. I hope it can be as useful for you as it is for me.

Note: As with every new project, many things can happen. Not every case can be tested and many things you don't see until after they are tested. It is a very fresh project; if there is something that doesn't work or you think should work differently than it does, before give your vote write and give me the chance to fix it. In this way, we both get the benefit of having more stable code and creating a more complete library; and, at the same time, you get my thanks if that helps.

Also, I included one libraries as sample. I borrowed some icons from Windows Vista; for the 256x256 versions I put a watermark because the icons has copyright ownership. Hopefully, I won't have trouble with that.

Objective

The objective of the library is to create an abstraction layer from the different file formats and provide an interface to allow icon modification without the hassle of knowing internal file formats.

Formats Currently Supported

  • ICO: Read and write icons with different image sizes and depths
  • ICL: Read and write icons inside the icon library
  • DLL: Read DLLs and export to a new DLL
  • EXE: Import supported
  • OCX: Import supported
  • CPL: Import supported
  • SRC: Import supported

MultiIcon

Iconlib exposes three different objects:

  • MultiIcon: The only object that can be instantiated in the library. Once a MultiIcon object is created, it exposes APIs to load and save in the file system or streams with standard formats as ICO/ICL/DLL.
  • SingleIcon: A single icon inside MultiIcon; it allows you to add/remove images in it.
  • IconImage: A single image inside SingleIcon; at this point, IconImage exposes the icon lowest resources like the XOR (Image) and the AND Image (Mask). Also, it exposes an Icon property that will basically construct a .NET Icon created from the XOR and AND image from where you can get a HICON handle.

As you can see, there is a hierarchical structure. Basically, a MultiIcon contains Icons and an Icon contains Images.

Library Objects Diagram

The library contains many classes and structs but only exposes the three important classes;that's all the developer needs to control the complete behavior of the library. The rest are all internal; many classes/structs and methods are not safe to be exposed to the developer. For that reason, I recommend keeping IconLib as a separated project because, if the developer incorporates the source in his/her project, all the internal classes/structs/methods will be exposed and probably could not be used properly.

I cannot give support for the library when it is not used the way it was designed. I'm providing the source-code as a nice gesture because I believe in open-source code. I hope you will make good use of it without ripping off the source from where it belongs.

Icon Format

Before I started IconLib, I had no clue about how icons work, but was not before I found an excellent article. Although it is out of date with the coming of Windows Vista icons, it is very precise in explaining how Icons format files work.

Something to watch for: On my first version, I followed the icon format details, but the library could not load some of the icons I was testing. When I went deep to the bytes I noticed that much information was missing from the directory entry.

I tested those Icons with another product and I could see that, for example, one popular product had no problem opening this kind of icon; that is because every icon directory entry points to a ICONIMAGE struct. This ICONIMAGE struct has a BITMAPINFOHEADER struct that contains more information than the icon directory entry itself, so basically, by using the information from the BITMAPINFOHEADER, I could reconstruct the information in the directory entry.

The same rule cannot be applied to Windows Vista Icons because those images doesn't contain a BITMAPINFOHEADER struct anymore. So, if some information is missing from the directory entry, the icon image becomes invalid.

Anyway, reconstructing the icon directory entry is a plus and discarding icon images not properly constructed is acceptable, No company should provide icons with missing information in the headers.

NE Format (ICL)

The NE Format is the popular format to store icon libraries. This format originality was used for Executables on the 16-bit version of Windows.

You can get more information for NE format from the Microsoft web site.

This was the more challenging part of the project. When I started to research about ICL, I have no clue that these were 16-bit DLLs. I couldn't find any data about this extension; a couple days later, I almost dropped the project, but I read someplace that ICLs are 16-bit with resources inside. My quest to recover resources from a 16-bit DLL started. So far, my only next objective was trying to load a 16-bit DLL in memory; of course, at first I tried to load the library with the standard Win32API LoadLibrary, LoadLibraryEx, but it failed with:

193 - ERROR_BAD_EXE_FORMAT (Is not a valid application.)

I'm no expert in Kernel memory allocation, but I guess it's because in Win32 the memory is protected between applications and in 16-bit it is not. So, when I tried to allocation memory for 16-bit, the OS rejected the operation.

The next step was trying to load the ICL (16-bit DLL) in memory using just 16 bits APIs. If you read the MSDN WIN32 API, the only API left for 16-bit is Loadmodule. When I tried to use it, it loaded the library but immediately Windows starts to give strange message boxes, such as "Not enough memory to run 16-bit applications" or things like that. I wrote in MS forums and other forums, but couldn't find anything really helpful about how I could get those resources. At that time, it was very clear that I could not load a 16-bit DLL in memory and that I needed to create my own NE parser/'linker'.

The Microsoft article about NE Format (New Executable) is an excellent source. It describes, in detail, every field in the file.

A NE format file starts with an IMAGE_DOS_HEADER. This header is there to keep compatibility with MS-DOS OS. This header also contains some specific fields to indicate the existence of a newly segmented file format. IMAGE_DOS_HEADER usually contains a valid executable program to run on MS-DOS. This program is called a stub program and usually it just prints the message 'This program cannot run on MS-DOS' on the screen.

After you read the IMAGE_DOS_HEADER, the first thing to do is to know whether this is a valid header. Usually, every file contains what is called a Magic Number because the data store in that field is not relevant to the program but it contains a signature to describe the type of the file.

You can find Magic Numbers almost everywhere. The magic number for the IMAGE_DOS_HEADER is 0x5A4D; this represent the chars 'MZ', and it stands by 'Mark Zbikowski'. He is a Microsoft Architect and started to work with MS a few years after its inception. Probably, he could never think that his signature was going to appear thousands of times in almost every personal computer in the world. If the magic number is 'MZ', the only extra field you care about is the e_lfanew. This header is the offset to the new EXE header, NE Header.

You seek in the file for this offset, and then at this point, you read a new header. The header is IMAGE_OS2_HEADER and it contains all information about the program to be loaded in memory.

The first thing to do is to load the magic number again, but this time the magic number must be 0x454E; it means 'NE'. If the signature matches, you can continue analyzing the rest of the headers. At this point, the more important field is ne_rsrctab; this field contains the offset of the resource table. This offset is the number of bytes you have to jump from the beginning of this header to be in position to read the resource table.

If everything went well, you are ready to read the resource table.

The first field of the Resource Table is the align shift. Usually, you find the explanation as 'The alignment shift count for resource data. When the shift count is used as an exponent of 2, the resulting value specifies the factor, in bytes, for computing the location of a resource in the executable file.'

In my own words, this field was tricky to understand how it works. It was created for compatibility with MS-DOS, and it will contain the multiply factor necessary to reach the resource. As you will see, the resource offset is a variably type ushort; this means that it can address only 64 Kb (65536). Actually, almost every file is bigger than that, and here is where the 'alignment shift' field comes into play. Alignment shift is an ushort and 'usually' it is in the range of 2 to 10. This number is the number of times you have to shift the number 1 to the left.

For example, an alignment shift of 5 means 1 << 5, which is equal to 32.

An alignment shift of 10 means 1 << 10 = 1024.

Now, with the virtual offset address from the resource table, you multiply for the result shift value and you get the real offset address in the file. For example, if the resource is located at the virtual address 0x2000 and the alignment shift is 5, you get the following:

Realoffset = (1 << 5) * 0x2000
Realoffset = 32 * 0x2000
Realoffset = 0x40000

The real offset of this resource is at 262144 (0x40000).

Wow, this is cool, right? Because you just use a ushort, you can locate a resource at any position. Okay, now you wonder, where is the trick?

The trick is, for example, if you use a shift alignment of 5; that means the minimum addressable space is 32 bytes (1 << 5). This means that, if you want to allocate 10 bytes with this method, 32 bytes will be allocated and just the first 10 will be used; another 22 bytes will be wasted.

Now, if you take the shift alignment as 0, you won't waste space because the virtual address will match with the real address. It is not so easy, and that works only if the resource is located in the range of the first 64Kb space. So to make it clear, this shift alignment is directly proportional to the file size.

The next table shows the maximum file sizes you can get with different shift alignments:

(1 << 0) * (2 ^ 16) = 64KB
(1 << 1) * (2 ^ 16) = 128KB
(1 << 2) * (2 ^ 16) = 256KB
(1 << 3) * (2 ^ 16) = 512KB
(1 << 4) * (2 ^ 16) = 1MB
(1 << 5) * (2 ^ 16) = 2MB
(1 << 6) * (2 ^ 16) = 4MB
(1 << 7) * (2 ^ 16) = 8MB
(1 << 8) * (2 ^ 16) = 16MB
(1 << 9) * (2 ^ 16) = 32MB
(1 << 10) * (2 ^ 16) = 64MB

Calculating this value is not so easy. IconLib at first used a shift factor of 9 because I thought that 32 Mb was more than enough for an Icon library. But, I was surprised when I extracted Windows Vista DLLs and IconLib got out of range for some files. I incremented the shift factor to 10; then, I could dump the content of the Windows Vista DLL in an ICL file, but it took 63 Mb.

A factor of ten allows you to create an ICL library up to 64 Mb but every resource will address at minimum 1024 bytes. If you think that's not bad because all resources will be bigger than 1024, it is not so easy. A factor of ten means that it can address in multiples of 1024; if the resource is 1025, it will allocate 2048 bytes in the file system.

In conclusion, with a factor of 10, IconLib is wasting an average of (1024 / 2) 512 bytes by resource allocated, but at the same time it lets you create an Icon Library with 64 Mb. My next release will predict the max file size and adjust the shift factor dynamically; it is not an easy task if you want to predict the number without scanning the memory to know the max space to be addressed, especially for PNG images where this value is dynamic too. Hopefully, the shift alignment field is clear now and you can come back to the resource table.

The next field is an array of TYPEINFO.

TYPEINFO is a struct that gives you information about the resource. There are many types of resource that can be allocated, but IconLib just in interested in two types, RT_GROUP_ICON and RT_ICON.

When IconLib reads the TYPEINFO array, it discards all structs where rtTypeID is not RT_GROUP_ICON or RT_ICON.

The RT_GROUP_ICON type gives you information about a icon.

RT_ICON type gives you information about a single image inside the icon.

rtResourceCount is the number of resources of this type in the executable.

rtNameInfo is an array of TNAMEINFO containing the information about every resource of this type. The length of this array is equal to rtResourceCount.

Here is where you have the information about the resource itself; the rnOffset is the virtual address where the physical resource is located. To know the real address, see how alignment shift works above.

The rnLength is the length of the resource on a virtual address space. This means if, for example, the resource has a length of 1500 bytes and the alignment shift is 10, the value on this field will be 2. The way to calculate the length is:

rnLenght = Ceiling(realresourcesize /
   (1 << resource_table.rscAlignShift));

rnFlags tell us if the resource is fixed, preloaded, or shareable
rnID is the ID of the resource.
rnHandle is reserved.
rnUsage is reserved.

Going back to TYPEINFO, if the TYPEINFO struct type is RT_GROUP_ICON, you read the array of TNAMEINFO that gives you information about every icon in the resource.

The offset in every TNAMEINFO will contain a pointer to a GRPICONDIR struct. This struct will tell information about a single icon, like how many images it contains and one array of GRPICONDIRENTRY, when every in GRPICONDIRENTRY contains information about the image, like width, height, colorcount, and so forth. Now if the TYPEINFO struct type is RT_ICON, you read the array of TNAMEINFO that gives you the information about every single image inside the resource.

Going back to the Resource Table, you have another three fields: rscEndTypes, rscResourcesNames, and rscEndNames.

rscEndTypes is a ushort value that tells you when to stop reading for TYPEINFO structs. The resource table struct doesn't tell how many TYPEINFO structs it contains, so the only way to know is with a stopper flag. This flag is rscEndTypes; if, when you read TYPEINFO the two first bytes are zero, this means you have reached the end of the TYPEINFO array.

rscResourceNames is an array of bytes with the names of every resource in TYPEINFO struct. The names (if any) associated with the resources are in this table. Each name is stored as consecutive bytes; the first byte specifies the number of characters in the name. For example if the array is [5, 73, 67, 79, 78, 48, 5, 73, 67, 79, 78, 49], this is translated like an array of two string 'Icon1', 'Icon2'.

[5, 73, 67, 79, 78, 48, 5, 73, 67, 79, 78, 49]
[73, 67, 79, 78, 48] = 'Icon1'
[73, 67, 79, 78, 49] = 'Icon2'

If you wonder when you have to stop reading for bytes in the array, for this another stopper flag rscEndNames with a value of zero exists. When the bytes have been read, if a null ('\x0') character is detected, the process must stop reading the names, and they are ready to be translated as ANSI strings.

At this point, you already have all the information and binary data for the Icons and the images inside the Icon.

IconLib: Icons Unfolded (MultiIcon and Windows Vista supported)

IconLib loads all the Icons and Icon images in memory to obtain good performance working with them. Also, it doesn't need to lock the file on the file system.

Creating a ICL file is no so complex after all because IconLib creates an ICL from scratch; it doesn't care about the other segments in the NE Format, so the process is relative simple. You write an IMAGE_DOS_HEADER, write the MSDOS stub program, and then write an IMAGE_OS2_HEADER, where you choose the right alignment factor and you write the resource table at the location specified by the field ne_rsrctab in the os2_header.

When the resource table is written, it has to apply the same rules as loading. This means that it writes a partial resource table struct and the two TYPEINFO structs (RT_GROUP_ICON and RT_ICON and, inside the TYPEINFO, writes the TNAMEINFO information).

The following table shows a NE format that stores two Icons. The first icon contains one image; the second icon contains two images.

[NESample.png]

There's something I would like to mention. As I said before, I redesigned the core three times. The first time, I followed every known specification about how the Icon file has to be read and written from Icons and DLLs. When I exported icons from DLLs, I kept all information about the icon, such as the Icon names, group ID and icon ID. When I saved them on the file system, I saved in the same way I read it, so basically I could export the icons from a DLL, export them to a ICL file, and then load the ICL and export to a DLL, and I would keep the same IDs for the groups and Icons.

So far, I tested two popular commercial products and they could open them without problems; but, for example, I started to have problems when I exported some DLLs or EXEs to ICL files. If you open explorer.exe from a Windows folder in a Visual Studio, the first thing you will notice is that the icons IDs are not consecutive; they start with ID 100, 101, 102, 103, 104 and jump to 107, and continue.

IconLib exported explorer.exe to an ICL file, and imported it to Visual Studio; there was no problem at all, but my surprise was when I tried to open it with a popular Icon Editor. The icon library shows image icons mismatched and mixed between the icons. I spent many days trying to figure why it was happening.

Basically, after many tests of different applications, I noticed that those applications write the ICL files and discard the Icons and Group IDs. They expect consecutive IDs.

For ICL files, there is a header to be written: TNAMEINFO. This header contains a field that is the ID; this ID can be a GRP ICONDIRENTRY (Icon itself) or a ICONDIRENTRY ID (single image ID inside the icon). When those applications write ICL files, they do it in a consecutive way; they discard the IDs when they import from the DLL and write the group's id as 1, 2, 3, 4; the same for the icons' id, they do 1,2,3,4....

I noticed that some applications are not prepared to handle ICL files properly for all cases; another less popular application passed it; it could read ICL files where the IDs were not consecutive but when it saved the ICL file, it discarded the source ID and put in its own.

I had a big dilemma. How could I keep all the information and write that information as it is coming from the EXE/DLLs in the ICL files? That would make my ICL files properly constructed but incompatible with some applications. Or, I could discard the original IDs in the importation and create consecutive IDs; that means discarding part of the original information and putting in my own (I was not happy about this solution), but small fishes can swim in a pool with big fishes unless they behave like one.

I didn't have another choice than re-design my core to produce those results using consecutive IDs. After I redesigned, I reduced the source code because now I didn't need to keep all the information that was generated on the fly, but when icons are exported from the DLLs, the original Icons IDs are lost. However, a regular developer will rarely use those IDs.

I still wonder whether it is a mis-implementation on those products to fully support ICLs, or is there a rule in the NE Format files that tell you can't store a resource with a 'random' ID? So far, all my researching concludes that you can use any ID for the ICONIMAGES inside NE Format.

PE Format (DLL, EXE, OCX, CPL, SRC)

PE Format means Portable Executable; this format was created by Microsoft to support 32- and 64-bit versions of Windows as a NE Format replacement used for the 16-bit version of Windows.

File formats such as EXE, DLL, OCX, CPL, and SCR don't differ too much between them. For example, think about an EXE like a DLL with an entry point. When working with resources, all those files are identical; this means that if the library supports PE Format, it supports all the above extensions. Because Win32 API already supports resources handling for PE format, it was not necessary to support this file format natively. Instead, IconLib make use of Win32 APIs to access to the icons resources. The only native functionality was to read the first set of headers from the PE file to detect whether the file to be loaded is a PE format or not.

If you want to access just to the resources, the best way to do it is to load the library as a DATAFILE. This means no code at all will be executed from the library; instead, the Win32 API will access just the resources data.

hLib = Win32.LoadLibraryEx(fileName, IntPtr.Zero,
   LoadLibraryFlags.LOAD_LIBRARY_AS_DATAFILE);

The IconLib core just supports reading and writing from and to a stream; MultiIcon overloads some functions such as Load/Save and create a FileStream from a file in the file system before call the Load(stream). The Win32 API LoadLibrary only can load libraries from the file system; therefore, the stream will be saved in a temporary file before Win32 API LoadLibraryEX is called. Access to the resources it is an easy task when the resources are accessed in the proper order.

The first thing IconLib does is call Win32.EnumResourceNames, sending as a parameter GROUP_ICONS; this will give you back the ID of every icon. This ID can be number or a pointer to an string. If the value returned is less than 65535, it is a number; if the value if bigger than 65535, it is a pointer to one string.

Once you have all the IDs for the icons, you call the function Win32.FindResource for every ID found. This gives you a handle to the resource; then, you can proceed to load and lock the resource to access to the resources entries. Those entries contain the IDs of every image inside the icon just loaded/locked. Now, you repeat the steps that you did before, but instead of using the constant GROUP_ICONS, you use RT_ICON; this tells the Win32 API that you want to access the image inside the icons.

Here is the critical step to do: Under Windows XP or previous OS, after you lock the resource for the icon image, you have a pointer to a ICONIMAGE. This icon image will contain the BITMAPINFOHEADER, Palette, XOR Image, and the AND Image (mask), but in Windows Vista, it returns a pointer to a PNG image. This is the main reason why current Icon Editors, even the more popular ones, will crash, allocate a huge amount of memory, or just they will drop the image because they will be parsing a PNG image such as a BMP image.

IconLib resolves that issue by reading the first bytes of the Image and detecting the signature of the image creating the proper encoder instance before reading and parsing the image. When IconLib has to create a DLL, the best way so far was use an empty DLL as template, and add the resources to it.

Win32 API offers three APIs that will make the job easier:

  • BeginUpdateResources
  • UpdateResources
  • EndUpdateResources.

MSDN says that you can call BeginUpdateResources, and then call UpdateResources as many times as you want; the file won't be written yet. At last, you call EndUpdateResources and the changes are committed to the DLL.

That methodology worked pretty well for small DLL files. When IconLib was creating libraries with more than 80 images, everything was okay but the call to EndUpdateResources always failed. After a lot of unsuccessful tries, the only thing I could think was that the API to update resources has an internal buffer. When that buffer is filled, calls to EndUpdateResoruces fail to commit the changes into the DLL.

The workaround that I found was to commit every 70 updates on average; this worked pretty well but enormously increased the time to update the DLL. For that reason, unless I can find out why Win32 is doing that, I'll try to come up with my own PE Format implementation and not use the Win32 at all. That will speed up the process a lot.

You can get more information for PE format from Microsoft web site: http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx.

Windows Vista Icons Support

I wanted to create a library to work with icons and have no limitation, so support for Windows Vista was a must.

Windows XP introduced icons with an alpha channel and 48x48 pixels; Windows Vista MS introduced icon images with a size of 256x256 pixels. This image inside the icon can take 256 Kb for the image and another 8 Kb for the mask in uncompressed format. That increased the size of icons library substantially; they resolve this issue storing the image in a compressed format. The compression used was PNG (Portable Network Graphic) because it is free of patents, it supports transparency (Alpha Channel), and it employs lossless data compression. The factor is an average between 3 to 5 times smaller than uncompressed bitmaps.

If you think there is not much difference, load the imageres.dll file from Windows\System32 in Windows Vista (11 Mb), do a for loop for all images, and set the encoder to be BMP instead of PNG, and then save it to a DLL or a ICL file. You will notice that the DLL is about 45 Mb and the ICL about 54 Mb. There is where you can see that PNG really makes the difference.

To store the compressed image, they could come up with a way to keep certain backward compatibility; this was setting the biCompression field in BITMAPINFOHEADER to BI_PNG instead BI_RGB. This header is already supported from Windows 3.1 and the filed BI_PNG from Windows 95; instead, they broke compatibility and they stored the image alone (see 'Ohh, the Microsoft Policy about Compatibility Is Changing?' below).

[Windows_Vista_Icons.png]

The sample only contains two images, but there can be as many as 65535.

Although, in all my researching, I saw only 256x256 images in PNG format, that doesn't mean it could not store all images as PNG. This was only a decision to keep compatibility with previous version of Windows.

Personally, I think Icon editors should support PNG at any size and bits depth. Icons not only are used by the Windows OS; it is the same reason Windows icons allow introducing non-standard images, such as 128x96x24, when Windows will never make use of them. If you are creating an icon that Windows Vista will use, only store PNG compression for 256x256 images.

Smart Classes/Structs

The more difficult stuff was how to come up with a clean code and APIs capable of understanding different icon formats and icon libraries and also different image compressions without create a chaos of switch/if/else. In my journey of creating the library, I re-designed the core from scratch three times, and still there is a TODO list of changes to avoid IconImages objects to know about different compression methods. An IconImage should not be responsible to know the format of the image to be read/write; instead, it should depend on the different encoders to know this information.

Right now, IconImage has a reference to an ImageEncoder object (base class), but IconImage object still is responsible to discover the signature of the image to know whether it has to create a BMPEncoder or a PNGEncoder instance. For now, the support for different file formats are in Static classes. Every class support three methods (IsRecognizedFormat(), Load(), and Save()). At first, I came up with an interface but later I had to remove it because I needed to implement 'IsRecognizedFormat()' as a static method but C# doesn't support static methods on interfaces. Later, I'll find a clear solution for integrating the different formatters (IconFormat, NEFormat, PEFormat) under a common base class. Also, there are a couple of changes to manage the memory allocations more efficiently, but that won't change the core design.

Coming back to what I called Smart Class/Structs, basically an Icon is a hierarchical structure and Icon libraries are the same but contain one more level of information.

The objective of this smart classes/structs was to avoid interchange data between the different objects; instead, every class/struct should be capable of reading and writing itself. If a class of struct contains more classes or structs inside, it should ask the child to read/write that portion of information and so on.

If you open the source code, immediately you will notice that the parameter (Stream stream) is everywhere. This allows the object that receive this parameter to read/write itself in the stream at the current position. For example, when a Icon file has to be created, the MultiIcon object will open a File Stream and call ImageFormat.Save(stream), sending the stream just opened as a parameter.

ImageFormat object will contain only the logic to write itself and rely on the different classes/structs to write the rest of the information.

ImageFormat.Save(stream)
{
   ICONDIR.write(stream)
   {
      Write iconDir header
   }
   Loop for each IconImage
   {
      ImageEntry.Write(stream)
      {
         Write iconEntry header
      }
      Image.Write(stream)
      {
         BitmapInfoHeader.Write(stream)
         {
            Write bitmap info header
         }
         Write Color Palette
         Write XOR image
         Write AND image
      }
   }
}

This is a simple case, but more complex cases such as reading ICL (Icon Libraries) follow the same behavior. So, following this model to write and read different formats was really easy and also it produced cleaner code.

Image Encoder

I wanted to provide a library that was easy to understand and flexible enough to adapt any kind of image format. The ideal case was to create a class with basic functionality but leaving the specific format implementation to other classes. ImageEncoder object keeps all the information about one icon image and it contains information like image properties, palette, icon image, and icon mask. This class is an abstract class that cannot be instantiated.

BMP Encoder

BMPEncoder class has the logic to read and write Icon entries when biCompression is BI_RGB (BMP).

PNG Encoder

PNGEncoder class has the logic to read and write Icon entries when image is PNG format.

I followed the information I could get from different sources to create Icons with PNG compression. So far, the implementation doesn't have problems and Icons Images in PNG format can be opened with all Icons editors that support Windows Vista.

Icon libraries are a different subject. So far, I didn't find a single open source or commercial icon editor, even in the more popular ones that allow opening or writing icon libraries like ICL or DLL with PNG compression. In some commercial products, you will notice that the PNG icons are not loaded; also, if you create PNG icons, they are uncompressed before they're saved on a DLL or ICL. I think that is because Microsoft still didn't release any information about it and companies are waiting for the final Windows Vista to come out.

I based my work for creating compressed icon libraries (ICL, DLL) on reverse engineering in Windows Vista RC2 and following the same logic that Microsoft boys did for icons files. IconLib is capable of loading all icons from Windows Vista DLLs/EXEs and CPL files (PNG format inclusive); also, it allows writing ICL/DLL icon libraries with PNG compressions.

The bad news is that you can load them only with IconLib for now; if you try to load an ICL or DLL with PNG images generated with IconLib and try to open it with a third-party icon editor, you will see that the PNG icons are gone. Also, the icons contain images from other icons. So far, all my researching concludes to a mis-implementation of the PNG format for ICL libraries in those products and has nothing to do with IconLib. Now, perhaps you wonder how I can be sure that IconLib generates ICL or DLL properly?

Of you try to open Windows Vista icons that contains256x256 PNG icons in any VisualSudio version (Orcas inclusive if you wonder about VS2006 so far), it will show an image with a size about 2573x1293 with XP format. Of course, that image doesn't exist and you can't edit it, but that's the way how Visual Studio sees it. Now, if you load a DLL with 256x256 PNG files, generated with IconLib and save the icon that contains the PNG image to the file system and open the icon image with Visual Studio, you will notice the same behavior as the DLLs from Windows Vista.

Anyway, the entire work is based on suppositions, and I can't be really sure yet as long no Windows Vista Libraries Icons Editors hit the market or MS released more information about it.

Ohh, the Microsoft Policy about Compatibility Is Changing?

I have to comment on this because I think it is a breakthrough of how usually Microsoft does things from my point of view.

I've developed on the Windows platform for the last decade, from Windows 3.1 to today, and something that I saw in Microsoft APIs is the amazing compatibility between versions. Personally, I think many Win32 APIs are so intrinsic and complicated because they had to keep backwards compatibility. I had so many headaches in the last years because of it.

For example the huge show stopper for Windows future generation was the GDI that imposed a set of rules that could not be broken in any way. GDI+ helped, but still ran under the GDI rules, and that is the reason why there are things that Windows could never do until now. This happened when I decided to implement Windows Vista icons support. I read that Windows Vista icons are 256x256 and they use PNG compression.

At first I was 100% convinced they were going to keep backwards compatibility, so I started to think how they did it. The first thing that came to mind was that the Microsoft boys were going to use the field biCompression in the header BITMAPINFOHEADER and instead set to BI_RGB (BMP). They were going to use BI_PNG (PNG) that is already supported in the header, the palette was going to be empty, and the XOR and AND Image will contain the PNG data.

Ohh, imagine my surprise was that didn't happen. Instead, they completely dropped the concept of having a BITMAPINFOHEADER, the image (XOR) and the mask (AND). Instead, the icon directory points to a 100% PNG structure.

At first, I thought, "What have they done!!!" This was going to break all Icons Editors out there. Also, Visual Studio and Resource Editors won't be able to open ICO files anymore, but when I sat and thought about it, it came to my mind that it was the right step to take.

Developers have always complained about how complicated some Win32 APIs are; and this time MS heard about that and did things right. If they could keep compatibility, it would mean that ICO and ICON libraries nowcould have three places with redundant information about each image. Like ICONDIRENTRY, BITMAPINFOHEADER, and PNGHEADER usually found in those bizarre things in Win32 API.

Instead, they now have the Icon directory entry that point to the image itself. In this way, they open the way for future implementation or different images or compressions. Still, ICO files are limited by a maximum of 256x256 pixels because the Icon directory stores width and height in two-byte type fields bWidth and bHeight. Probably, that can be resolved by using more than one Plane. But, we still are far from using ICONS with more than 256x256 pixels.

So, this time I congratulate the boys at Microsoft for thinking 'what is the best way to do it' over anything else.

If you wonder whether this means VS2005 or any VS won't be able to open properly ICO or DLLs from Windows Vista, you are right; it WON'T. Also, I tested ORCAS (VS2006) and it doesn't support it. But, that can be easily resolved with a VS patch that hopefully will come up soon; otherwise, you will have products like this library that will support Windows Vista Icons.

Roadmap

  • IconLib is a powerful library to allow icons or icon libraries creation and modifications. I plan to support updates for DLLs and EXE in the next version, allowing you to replace/add/delete icons inside them.
  • IconLib alone is only useful from a programming language, so also I plan to create an advanced Icon Editor application to make full use of IconLib. Probably, that will be my next article in the next few months.
  • If I can get file formats like .icc (Icons collection), Icns, RSC, bin (mac), I'll support them. If you know some file format and you have the internal file structure, let me know and I'll try it to implement it.
  • If someone is interested in creating an open-source Icon Extractor & Editor, he is welcome to use IconLib as the file formats engine and I can provide support for IconLib.

History

  • IconLib 0.71 (Initial Release)

License

[CastorLic.png]

This work is licensed under a Creative Commons Attribution-ShareAlike 2.5 License.

References



About the Author

Gustavo Franco

started with programming about 19 years ago as a teenager, from my old Commodore moving to PC/Server environment Windows/UNIX SQLServer/Oracle doing gwBasic, QBasic, Turbo Pascal, Assembler, Turbo C, BC, Clipper, Fox, SQL, C/C++, Pro*C, VB3/5/6, Java, and today loving C#. Currently working on VOIP/SIP technology. Passion for most programming languages and my son Aidan.

Downloads

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • With JRebel, developers get to see their code changes immediately, fine-tune their code with incremental changes, debug, explore and deploy their code with ease (both locally and remotely), and ultimately spend more time coding instead of waiting for the dreaded application redeploy to finish. Every time a developer tests a code change it takes minutes to build and deploy the application. JRebel keeps the app server running at all times, so testing is instantaneous and interactive.

  • Live Event Date: May 6, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT While you likely have very good reasons for remaining on WinXP after end of support -- an estimated 20-30% of worldwide devices still are -- the bottom line is your security risk is now significant. In the absence of security patches, attackers will certainly turn their attention to this new opportunity. Join Lumension Vice President Paul Zimski in this one-hour webcast to discuss risk and, more importantly, 5 pragmatic risk mitigation techniques …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds