在开发应用程序时,经常需要使用图标来增强用户界面的友好性。有时,可能希望在不同的应用程序中使用相同的或相似的图标。本文将展示如何轻松实现这一功能。
本文不是关于如何开发Shell扩展和上下文菜单处理程序的教程。为了理解所有需要的细节,推荐阅读Michael Dunn在CodeProject上发表的系列文章:“The Complete Idiot's Guide to Writing Shell Extensions”,特别是第一部分。
开发的一个完整功能的项目名为IconExtract.zip,使用ATL和COM开发,结构类似于上述文章中描述的项目,因此那里给出的所有解释都是适用的。这个Shell扩展将在Windows资源管理器中右键单击.exe或.dll文件时,在上下文菜单中添加两个菜单项,一个用于提取大图标,另一个用于提取小图标。通过点击这些菜单项,可以将相应的图标信息(大或小)复制到剪贴板中。稍后,可以将这些信息粘贴到图形编辑器中,例如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结构有一个名为hbmColor的字段,类型为HBITMAP,这是一个指向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 (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;
}