Changing default icons for folders and drives
Shows how to globally change the icons for folders and drives. Also shows some nifty tricks you might find handy in your applications.

Introduction
This article explains a simple way to get and set icons for folders (open and
closed), hard drives, floppies and CD drives. The default icons are stored in
Shell32.dll as icon resources. To change the icons, we need to modify the
registry by adding some values and to set them back to default icons, we simply
remove the values we added. Of course simply changing the registry values won't
change the icons because Windows will take the icons from the icon cache. Tweak
UI has a feature to flush the icon cache and I ran Spy++ on it as well as
monitored registry changes. I found that what they do is to change the icon size
value in the WindowMetrics registry key, broadcast a
WM_SETTINGCHANGE with SPI_SETNONCLIENTMETRICS, change
the icon size back to it's original value and again broadcast the
WM_SETTINGCHANGE message. I did the same and found that it
works fine.
Demo project
I put together a simple demo project for making things clearer for you. It
will let you get and set folder and drive icons as well as let you set it back
to their default values. I've used some interesting techniques in the program
which I'll explain below for the interested ones.
Getting default (or customized) icons
The default icons are stored in Shell32.dll and we simply read the
hard coded indexed icons out of the DLL. But before that we first check to see
if the icons have already been customized by reading the registry.
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 is a rather unknown function which will extract an
indexed icon resource from a DLL or EXE file. Of course I might be wrong as to
it's being unknown, I was speaking personally here. I didn't know of such a
function till today.
Setting custom icons
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();
}
Well, there is not anything very much special here and we simply add a new
value to the registry key as shown above. I use RegCreateKeyEx
instead of RegOpenKeyEx because by default the sub-key Shell Icons
does not exist. As you can see I then call RefreshIcons which is
the function that duplicates what Tweak UI does. I'll explain it later in this
article.
Setting to default
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();
}
Setting to default is a simple matter of deleting the values we added to the
registry and calling RefreshIcons.
Browse for icons dialog
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);
}
}
I have used the PickIconDlg shell function to show the user the
standard shell browser dialog. It will let you browse icon files as well as DLLs
and other files containing icons. It's only available in Shell32.dll
version 5.0 and above, which means this is not available for pre-Windows 2000
users.
RefreshIcons similaire à 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);
}
Basically the whole purpose of this function is to flush the icon cache and
force Windows to redraw the icons. By changing the shell icon size value in the
registry and then broadcasting WM_SETTINGCHANGE with
SPI_SETNONCLIENTMETRICS, we manage to flush the icon cache. Of
course we then need to set the shell icon size back to normal. I know it
sounds like a rather round about way to do it, but till they add a
"ShFlushIconCache" to the shell API, this is the only solution we have.
The index numbers
Icon |
Index |
| Closed folder |
3 |
| Open Folder |
4 |
| Hard Disk |
8 |
| Floppy Disk |
6 |
| CDROM Drive |
11 |
Conclusion
Please note that the CDROM index is not valid when you have a CD-RW drive. I
haven't figured out the index for CD-RW drives. Or perhaps it did not work in my
case because mine is a DVD/CD-RW combo drive. Anyway as usual please send in the
feedback through the really cool forum found at the bottom of this article.
Thomas Freudenberg has written an article on the same topic here; though in his
case, while he does not demonstrate the Tweak UI simulation, he does include a
more complete list of icon indices.