65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.80/5 (26投票s)

2003年1月27日

3分钟阅读

viewsIcon

176273

downloadIcon

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 模拟,但他确实包含了一个更完整的图标索引列表。

© . All rights reserved.