windows系统中编译出exe的显示图标不能及时刷新问题

不知道大家与没遇到这样一个问题。无论是使用C#还是MFC编译生成exe文件,当改变该exe的.ico图标图片后,会发生这个exe的显示图标不刷新的问题。这个问题在windows系统中由来已久,从win xp系统到win 10系统一直存在。这个问题也困扰我许久。

近期在MFC项目中,花费1天时间仔细研究了MFC程序,以及windows系统对.exe文件的图标显示机理,跟大家分享一下。下面就以MFC程序为例,深入探讨。

问题一:

使用SetIcon()设置MFC程序主窗口的exe图标后。任务栏显示图标跟随变化,Alt+Tab切换进程时图标变化,标题栏图标跟随变化。但是资源浏览窗口中的预览图标,没有跟随一起变化,该exe文件的"属性->常规"中的显示图标已经发生变化,该exe的快捷方式预览图标也没有跟随变化。

cpp 复制代码
//MFC程序本质上也是调用下面的Win API,发送消息WM_SETICON 来设置exe软件的窗口图标
CWnd* wnd = new Cwnd();
wnd->SetIcon(IDR_MAINFRAME, TRUE);     //设置大图标
wnd->SetIcon(IDR_MAINFRAME, FALSE);    //设置小图标


//Windows API
HICON hIcon = (HICON)::LoadImage(::GetModuleHandle(NULL), MAKEINTRESOURCE(nRes), IMAGE_ICON, ::GetSystemMetrics(SM_CXICON), ::GetSystemMetrics(SM_CYICON), LR_DEFAULTCOLOR | LR_SHARED); 

ASSERT(hIcon); 

::SendMessage(m_hWnd, WM_SETICON, (WPARAM) TRUE, (LPARAM) hIcon);
hIcon = (HICON)::LoadImage(::GetModuleHandle(NULL), MAKEINTRESOURCE(nRes), IMAGE_ICON, ::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR | LR_SHARED); 

ASSERT(hIcon); 

::SendMessage(m_hWnd, WM_SETICON, (WPARAM) FALSE, (LPARAM) hIcon);"

原因及解决:

之所以windows不同版本的系统,无论是C#和MFC程序都会遇到同一种现象,原因是windows系统图标缓冲刷新机制导致的。windows系统中,任务栏,Alt+Tab切换进程和标题栏图标读取的是当前窗口在内存中的图标资源,所以通过SetIcon()后,显示会立即生效。但是资源浏览窗口中的预览图标,是explorer.exe进程进行刷新和管理的。出于性能的考虑,explorer.exe进程只有首次创建文件,修改文件名称,更新文件所在路径时,才会主动刷新预览图标。explorer.exe刷新预览图标的来源是thumbcache*.db文件和iconcache*.db文件,TrayNotify目录。thumbcache*.db中主要存储的是"图片、视频、PDF、Office文档",iconcache*.db文件通常就是存储预览exe软件图标,具体会有不同大小的图标数据库,如"iconcache_16.db、iconcache_32.db、

iconcache_48.db、iconcache_256.db、iconcache_idx.db等"TrayNotify目录主要存储的是"系统托盘、通知区域、隐藏图标列表"。知道这些了,就可以通过删除上述2个.db文件,继而主动让explorer.exe强制刷新exe文件在资源浏览器中的预览图标。主动刷新方法,可以将下面的shell的脚本指令,保存成*.bat文件,双击执行,自动删除图标缓存数据库。

bash 复制代码
rem 关闭Windows  explorer
taskkill /f /im explorer.exe
rem 清理系统图标缓存数据库
attrib -h -s -r "%userprofile%\AppData\Local\IconCache.db"
del /f "%userprofile%\AppData\Local\IconCache.db"
attrib /s /d -h -s -r "%userprofile%\AppData\Local\Microsoft\Windows\Explorer\*"
del /f "%userprofile%\AppData\Local\Microsoft\Windows\Explorer\thumbcache*"
del /f "%userprofile%\AppData\Local\Microsoft\Windows\Explorer\iconcache*"
rem 清理 系统托盘记忆的图标
echo y|reg delete "HKEY_CLASSES_ROOT\Local Settings\Software\Microsoft\Windows\CurrentVersion\TrayNotify" /v IconStreams
echo y|reg delete "HKEY_CLASSES_ROOT\Local Settings\Software\Microsoft\Windows\CurrentVersion\TrayNotify" /v PastIconsStream
rem 重启Windows外壳程序explorer
start explorer

问题二:

在MFC程序中,当在资源管理器中,导入两个ICO文件,把资源分别命名为"IDR_MAINFRAME"和"IDI_ICON_LEAROL"。SetIcon设置为"IDI_ICON_LEAROL"后,任务栏,Alt+Tab,标题栏的图标会立即生效,快捷方式,资源浏览器的图标,exe文件右键属性的图标不会刷新。只有把要使用的图标的名称命名成"IDR_MAINFRAME",编译生成exe后,任务栏,Alt+Tab,标题栏的图标更新的同时,exe文件的右键属性图标才会刷新,但此时快捷方式和资源浏览器里的图标未刷新。再执行前文的*.bat的Shell指令,清除缓存后,可以把快捷方式和资源浏览器里的图标更换。

原因及解决:

explorer.exe 在提取 EXE 图标时优先读取:exe的主图标,而MFC程序编译时,会自动将命名为"IDR_MAINFRAME"的ICO图标,放在exe的图标组"Icon Group"第1个,做为exe的主图标。这点可以通过软件"Resource Hacker",读取exe中的资源组来证明。"Resource Hacker"的下载路径为Resource Hacker。以下两个图片的测试可以验证

如下图所示,当SetIcon(IDR_MAINFRAME)时,图标IDR_MAINFRAME"被编译到了Icon Group的第一个

如下图所示,当SetIcon(IDI_ICON_LEAROL)时,仍然是图标IDR_MAINFRAME"被编译到了Icon Group的第一个