在开发项目时,为了保持用户界面的一致性,经常需要使用到系统图标。以Windows资源管理器中的文件夹图标为例,可以通过特定的方法提取并使用这些图标。本文将介绍如何实现这一过程,以及如何处理自定义图标的情况。
Windows系统中的图标通常存储在动态链接库文件中,如shell32.dll。对于文件夹图标,其在库中的位置是固定的:位置3代表闭合的文件夹,位置4代表打开的文件夹。可以使用ExtractIconEx函数来提取这些图标。
如果用户对文件夹图标进行了自定义,那么需要在注册表中进行相应的设置,以确保程序能够正确地加载这些自定义图标。具体来说,需要在以下路径下添加一个值:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Icons
该值的名称是想改变的shell图标的索引,值数据包含图标文件名和它的索引,两者之间用逗号分隔。例如,以下注册表值将打开文件夹的图标更改为C:\OpenFolder.ico中的图标(对于.ico文件,需要将索引设置为0):
"4"="C:\OpenFolder.ico,0"
为了解决自定义图标的问题,实现了以下方法:
HICON ExtractShellIcon (
int nIndex,
bool bLargeIcons = false
) {
HICON hIcon = NULL;
// Shell图标可以通过注册表进行自定义:
// "" = ","
// 例如:"3" = "c:\MyFolderIcon.ico,1"
HKEY hkeyShellIcons;
if (RegOpenKeyEx (
HKEY_LOCAL_MACHINE,
_T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Icons"),
0,
KEY_READ,
&hkeyShellIcons) == ERROR_SUCCESS)
{
TCHAR szBuffer[MAX_PATH * sizeof(TCHAR)];
DWORD dwSize = MAX_PATH * sizeof(TCHAR);
TCHAR szIndex[6] = {0};
_stprintf(szIndex, _T("%d"), nIndex);
if (RegQueryValueEx (hkeyShellIcons, szIndex, NULL, NULL, (LPBYTE)szBuffer, &dwSize) == ERROR_SUCCESS)
{
#ifdef _AFXDLL
CString strFileName, strIndex;
VERIFY(AfxExtractSubString(strFileName, szBuffer, 0, _T(',')));
VERIFY(AfxExtractSubString(strIndex, szBuffer, 1, _T(',')));
ExtractIconEx(strFileName, atoi(strIndex), bLargeIcons ? &hIcon : NULL, bLargeIcons ? NULL : &hIcon, 1);
#else
std::vector ls;
tokenize(std::back_inserter(ls), szBuffer, _T(','));
ExtractIconEx(ls[0].c_str(), atoi(ls[1].c_str()), bLargeIcons ? &hIcon : NULL, bLargeIcons ? NULL : &hIcon, 1);
#endif
}
RegCloseKey(hkeyShellIcons);
}
// 如果没有自定义,则从shell32.dll获取原始图标
if (!hIcon)
ExtractIconEx(_T("SHELL32.DLL"), nIndex, bLargeIcons ? &hIcon : NULL, bLargeIcons ? NULL : &hIcon, 1);
return hIcon;
}
在非MFC版本中调用的tokenize函数是一个简单的分词器,包含在源文件中。
要使用方法,只需调用ExtractShellIcon。第一个参数指定想要检索的图标。在下面编译了一个包含在shell32.dll中的图标列表。第二个参数表示想要小图标还是大图标。
以下是shell32.dll中包含的一些图标及其索引: