Introduction
Sometimes you can see some applications with very interesting icons and maybe you would like to use
the same or similar icons in your applications. In this article I will show how you can easily do this.
This article is not a tutorial about how to develop shell extensions and particularly context
menu handlers. For understanding all the needed details I recommend the excellent series of
articles by Michael Dunn published here at CodeProject: "The Complete Idiot's Guide to Writing Shell
Extensions" and particularly Part I.
My fully functional project IconExtract.zip is developed using ATL and COM and is structured similarly to the
project described in the above mentioned article so that all the explanations given there are applicable. This
shell extension will add two menu items to the context menu when you right click on an .exe or .dll file
in Windows Explorer, one menu item is for extracting the large icon and the other one is for extracting the
small icon. By clicking on these menu items you copy the corresponding icon information (large or small) in
the clipboard in the BITMAP
format. Later you can paste this information in a graphical editor, for example in
the one used by the Visual C++ environment and you can modify it as you want and use similar icons in your own
applications.
I will present just the specific details about how to extract icon information from files and how to copy
this information in the clipboard in order to be later saved.
How to extract icon information from files
All the specific actions are implemented in the QueryContextMenu()
and InvokeCommand()
functions of
the IContextMenu
interface. For getting the handles to the icons I used the function:
UINT ExtractIconEx(LPCTSTR lpszFile, int nIconIndex, HICON FAR *phiconLarge, HICON FAR *phiconSmall, UINT nIcons)
which can extract icons from executable files, dynamic-link libraries (DLL), or icon files.
The function ExtractIcon()
could also be used, but it extracts only the large icons.
First we need to see if there are any icons in the file. For this purpose, in the function QueryContextMenu()
I called
ExtractIconEx()
with the nIconIndex
parameter set to -1 and the phiconLarge
and
phiconSmall
parameters set both to NULL:
int nIcons = (int)ExtractIconEx(m_szFile, -1, NULL, NULL, 0);
If the returned number is greater than 0 then I insert in the contxet menu the menu items for large and small icon extraction.
In this way the shell extension is applicable to all the files, but the Menu Items will be inserted only for the files
which contain icons. Later in the InvokeCommand()
function I get the handles to both icons with the call:
HICON hIconLarge, hIconSmall;
ExtractIconEx(m_szFile, 0, &hIconLarge, &hIconSmall, 1);
The icon information can be extracted in an ICONINFO
structure using the function GetIconInfo()
as in the call:
ICONINFO oIconInfo;
GetIconInfo(hIconLarge, &oIconInfo);
The ICONINFO
structure has the field hbmColor
of type HBITMAP
which is a handle to a BITMAP
structure.
Using this handle we can copy the bitmap information in the clipboard:
BOOL bOpen = ::OpenClipboard(NULL);
if(TRUE==bOpen)
{
::EmptyClipboard();
::SetClipboardData(CF_BITMAP, oIconInfo.hbmColor);
::CloseClipboard();
}
Finally after we finish using the icon handles we must destroy the icons by calling the DestroyIcon()
function:
DestroyIcon(hIconLarge);
DestroyIcon(hIconSmall);
Here I present the full code for QueryContextMenu()
and InvokeCommand()
functions:
HRESULT CIconExtractObj::QueryContextMenu(HMENU hmenu, UINT uMenuIndex, UINT uCmdID, UINT uidLastCmd, UINT uFlags)
{
if(uFlags & CMF_DEFAULTONLY)
return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 0);
int nMenus = 0;
int nIcons = (int)ExtractIconEx(m_szFile, -1, NULL, NULL, 0);
if(nIcons > 0)
{
InsertMenu(hmenu, uMenuIndex, MF_STRING | MF_BYPOSITION, uCmdID++, _T("Extract &Large Icon"));
if(NULL != m_hExtractBmpL)
SetMenuItemBitmaps(hmenu, uMenuIndex, MF_BYPOSITION, m_hExtractBmpL, NULL);
uMenuIndex++;
InsertMenu(hmenu, uMenuIndex, MF_STRING | MF_BYPOSITION, uCmdID++, _T("Extract &Small Icon"));
if(NULL != m_hExtractBmpS)
SetMenuItemBitmaps(hmenu, uMenuIndex, MF_BYPOSITION, m_hExtractBmpS, NULL);
uMenuIndex++;
nMenus = 2;
}
return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, nMenus);
}
HRESULT CIconExtractObj::InvokeCommand(LPCMINVOKECOMMANDINFO pCmdInfo)
{
if(0 != HIWORD( pCmdInfo->lpVerb))
return E_INVALIDARG;
HICON hIconLarge, hIconSmall;
ExtractIconEx(m_szFile, 0, &hIconLarge, &hIconSmall, 1);
ICONINFO oIconInfo;
switch(LOWORD(pCmdInfo->lpVerb))
{
case 0:
GetIconInfo(hIconLarge, &oIconInfo);
break;
case 1:
GetIconInfo(hIconSmall, &oIconInfo);
break;
default:
return E_INVALIDARG;
}
BOOL bOpen = ::OpenClipboard(NULL);
if(TRUE==bOpen)
{
::EmptyClipboard();
::SetClipboardData(CF_BITMAP, oIconInfo.hbmColor);
::CloseClipboard();
}
DestroyIcon(hIconLarge);
DestroyIcon(hIconSmall);
return S_OK;
}
Thanks to Michael Dunn for the improvement suggestions I implemented in this update to the article.
I hope that you will find the presented information useful!