更改文件夹和驱动器的默认图标






4.80/5 (26投票s)
2003年1月27日
3分钟阅读

176273

8195
展示如何全局更改文件夹和驱动器的图标。 同时也展示了您可能在应用程序中发现的一些巧妙技巧。
引言
本文解释了一种简单的方法来获取和设置文件夹(打开和关闭)、硬盘驱动器、软盘和 CD 驱动器的图标。 默认图标作为图标资源存储在 Shell32.dll 中。 要更改图标,我们需要通过添加一些值来修改注册表,并将它们设置回默认图标,我们只需删除我们添加的值即可。 当然,简单地更改注册表值不会更改图标,因为 Windows 将从图标缓存中获取图标。 Tweak UI 具有一个清除图标缓存的功能,我在其上运行了 Spy++,并监视了注册表更改。 我发现他们所做的是更改 WindowMetrics 注册表项中的图标大小值,使用 SPI_SETNONCLIENTMETRICS
广播一个 WM_SETTINGCHANGE
,将图标大小改回原来的值,然后再次广播 WM_SETTINGCHANGE
消息。 我做了同样的事情,发现它工作正常。
演示项目
我整理了一个简单的演示项目,以便让您更清楚地了解情况。 它将让您获取和设置文件夹和驱动器图标,并让您将其设置回它们的默认值。 我在程序中使用了一些有趣的技术,我将在下面为感兴趣的人解释。
获取默认(或自定义)图标
默认图标存储在 Shell32.dll 中,我们只需从 DLL 中读取硬编码的索引图标即可。 但在此之前,我们首先检查图标是否已通过读取注册表进行自定义。
void CShellIconChangerDlg::OnCbnSelchangeCombo1() { //First chk if customized CString val; HKEY hKey; LONG result = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Icons", 0,KEY_READ,&hKey); BYTE buff[512]; ZeroMemory(buff,511); DWORD sz = sizeof buff; DWORD typ = REG_SZ; CString indbuff; indbuff.Format("%d",m_combo.GetItemData(m_combo.GetCurSel())); result = RegQueryValueEx(hKey,indbuff,0,&typ,buff,&sz); RegCloseKey(hKey); if(result == ERROR_SUCCESS) { val = buff; int comma = val.ReverseFind(','); int cusindex = 0; if(comma != -1) { CString tmpstr = val.Mid(comma+1); cusindex = atoi(tmpstr); val = val.Left(comma); } HICON hIcon = ::ExtractIcon(AfxGetApp()->m_hInstance, val,cusindex); m_curicon.SetIcon(hIcon); } else { char sysfolder[MAX_PATH]; GetSystemDirectory(sysfolder,MAX_PATH); CString strIconPath = sysfolder; strIconPath += "\\Shell32.dll"; HICON hIcon = ::ExtractIcon(AfxGetApp()->m_hInstance, strIconPath,m_combo.GetItemData(m_combo.GetCurSel())); m_curicon.SetIcon(hIcon); } }
ExtractIcon
是一个鲜为人知的函数,它将从 DLL 或 EXE 文件中提取索引图标资源。 当然,我可能是错的,因为它鲜为人知,我在这里是个人看法。 我直到今天才知道有这样的功能。
设置自定义图标
void CShellIconChangerDlg::OnBnClickedOk() { HKEY hKey; DWORD dwDisposition; LONG result = ::RegCreateKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Icons",0, NULL,REG_OPTION_NON_VOLATILE,KEY_WRITE,NULL,&hKey,&dwDisposition); CString buff; buff.Format("%d",m_combo.GetItemData(m_combo.GetCurSel())); RegSetValueEx(hKey,buff,0,REG_SZ, (const BYTE*)m_strIconPath.GetBuffer(0),m_strIconPath.GetLength()); m_strIconPath.ReleaseBuffer(); RegCloseKey(hKey); RefreshIcons(); OnCbnSelchangeCombo1(); }
嗯,这里没有什么特别的,我们只需将一个新值添加到注册表项中,如上所示。 我使用 RegCreateKeyEx
而不是 RegOpenKeyEx
,因为默认情况下子键 Shell Icons 不存在。 正如您所见,然后我调用 RefreshIcons
,该函数复制了 Tweak UI 所做的事情。 我将在本文后面解释它。
设置为默认值
void CShellIconChangerDlg::OnBnClickedButton2() { HKEY hKey; DWORD dwDisposition; LONG result = ::RegCreateKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Icons",0, NULL,REG_OPTION_NON_VOLATILE,KEY_WRITE,NULL,&hKey,&dwDisposition); CString buff; buff.Format("%d",m_combo.GetItemData(m_combo.GetCurSel())); RegDeleteValue(hKey,buff); RegCloseKey(hKey); RefreshIcons(); OnCbnSelchangeCombo1(); }
设置为默认值只是删除我们添加到注册表中的值并调用 RefreshIcons
。
浏览图标对话框
void CShellIconChangerDlg::OnBnClickedButton1() { WCHAR szIconPath[MAX_PATH]={0}; int index = 0; if(PickIconDlg(m_hWnd,(WCHAR*)&szIconPath,MAX_PATH,&index)) { CString str; str.Format("%S,%d",szIconPath,index); m_strIconPath = str; HICON hIcon = ::ExtractIcon(AfxGetApp()->m_hInstance, CString(szIconPath),index); m_iconpreview.SetIcon(hIcon); } }
我使用了 PickIconDlg
shell 函数来向用户显示标准的 shell 浏览器对话框。 它将允许您浏览图标文件以及包含图标的 DLL 和其他文件。 它仅在 Shell32.dll 版本 5.0 及更高版本中可用,这意味着它不适用于 Windows 2000 之前的用户。
RefreshIcons 类似于 Tweak UI
void CShellIconChangerDlg::RefreshIcons() { CString val; HKEY hKey; LONG result = ::RegOpenKeyEx(HKEY_CURRENT_USER, "Control Panel\\Desktop\\WindowMetrics", 0,KEY_READ,&hKey); BYTE buff[256]; ZeroMemory(buff,255); DWORD sz = sizeof buff; DWORD typ = REG_SZ; RegQueryValueEx(hKey,"Shell Icon Size",0,&typ,buff,&sz); RegCloseKey(hKey); val = buff; int i = atoi(val); i++; val.Format("%d",i); result = ::RegOpenKeyEx(HKEY_CURRENT_USER, "Control Panel\\Desktop\\WindowMetrics", 0,KEY_WRITE,&hKey); RegSetValueEx(hKey,"Shell Icon Size",0,REG_SZ, (const BYTE*)val.GetBuffer(0),val.GetLength()); val.ReleaseBuffer(); RegCloseKey(hKey); ::SendMessage(HWND_BROADCAST , WM_SETTINGCHANGE,SPI_SETNONCLIENTMETRICS,NULL); i = atoi(val); i--; val.Format("%d",i); result = ::RegOpenKeyEx(HKEY_CURRENT_USER, "Control Panel\\Desktop\\WindowMetrics", 0,KEY_WRITE,&hKey); RegSetValueEx(hKey,"Shell Icon Size",0,REG_SZ, (const BYTE*)val.GetBuffer(0),val.GetLength()); val.ReleaseBuffer(); RegCloseKey(hKey); ::SendMessage(HWND_BROADCAST , WM_SETTINGCHANGE,SPI_SETNONCLIENTMETRICS,NULL); }
基本上,此函数的全部目的是清除图标缓存并强制 Windows 重新绘制图标。 通过更改注册表中 shell 图标的大小值,然后使用 SPI_SETNONCLIENTMETRICS
广播 WM_SETTINGCHANGE
,我们设法清除了图标缓存。 当然,然后我们需要将 shell 图标大小设置回正常大小。 我知道这听起来有点绕,但在他们将“ShFlushIconCache”添加到 shell API 之前,这是我们唯一的解决方案。
索引号
图标 |
目录 |
关闭的文件夹 | 3 |
打开的文件夹 | 4 |
硬盘 | 8 |
软盘 | 6 |
CD-ROM 驱动器 | 11 |
结论
请注意,当您有 CD-RW 驱动器时,CD-ROM 索引无效。 我还没有弄清楚 CD-RW 驱动器的索引。 或者也许在我的情况下它不起作用,因为我的驱动器是 DVD/CD-RW 组合驱动器。 无论如何,像往常一样,请通过本文底部的非常酷的论坛发送反馈。 Thomas Freudenberg 撰写了一篇关于同一主题的文章here; 尽管在他的案例中,虽然他没有演示 Tweak UI 模拟,但他确实包含了一个更完整的图标索引列表。