图标提取器 Shell 扩展





5.00/5 (5投票s)
一个用于从 .exe 和 .dll 文件提取图标的上下文菜单处理程序 Shell 扩展
引言
有时您会看到一些应用程序具有非常有趣的图标,并且您可能希望在您的应用程序中使用相同或相似的图标。在本文中,我将展示如何轻松地做到这一点。本文不是关于如何开发 shell 扩展,特别是上下文菜单处理程序的教程。为了理解所有需要的细节,我推荐 Michael Dunn 在 CodeProject 上发表的精彩系列文章:“编写 Shell 扩展的完全白痴指南”,特别是 第一部分。我功能齐全的项目IconExtract.zip是使用 ATL 和 COM 开发的,并且结构与上述文章中描述的项目类似,因此那里给出的所有解释都适用。此 Shell 扩展将在您在 Windows 资源管理器中右键单击.exe或.dll文件时,向上下文菜单添加两个菜单项,一个菜单项用于提取大图标,另一个菜单项用于提取小图标。通过单击这些菜单项,您可以将相应的图标信息(大或小)以BITMAP
格式复制到剪贴板。稍后,您可以将此信息粘贴到图形编辑器中,例如 Visual C++ 环境中使用的那个,您可以根据需要对其进行修改并在您自己的应用程序中使用类似的图标。
我将只介绍关于如何从文件中提取图标信息以及如何将此信息复制到剪贴板以便稍后保存的具体细节。
如何从文件中提取图标信息
所有具体操作都在IContextMenu
接口的QueryContextMenu()
和InvokeCommand()
函数中实现。为了获取图标的句柄,我使用了函数
UINT ExtractIconEx(LPCTSTR lpszFile, int nIconIndex, HICON FAR *phiconLarge, HICON FAR *phiconSmall, UINT nIcons)
它可以从可执行文件、动态链接库 (DLL) 或图标文件中提取图标。函数ExtractIcon()
也可以使用,但它只提取大图标。首先我们需要看看文件中是否有任何图标。为此,在函数QueryContextMenu()
中,我调用了ExtractIconEx()
,并将nIconIndex
参数设置为 -1,并将phiconLarge
和phiconSmall
参数都设置为 NULL
int nIcons = (int)ExtractIconEx(m_szFile, -1, NULL, NULL, 0);
如果返回的数字大于 0,那么我在上下文菜单中插入用于提取大图标和小图标的菜单项。通过这种方式,Shell 扩展适用于所有文件,但菜单项只会为包含图标的文件插入。稍后,在InvokeCommand()
函数中,我通过调用以下代码获取两个图标的句柄
HICON hIconLarge, hIconSmall; ExtractIconEx(m_szFile, 0, &hIconLarge, &hIconSmall, 1);
可以使用函数GetIconInfo()
将图标信息提取到ICONINFO
结构中,如下面的调用所示
ICONINFO oIconInfo; GetIconInfo(hIconLarge, &oIconInfo);
ICONINFO
结构具有类型为HBITMAP
的字段hbmColor
,它是BITMAP
结构的句柄。使用此句柄,我们可以将位图信息复制到剪贴板
BOOL bOpen = ::OpenClipboard(NULL);
if(TRUE==bOpen)
{
::EmptyClipboard();
::SetClipboardData(CF_BITMAP, oIconInfo.hbmColor);
::CloseClipboard();
}
最后,在使用完图标句柄后,我们必须通过调用DestroyIcon()
函数来销毁图标
DestroyIcon(hIconLarge); DestroyIcon(hIconSmall);
在这里,我提供QueryContextMenu()
和InvokeCommand()
函数的完整代码
// IContextMenu HRESULT CIconExtractObj::QueryContextMenu(HMENU hmenu, UINT uMenuIndex, UINT uCmdID, UINT uidLastCmd, UINT uFlags) { //If the flags include CMF_DEFAULTONLY then we shouldn't do anything if(uFlags & CMF_DEFAULTONLY) return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 0); int nMenus = 0; //The number of icons in the file 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 lpVerb really points to a string, ignore this function call and bail out if(0 != HIWORD( pCmdInfo->lpVerb)) return E_INVALIDARG; //Extract first icon 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(); } //Destroy the icons DestroyIcon(hIconLarge); DestroyIcon(hIconSmall); return S_OK; }
感谢 Michael Dunn 提出的改进建议,我已在此文章的更新中实施。我希望您会发现所提供的信息有用!