I have an *.ico file that contains multiple icons in different sizes linked to my executable as a resource. I use this resource to set my application's icon with RegisterClassEx()
, i.e.:
wcx.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1));
In addition to that, I'd also like to convert all the single icons in this resource to ARGB
pixel arrays. This should be possible by using GetDIBits()
on the bitmap returned by GetIconInfo()
.
However, there is one problem: I need to find out the number of icons in the HICON
handle returned by LoadIcon()
as well as their sizes. I do not seem to find an API that takes a HICON
handle and tells me how many icons there are actually in there and what their sizes are.
Is this possible somehow or do I need to go the hard way and parse the *.ico resource myself?
The original icon handling functions are ancient. They were introduced in 16-bit Windows and designed for a system that defined only one icon size. Therefore, most of those functions are unaware of the possibility of more than one icon size. To get to the differently sized icons in a resource additional work is required.
Following the ICO file format, consisting of an icon directory and the actual icon images, an icon resource consists of two parts as well: The icon directory of type RT_GROUP_ICON
and the individual icons (RT_ICON
). The directory is represented by the following structures:
#pragma pack( push )
#pragma pack( 1 )
typedef struct
{
WORD idReserved; // Reserved (must be 0)
WORD idType; // Resource type (1 for icons)
WORD idCount; // How many images?
GRPICONDIRENTRY idEntries[1]; // The entries for each image
} GRPICONDIR, *LPGRPICONDIR;
#pragma pack( pop )
and
#pragma pack( push )
#pragma pack( 1 )
typedef struct
{
BYTE bWidth; // Width, in pixels, of the image
BYTE bHeight; // Height, in pixels, of the image
BYTE bColorCount; // Number of colors in image (0 if >=8bpp)
BYTE bReserved; // Reserved
WORD wPlanes; // Color Planes
WORD wBitCount; // Bits per pixel
DWORD dwBytesInRes; // how many bytes in this resource?
WORD nID; // the ID
} GRPICONDIRENTRY, *LPGRPICONDIRENTRY;
#pragma pack( pop )
The icon directory for an icon group ID can be retrieved using the following code:
typedef std::list<GRPICONDIRENTRY> IconDirectory;
IconDirectory GetIconDirectory( HMODULE hMod, WORD Id ) {
HRSRC hRsrc = FindResourceW( hMod, MAKEINTRESOURCE( Id ), RT_GROUP_ICON );
HGLOBAL hGlobal = LoadResource( hMod, hRsrc );
GRPICONDIR* lpGrpIconDir = (GRPICONDIR*)LockResource( hGlobal );
IconDirectory dir;
for ( size_t i = 0; i < lpGrpIconDir->idCount; ++i ) {
dir.push_back( lpGrpIconDir->idEntries[ i ] );
}
return dir;
}
With the information from the icon directory the individual icons can be constructed with this code:
HICON LoadSpecificIcon( HMODULE hMod, WORD Id ) {
HRSRC hRsrc = FindResourceW( hMod, MAKEINTRESOURCE( Id ), RT_ICON );
HGLOBAL hGlobal = LoadResource( hMod, hRsrc );
BYTE* lpData = (BYTE*)LockResource( hGlobal );
DWORD dwSize = SizeofResource( hMod, hRsrc );
HICON hIcon = CreateIconFromResourceEx( lpData, dwSize, TRUE, 0x00030000,
0, 0, LR_DEFAULTCOLOR );
return hIcon;
}
Putting all the pieces together, the following loads explorer.exe
as a resource file, retrieves the first icon group with ID 101
and prints the information from the icon directory for each entry. It then creates the individual icons and outputs the xHotspot
and yHotspot
data. For icons, the hotspot is located in the center:
void PrintIconDirEntry( const GRPICONDIRENTRY& DirEntry ) {
_wprintf_p( L"ID: %04d; width=%02d; height=%02d; bpp=%02d\n",
DirEntry.nID,
DirEntry.bWidth, DirEntry.bHeight, DirEntry.wBitCount );
}
void PrintIconInfo( HICON hIcon ) {
ICONINFO ii = { 0 };
GetIconInfo( hIcon, &ii );
_wprintf_p( L"xHotspot=%02d; yHotspot=%02d\n", ii.xHotspot, ii.yHotspot );
}
typedef std::list<GRPICONDIRENTRY>::const_iterator IconDirectoryCIt;
int _tmain(int argc, _TCHAR* argv[])
{
HMODULE hMod = LoadLibraryExW( L"C:\\Windows\\system32\\explorer.exe",
NULL, LOAD_LIBRARY_AS_IMAGE_RESOURCE );
IconDirectory dir = GetIconDirectory( hMod, 101 );
for ( IconDirectoryCIt it = dir.begin(); it != dir.end(); ++it ) {
PrintIconDirEntry( *it );
HICON hIcon = LoadSpecificIcon( hMod, it->nID );
PrintIconInfo( hIcon );
DestroyIcon( hIcon );
}
return 0;
}
To sum this up: Retrieving all icon sizes and color variations for an icon resources involves two steps:
CreateIconFromResourceEx
.References: