http://www.kennyndkarin.com/kenny/codecorner/tools/iconbrowser/
Icon Browser: An EXERCISE IN Resource Management
By Kenny Kerr, November 2001
Summary info
Prerequisites
This Article Assumes You're Familiar with C , C #, and .NET Programming.
Article Summary
The System.Runtime.InteropServices namespace contains many useful classes, structures and attributes that you can use to access your legacy libraries and resources. The .NET Framework as a whole also provides many services for migrating existing code and resources to managed code. This article explains how to use these services to extract resources from Win32 resource files as well as how to write icon resources to .ICO files. It also covers many of the issues surrounding resource management and interoperating with unmanaged code, memory and binary formats.
Resources
.NET Framework system.Runtime.InteropServices namespacehttp: //msdn.microsoft.com/library/en-us/cpref/html/frlrfsystemRuntimeInterOpServices.asp
Platform SDK Documentation for Resource FunctionShttp: //msdn.microsoft.com/library/en-us/winui/resource_0jsj.asp
Icons in win32http: //msdn.microsoft.com/library/en-us/dnwui/html/msdn_icons.asp
Downloads
Kerr.iconBrowser.exe - The Executable for the icon broopeser application. You need The .NET Framework SDK Release Candidate Installed To Run IT.
Source Code - The Full Source Code for the Application. Ideally You Would Open this in Visual Studio. Net Release Candidate, But You Can Also Build This Using Just The SDK (with a bit more effort).
Introduction
Every so often I find myself needing to extract an icon from an executable or dynamic-link library (DLL). And every time I end up bending over backwards to get this done, especially if the library contains hundreds of icons and I have to sift through all of them until I find the one I want. So I decided to write a little program to make the process of extracting an icon from a library much easier. Well it started out simple. I wrote the icon Browser application in C # using the .NET Framework and in about 45 minutes I had a really cool icon viewer. Then I attempted to implement the Export feature that would allow the user to choose any icon in the library and save it to an external .ICO file. I hit a brick wall. It turns out there's no simple way to save an icon to a file.In this article I present the icon Browser application. It is a small desktop application that allows you to browse through the icons embedded in executables and DLLs. It also allows You to save any of the icons to a. ICO file. I will use this application as a vehicle for exploring programming tasks such as reading and writing unmanaged resources and other issues related to porting or coexisting with legacy code and binaries. In addition, I'll look at taking C classes employing traditional deterministic Finalization and show you how to import the....................
Icon Browser
Before we get into the programming details let me quickly introduce the Icon Browser and show you how you can use it. Even if you're not too interested in resource management and interoperating with unmanaged code from .NET, you might find this application useful. Icon Browser is very easy to use (see Figure 1). you can type a file name in the edit box and click the Go button and Icon Browser will search the given file and display any icons it finds in the list view. If you ' re not sure about the name or location of the file you can click the Browse button and search for a file using the familiar 'Choose a File' dialog box. Once the icons have been loaded into Icon Browser you can choose any of the icons and Click The Export Icon Button to Save The icon to a .ico file. You will be presented with a 'save icon as' Dialog Box So That You can indeicate where to save the new .ico file. I've Also Added a Handy Context Menu to the list view soed you can Just Right Click on an icon and self The Export Icon Command.figure 1 - The icon Browser Application
If you are following along with me on your own computer you will notice that there is a default file name in the Save Icon As dialog box and it's probably a number. You might be wondering what this number is. The name or number is what is known as the resource name for the icon in the library. It's just a unique way to identify a resource in a library. The resource name is also used as the title for each of the icons that are displayed in icon Browser (see Figure 1) .
And That's The icon Browser Application, Simple But Effective! The Application AS Well AS ITS Source Code Is Available With The Download for this article.
What Are Icons Anyway?
Most people think of icons as the little images that represent documents and other files in the Windows shell. Technically, an icon is a group of icon images. An icon image resembles that of a device-independent bitmap. So an icon can be represented by a number of different images tailored for different display configurations. in fact, when designing icons for Windows XP you are encouraged to include 9 different images. Figure 2 shows the images for Microsoft Notepad in GIF Movie Gear, which is a tool you can use to Assemble Icon Images and save to a .ico file.figure 2 - Notepad icon images
When using an icon you will typically only deal with a handle to the icon and not the actual icon resource. In C or C you will use the HICON, which is simply an abstract handle to the icon and pass it to the various Win32 API calls ................
Loading Icons, So Many Choices!
There are many different ways to load an icon. Two of the most common functions you can use are ExtractIcon and LoadIcon. ExtractIcon will return an HICON given a file name and the zero-based index of the icon in the file. This is useful if you just want to enumerate all or some of the icons in a file. LoadIcon is a bit more useful in that you can identify an icon based on its resource name or identifier. in .NET you do not generally load icons from unmanaged executables or Dlls. INSTEAD You Will Normal Use The ResourceManager Class to Load Resources from Resource Files As Defined by The .NET Framework.
SO HOW DO I READ ICONS ANMANAGED LIBRARIES? Read on.
Platform Invoke
Platform Invoke, which is more commonly referred to as P / Invoke, is the bridge between the managed world of .NET and unmanaged code like the Win32 APIs. P / Invoke is part of the .NET Framework's extensive interoperability layer. To call an unmanaged Function You NEED TO Use THE DLLIMPORT Attribute To Identify The Function and The DLL That IMPLEMENTS It. Here Is The Declaration for The LoadCon API. / ORIGINAL DECLARED IN: Winuser.h
// HiCI Loadicon (Hinstance Hinstance,
// pctstr pname;
[
DllImport
(
"User32.dll"
,
SetLasterror
=
True
,
Charset
=
Charset
.
Auto
)]]
Public static extern
INTPTR LOADICON
(
INTPTR HINSTANCE
,
INTPTR PNAME
);
"User32.dll" is the single parameter to the DllImportAttribute instance constructor. It identifies the DLL implementing the unmanaged function. SetLastError and CharSet are public fields for the DllImportAttribute class.
Setting the SetLastError field to true informs the Common Language Runtime (CLR) that the unmanaged function may call the SetLastError Win32 API function. In response the CLR will cache the value returned by the GetLastError Win32 API function so that you can retrieve it later. This is to avoid race conditions, as it is possible for another function used by the CLR to call SetLastError before you get around to retrieving the error code. The .NET Framework exposes the cached error code through the GetLastWin32Error static method on the Marshal class. So IT is a good idea to set this field if you sust your unmanaged functions mother set the last error code and you are interested in Retrieving IT.
Setting the CharSet field to CharSet.Auto indicates that the unmanaged function has a Unicode and an Ansi version. Most Win32 API functions that deal in any way with strings qualify. For example, there does not actually exist a function named LoadIcon. It is actually # define'd to LoadIconW if UNICODE is defined or LoadIconA if it is not. The -W, or Unicode, versions of the functions were introduced with Windows NT as Windows NT and it successors Windows 2000 and XP deal natively with Unicode strings. Windows 9x and its successor Windows ME mostly only support the -A, or Ansi, versions. Setting CharSet to CharSet.Auto instructs the CLR to pick the right version based on the operating system that the code is running on. If you do not explicitly set the CharSet field it defaults to CharSet.Ansi, which will not give you the best performance on Window XP as the operating system will have to convert any strings from Ansi to Unicode before it can use them.The next thing you'll Notice is what I've changed the return type and the parameter types to the IntPtr class. You will normally use IntPtr for opaque unmanaged types that can not be represented by corresponding managed types. Although pName is a PCTSTR, which is a constant pointer to a TCHAR (in other Words a string, it is more commitentifier using the makeintresource marcro. for this reason pname is also declared as an INTPTR.
LoadIcon, as with many of the resource management functions, requires an instance handle. If you're programming in C then this is no problem. The operating system will assign your process an instance handle when it first starts up. And if you need to get resources from another DLL or executable it's a simple matter of calling LoadLibraryEx and passing the LOAD_LIBRARY_AS_DATAFILE flag to indicate that you just want access to the resources within the library and do not want to execute any code it may contain. Here's an example of using The loadLibraryEx Function to Load The Windows Explorer Shell Executable.hmodule HModule
=
LoadLibraryex
(
_T
(
"C: // windows // Explorer
.
EXE "
),
0
,
// must be zero.
LOAD_LIBRARY_AS_DATAFILE
);
Although LoadLibraryEx returns a module handle, it is generally interchangeable with an instance handle so you can freely pass it to functions that expect a HINSTANCE. In fact, a HMODULE is typedef'd to be a HINSTANCE in windef.h. In the world of .NET however, you do not deal with instance handles but rather with assemblies. An assembly, which is usually packaged as an executable or DLL, can be loaded using the Assembly.LoadFrom method as shown here.
AskEMBLY ASSEMBLY
=
AskSEMBLY
.
LoadFrom
(
"C: // kerr
.
Resources
.
DLL "
);
........................
Resource Management
Lets get back to the problem at hand. We want to retrieve resources from unmanaged libraries. To do this we need to declare a few of the Win32 API functions for use in .NET. I tend to put all my declarations in a single class that I call WindowsAPI. This way I can check at a glance what dependencies my project may have to ensure that they are available on any platforms that I support. You can download the WindowsAPI class along with the source code for this article. Now because resource management is so important (yes, even when using .NET), you should avoid using these functions directly as your code will end up getting quite messy as you try to free the various resources when you're done using them. Remember that you must always write your code to be resilient to exceptions. The best way to do this is to wrap any resource management functions inside a class. If you are a C programmer your first pass at writing a C # class to wrap the library functions might look something like this , With Error Handling Code Omitted for Brevity (See Figure 3) .figure 3 - Unreliable Library Class
Public class
Library
{
public
Library
(
String
Strfilename
,
Ulong
Ulflags
)
{
m_hmodule
=
Windowsapi
.
LoadLibraryex
(
Strfilename
,
INTPTR
.
ZERO
,
Ulflags
);
}
~
Library
()
{
Windowsapi
.
Freelibrary
(
m_hmodule
);
}
Private
INTPTR M_HMODULE
;
}
This is perfectly acceptable from a C point of view and it will even compile and work in C #. There's only one problem. The .NET Framework uses non-deterministic finalization. What this means is that the .NET Framework's garbage collector will call your object's destructor (called a finalizer in .NET speak) in a non-deterministic fashion, most likely when it notices you're low on memory. So unlike in C where your destructor is guaranteed to be called when execution leaves the scope in which the object was created, in .NET you can not rely on a destructor to free your resources in such a timely fashion. fortunately .NET defines a pattern for resource management, which if followed consistently makes resource management a breeze. The .NET Framework defines the IDisposable interface , Which Has A Single Method Called Dispose. When Used In Conjunction with the C # Using Statement, IT Provides Precise Control Over When Your Class Will Be disposed The library class to make us E of the idisposable interface, again with error handling omitted for break (see Figure 4) .figure 4 - Revised Library Class
Public class
Library: idisposable
{
public
Library
(
String
Strfilename
,
Ulong
Ulflags
)
{
m_hmodule
=
Windowsapi
.
LoadLibraryex
(
Strfilename
,
INTPTR
.
ZERO
,
Ulflags
);
}
~
Library
()
{
Free
();
}
Public void
Dispose
()
{
Free
();
}
Public void
Free
()
{
IF
(
INTPTR
.
ZERO
! =
m_hmodule
)
{
Windowsapi
.
Freelibrary
(
m_hmodule
);
}
m_hmodule
=
INTPTR
.
ZERO
;
}
Private
INTPTR M_HMODULE
;
}
You'll notice that I still have a destructor. This is just a safeguard in case the user of my class forgets to employ the using statement. In this case the library will still get freed, eventually. My new Library class can now be used As Follows.USING
(
Library Library
=
New
Library
(
@ "C: / Windows / Explorer
.
EXE "
Windowsapi
.
LOAD_LIBRARY_AS_DATAFILE
))
{
// use the library
}
Before Execution Leaves The Using Scope The Library Will Be Freed. And That's The Basics of Resource Management In C #. Next We'll Take A Look at extracting the icon resources from the library.
SO Where are the icons?
Now that we know how to load an unmanaged library we need to determine whether a library contains any icons. As I mentioned before, you can use the LoadIcon function to load an icon from a library as long as you have the name or identifier for the Icon Resource. if you don't know you can enjoy the one you want. this is done Using the enumResourceNames Function, Which is Declared As Follows.
Bool EnumResourcenames
(
HModule HModule
,
PCTSTR PTYPE
,
EnumresNameProc PCallback
,
Long_ptr lparam
);
The hModule parameter is a module handle, which you can get by calling LoadLibraryEx, so I can use my Library class for this. The pType parameter identifies the type of resource. Although you can define your own resource types, you will generally use one of the standard resource types. Two resource types that are of interest in this discussion are RT_GROUP_ICON and RT_ICON. An RT_GROUP_ICON resource represents an icon. It is basically just a directory of icon images. Each directory entry refers to a RT_ICON resource, which contains the actual Image Data (See Figure 5) .figure 5 - RT_GROUP_ICON AND RT_ICON RESOURCE MEMORY LAYOUT
So to enumerate the icons in a library you pass RT_GROUP_ICON as the type of resource to enumerate. The next parameter is the callback function, which is the traditional way of implementing an enumerator in C. You simply pass the address of a function to EnumResourceNames and it will call that function for each matching resource. Simple. Well not really. The problem is that the .NET Framework has no notion of a function pointer. fortunately for us the .NET Framework has a thing called a delegate, which you can use to implement a callback. A delegate can refer to a static or instance method. you can not pass EnumResourceNames a pointer to a function, but you certainly can pass it a delegate, assuming you declared the EnumResourceNames function appropriately and declared the matching delegate. Here's how you work do what.
[
DllImport
(
"Kernel32.dll"
,
SetLasterror
=
True
,
Charset
=
Charset
.
Auto
)]]
Public Static Extern Int
ENUMRESOURCENAMES
(
INTPTR HMODULE
,
INTPTR PTYPE
,
EnumResnameProc Callback
INTPTR PARAM
);
Public Delegate Bool
ENUMRESNAMEPROC
(
INTPTR HMODULE
,
INTPTR PTYPE
,
INTPTR PNAME
,
INTPTR PARAM
);
Since EnumResourceNames really operates on a library, I've added a method to my Library class for it. This saves us from having to pass it a module handle every time. And of course this is the more object-oriented way to go about it Now to enumerate the icon resources in a library becomes Very Easy (See Figure 6).
Figure 6 - Using The EnumResourceNameProc Delegate
Public void
Main
()
{
Using
(
Library Library
=
New
Library
(
@ "C: / Windows / Explorer
.
EXE "
Windowsapi
.
LOAD_LIBRARY_AS_DATAFILE
))
{
// Call The Callback Method for Each RT_GROUP_ICON Resource That
// is found.
Library
.
ENUMRESOURCENAMES
(
Windowsapi
.
RT_GROUP_ICON
,
New
Windowsapi
.
ENUMRESNAMEPROC
(
Callback
),
INTPTR
.
ZERO
);
}
}
Public Bool
Callback
(
INTPTR HMODULE
,
INTPTR PTYPE
,
INTPTR PNAME
,
INTPTR PARAM
)
{
// use the icon resource
Return True
;
// to Keep Enumerating
}
At this point we can call LoadIcon to get a handle to an icon tailored for the current display configuration. This can then be passed to the Icon.FromHandle method to get an Icon object, which in turn can be displayed in various ways in a. Net Application. If You Recall One of my Objectives for the icon browser application is to be aable to save an icon to one. To do this we need to dig a little deeper and get at the actual resource data.
To get the resource data we need to use the FindResource and LoadResource functions to locate and load the resource. This is different from calling LoadIcon, which generates a displayable icon based on the icon resource. Once the resource is loaded we call the LockResource function to . get a pointer to the actual resource bytes This pointer is valid until such time as the library containing the resource is freed I've declared two C # structures to represent the resource data (see Figure 7) .Figure 7 -. C # Strucures for RT_GROUP_ICON
[
StructLayout
(
Layoutkind
.
Sequential
,
Pack
=
2
)]]
Public struct
MEMICONDIR
{
Public Ushort
WRESERVED
;
Public Ushort
WTYPE
;
Public Ushort
WCOUNT
;
public
MEMICONDIRENTRY ARENTRIES
;
// inline array
}
[
StructLayout
(
Layoutkind
.
Sequential
,
Pack
=
2
)]]
Public struct
MEMICONDIRENTRY
{
Public Byte
BWIDTH
;
Public Byte
BHEIGHT
;
Public Byte
Bcolorcount
;
Public Byte
BRESERVED
;
Public Ushort
Wplanes
;
Public Ushort
Wbitcount
;
Public uint
DWBYTESINRES
;
Public Ushort
WID
;
}
You will not find these data structures defined in the Platform SDK documentation. What complicates matters when trying to use them from C # is that MEMICONDIR is actually a variable length structure. In C you would declare it as follows.
Struct
MEMICONDIR
{
Word WRESERVED
;
Word WTYPE
;
Word wcount
;
MEMICONDIRENTRY ARENTRIES
[
1
];
}
The length of the arEntries array is defined by wCount. In other words, arEntries is an inline array as apposed to a pointer to the first element of an array. So to get to the various elements of the array you need to perform a bit of pointer arithmetic. Although this is no problem to a seasoned C or C developer, it poses a challenge as C # does not encourage the use of pointers. fortunately it does not disallow it altogether. You do however need to explicitly mark code blocks as unsafe before you can make use of pointers. This can be done using the C # unsafe keyword. Please bear in mind that it is called unsafe for a reason. Misuse of pointers often leads to very buggy code. This is why even in C the use of pointers is discouraged and should usually be localized to C classes employing the 'resource acquisition is initialization' idiom. If you use any unsafe code in a .NET assembly the entire assembly is marked as unsafe. Figure 8 contains a simple console application that demonst Rates how to load the icon resource for the microsoft notepad application and how to us
Using
SYSTEM
;
Using
Kerr
.
Resources
;
Class
Console
{
Unsafe static void
Main
()
{
Try
{
// for BREVITY We Assume Notepad is located here. To run this
// Code Yourself Adjust this value Accordingly.
String
Strfilename
=
@ "D: /Windows/notePad.exe"
;
// load the library.
Using
(
Library Library
=
New
Library
(
Strfilename
,
Windowsapi
.
LOAD_LIBRARY_AS_DATAFILE
))
{
// Notepad's icon resource Identifier: 2
INTPTR HGROUPINFO
=
Library
.
FindResource
(
New
INTPTR
(
2
),
Windowsapi
.
RT_GROUP_ICON
);
// load the icon resource.
INTPTR HGROUPRES
=
Library
.
LoadResource
(
HgroupInfo
);
// Get a Pointer to the resource data.
Windowsapi
.
MEMICONDIR
*
PDIRECTORY
=
(
Windowsapi
.
MEMICONDIR
*)
Library
.
LockResource
(
hgroupres
);
IF
(
0
! =
PDIRECTORY
->
WCOUNT
)
{
// Pentry Points to the first entry in the array.
Windowsapi
.
MEMICONDIRENTRY
*
PENTRY
= &
PDIRECTORY
->
Arentries
);
// adjust the pentry to point to the last entry.
PENTRY
=
PDIRECTORY
->
WCOUNT
-
1
;
// Use the directory entry.
}
}
}
Catch
(
Exception E
)
{
Console
.
WriteLine
(
Console Application Failed: "
e
.
Message
);
}
}
}
Now that we know how to access these structures lets take a closer look at them. Both MEMICONDIR and MEMICONDIRENTRY have the StructLayout attribute applied to them. LayoutKind.Sequential is the single parameter to the StructLayoutAttribute instance constructor. It indicates that the members of the structure must be laid out sequentially in memory so as to be memory-compatible with unmanaged code. Otherwise, the CLR is free to move the struct members around in memory for efficiency. The Pack field indicates the alignment of the members in memory. The Pack field defaults to 8, which is convenient as this is the same as what Visual C 6 uses. and since most unmanaged structures you might encounter were probably declared and implemented using Visual C 6 it is a safe default. However the icon resource uses a Pack value Of 2 and this is why we need to declare it explicitly. This Results IN A Slightly Smaller Memory Footprint, As Most of The Structure Members Are 2 byte Words Or Smaller. To Put this in Perspective You Would Declare The Memicondir Structure In C Like this. # Pragma Pack
(
push
)
#pragma pack
(
2
)
Struct
MEMICONDIR
{
Word WRESERVED
;
Word WTYPE
;
Word wcount
;
MEMICONDIRENTRY ARENTRIES
[
1
];
}
#pragma pack
(
POP
)
The MEMICONDIRENTRY structure contains information about an individual icon image. The wID member is of particular interest to us as it contains the resource identifier for the RT_ICON resource within the library that contains the actual image bitmap information. You can use this identifier with the FindResource and LoadResource functions to locate and load the RT_ICON resource. and then you can use the LockResource function to get at the bitmap information. Finally we've found the images! The RT_ICON resource data is stored in memory in a very similar way to a device- independent bitmap (DIB). So much so that we can use the BITMAPINFO structure, which defines a DIB in memory to access the RT_ICON resource data. For more information about DIB's and the BITMAPINFO structure please consult the Platform SDK documentation. Technically the icon image Uses The Same Format As The CF_DIB Clipboard Format Where The Dib Bits Follow The BitMapInfo Header and Color Table. You will need to know this if you want to render the image yourself using the icon resource. For the purpose of this discussion we can ignore this fact except to remember that the BITMAPINFO is yet another variable length structure and we must treat it accordingly. One thing to keep in mind is that icons deviate slightly from the BITMAPINFOHEADER semantics in that the biHeight member is double the actual height. Figure 5 illustrates how the RT_GROUP_ICON and RT_ICON resources are laid out in memory.Next we'll look at how we can wrap all this resource management code in A few well-defined classes to reduce this complexity.
Resource Management Revisited
I have written two C # classes namely IconResource and IconImage respectively. The IconResource class takes care of loading an RT_GROUP_ICON resource from a library and populates an array of IconImage objects. Each IconImage object represents an RT_ICON resource, which is a single image for the icon resource . As a user of the IconResource class is entitled to keep a reference to an IconResource object long after the library that it originated from has been freed, we need to be careful about the memory that gets returned by calls to LockResource. Although we don ' t have to worry about releasing this memory, the operating system will ensure that it is freed once its parent library is unloaded from the process, we do need to concern ourselves with any pointers to this memory that we may hold on to. The IconImage class Needs to Hold On To ITS Resource Data So That It Can Be Used to Write It To a .ico File. I Will Talk More About this Later. To satisfy this Requirement The iconimage class mak es a local copy of the image bits. When the IconImage object is constructed it loads the RT_ICON resource, which is just the DIB bits for the image. It then copies these image bits into a managed byte array using the Copy method on the Marshal class . So now that the IconImage class contains its own copy of the resource data, the library where the resource was originally retrieved from can be freed without affecting the IconImage class.Lets See What You've Got!
Icons are not much fun if you can not see them. So lets take a look at the Icon Browser's MainWindow class and how it employs the classes that we've discussed so far to implement icon browsing. Although I am generally weary of computer generated code, the Visual Studio Forms Designer does a remarkable job of generating user interface code. It is a huge boon to productivity if handled correctly. The trick is to be conscious of the code it generates and to maintain your coding and naming standards. For example if you drag three text boxes onto a form it will name then textBox1, textBox2, and textBox3. Be sure to give them meaningful names in the context of your application as well as following any naming conventions you may be employing for your project. Further , for any non-trivial user interface component you need to emply standard design practices to ensure separation of data and presentation. This can be done using design patterns such as the Document / View pattern employed by MFC or the Model / View / Controller (MVC) Pattern.
As you can see in Figure 1, the Icon Browser is a simple forms-based application. Its made up of a resizable window that contains a list view for displaying the icons as well as a tool bar of sorts exposing the functionality supported by the Icon Browser application. The code of interest is in the OkEventHandler method, which is called in response to the Click event on the OK button. This brings everything we have learnt so far together. The OkEventHandler method uses the Library class to load the user-provided file. It then calls the Library class' EnumResourceNames method to enumerate the RT_GROUP_ICON resources. AddIconToList is the delegate called by EnumResourceNames for each icon resource that is found. It uses the IconResource class to load the resource data and adds an IconItem to the ListView to Repesent the icon to the user. IconItem Is A Simple Class That devices from listviewItem, That Used to initialize the listviewItem text and image index.exporting iCons
There's one more thing left to do. At the beginning of this article I mentioned that the Icon Browser would allow the user to export any icon to a .ICO file. Unfortunately for us the .ICO file format is different to the format used to store icons in resource files. Figure 9 illustrates how an icon is stored in a .ICO file. As you can see it holds somewhat of a resemblance with the memory layout of of the RT_GROUP_ICON resource. So with a little bit of translation code we can convert the one to the other. The major difference with the way the .ICO file format is written is that the icon image directory entry for each image stores an byte offset to the location of the image, whereas the RT_GROUP_ICON resource stores the resource name which you use to load the image. For the purposes of writing a .ICO file I have added a Save method to the IconResource class. This method takes a single string parameter that indicates the file to write the icon data to. It uses the BinaryWriter class to WR ITE The .ico File Header Information. It kilocates to the IconImage Class to Write The Directory Entry Information As Well As The Image Bits.figure 9 - Binary Layout of .ico File
Conclusion