前情提要众所周知,人类更倾向于看图而非文字
图片所包含的信息量更大,也更容易接受
(这就是大家不喜欢读书的原因ba)
言归正传,(这就是大家喜欢GUI的原因ba)
图像固然重要,但文字也不可或缺
于是乎,本着我都要的心态,现代图形界面中的 文件 & 文件夹 都被设计为:图标 + 文字描述 的形式
需求系统原生的文件夹图标是个黄色的“文件夹”(物理)
如果所有文件夹的图标长得都一样,那么也就失去了传递特异信息的能力
由于文字的解读要更加困难(后天能力),因此在众多千篇一律的文件夹中快速定位目标则成了一件烦人事
人云这时候,有的同学要说了:要什么GUI,我都是直接CLI的…
打住打住,下一位
同学B:对于Windows,可以直接在资源管理器中(需要焦点)直接输入想要文件(夹)名称(支持输入法)即可定位
这位同学说得好,不过有了更差异化的图标便更能锦上添花 (๑¯ω¯๑)
设置文件夹图标GUI属性-自定义-更改图标[1]
相信大家早就知道了,跳过跳过(skip)
API咳咳,身为Programmer,我们还是来讨论一下更深入♂♀的内容吧
そもそも,说到底,Windows设置文件夹图标的原理究竟是什么
本质上就是在这个文件夹内新建了一个desktop.ini(之前的文章也提到过:Windows 系统级个人文件夹 vs OneDrive简析)
这个文件的属性比较特殊:SH,aka.System + Hide
12[.ShellClassInfo]IconResource=xxx.ico,0
这里除了.ico文件,还可以是exe,dll等
我们可以通过以下代码来创建desktop.ini
1234567891011QString iniPath = folderPath + "/desktop.ini";QFile iniFile(iniPath);if (iniFile.open(QIODevice::WriteOnly | QIODevice::Text)) { QTextStream out(&iniFile); out << "[.ShellClassInfo]" << '\n'; out << "IconResource=" << iconPath << ",0" << '\n'; iniFile.close(); // Set desktop.ini file attributes:隐藏和系统文件 SetFileAttributesW(iniPath.toStdWString().c_str(), FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM);}
当你满怀欣喜地盯着文件夹时,—— 文件夹也在盯着你
Nothing happened.
事情并没有这么简单,也许还缺了什么
请看Windows官方文档:如何使用 Desktop.ini 自定义文件夹
使用以下步骤通过 Desktop.ini 自定义文件夹的样式:
使用 PathMakeSystemFolder 使文件夹成为系统文件夹。 这将设置文件夹上的只读位,以指示应启用为 Desktop.ini 保留的特殊行为。 也可以使用 attrib +s FolderName 命令行将文件夹设为系统文件夹。
为文件夹创建一个 Desktop.ini 文件。 应将其标记为隐藏和系统,以确保对普通用户隐藏。
确保创建的 Desktop.ini 文件为 Unicode 格式。 这是存储可显示给用户的本地化字符串所必需的。
再看FolderIco的教程:如果自定义文件夹图标不显示怎么办? — What If the Custom Folder Icon Does Not Show?
To keep folder icon changed must be met following conditions:
Folder must have “Read Only” or “System” attribute, only these attributes allows to show customized folder icon.
Folder must contain “desktop.ini” file (This file contain path to the customized icon).
综上,我们还缺少一个条件:
文件夹必须拥有 只读(R)或 系统(S)属性
Continue我们可以在cmd中通过attrib +R Folder命令为其添加只读属性
或者
1SetFileAttributesW(folderPath.toStdWString().c_str(), FILE_ATTRIBUTE_READONLY);
或者
12// Make this a system folder, so that we look for desktop.ini when we navigate to this folder.PathMakeSystemFolder(folderPath.toStdWString().c_str());
大家看到这里可能一头雾水,别急
首先,这可能很反直觉,但是文件夹上的只读(R)属性与文件不同,并不是“只读”的本意。”This attribute is not honored on directories. “ – SetFileAttributesW - READONLY. 在文件夹上,该属性和S属性一样,更多的这是一个标记,指示系统去进行一些特殊操作,例如:查找并加载desktop.ini
PathMakeSystemFolder,这个函数看起来是给文件夹赋予System属性,但实际上他会视情况,给予R或S属性,一般情况下是Read-Only属性。由于Windows是闭源系统,所以这里给出一个不知道是不是源码的源码 (我看到了两份不同的实现,所以不确定代码是否可靠)
好的,不管怎么样,到目前为止,我们已经达成了为文件夹自定义图标的所有条件。
正片叠底以上都是洒洒水,相信大家噼里啪啦、叽里呱啦就查出来了
图标缓存主要问题在于:为什么上述条件都达成之后,文件夹图标还是没有变化,或者说,延迟更新
传统功夫(無駄)可能有聪明的同学会说了:这题我会,用这个函数通知系统更改即可,SHChangeNotify(SHCNE_ATTRIBUTES, ...)
很遗憾,起码对于Windows 11的文件夹来说,该函数没有任何鸟用
无论是:SHCNE_ATTRIBUTES、SHCNE_UPDATEITEM 或是 SHCNE_ASSOCCHANGED,甚至是SHCNE_ALLEVENTS
Even:SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM)"Environment", SMTO_ABORTIFHUNG, 5000, NULL)
都无济于事…
还有一些無駄的方法我也罗列一下:
ie4uinit.exe -show
nircmd.exe sysrefresh (第三方)
参见:windows - Update folder icon with desktop.ini & instantly change (C++) - Stack Overflow
NO it doesn’t work at all. 5 hour in computer during the night works as mirage —— Piotr Sydow
以上方法都无法立即刷新图标缓存(通常在几分钟后刷新)
暴力美学(保底)当然,我们都知道,重启能解决90%问题
是的没错,重启资源管理器(explorer.exe)可以解决这个问题
12taskkill /f /im explorer.exestart explorer
BUT:
重启并不能算立即
用户体验,非常非常非常非常,BAD,納得できない
从人机交互和产品设计角度来说,非常失败,无法接受,仅此一项就会让用户流失
更不用说已经有软件能够做到不重启的情况下立即刷新 - FolderIco
ta 能做到,就说明:理论存在,实践开始
毁灭还有一种方法在民间广为流传,实属暴力楷模:[2]
123456789101112131415161718rem 关闭Windows外壳程序explorertaskkill /f /im explorer.exerem 清理系统图标缓存数据库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_32.db"del /f "%userprofile%\AppData\Local\Microsoft\Windows\Explorer\thumbcache_96.db"del /f "%userprofile%\AppData\Local\Microsoft\Windows\Explorer\thumbcache_102.db"del /f "%userprofile%\AppData\Local\Microsoft\Windows\Explorer\thumbcache_256.db"del /f "%userprofile%\AppData\Local\Microsoft\Windows\Explorer\thumbcache_1024.db"del /f "%userprofile%\AppData\Local\Microsoft\Windows\Explorer\thumbcache_idx.db"del /f "%userprofile%\AppData\Local\Microsoft\Windows\Explorer\thumbcache_sr.db"rem 清理 系统托盘记忆的图标echo y|reg delete "HKEY_CLASSES_ROOT\Local Settings\Software\Microsoft\Windows\CurrentVersion\TrayNotify" /v IconStreamsecho y|reg delete "HKEY_CLASSES_ROOT\Local Settings\Software\Microsoft\Windows\CurrentVersion\TrayNotify" /v PastIconsStreamrem 重启Windows外壳程序explorerstart explorer
直接删掉所有缓存文件和注册表项(逼Windows重建),并重启资源管理器
哦,我的老天爷啊
杀鸡焉用牛刀,太不优雅了吧,不到万不得已并不建议使用 -_-||
优雅而精准FolderIco已经向我们证明了,存在一种方案,既不需要删除文件,也不需要重启资源管理器,就能刷新图标缓存的方案
虽然问题在于这是闭源软件,看不到源码呜呜
// 难道要反编译嘛,aaa
不,不可能!
要不发个邮件好了,啊,在那之前,一定有办法,o(╥﹏╥)o
SHGetSetFolderCustomSettings经过了七七八十一天的搜索,在见识到了GPT-4o和Claude 3.5对于Windows API的无力之后
我终于找到了,那本真经:SHGetSetFolderCustomSettings
还得是:Stackoverflow
节选评论:
I did some further tests where I found that the supposed “catch all” SHCNE_ASSOCCHANGED is unreliable aswell. But SHGetSetFolderCustomSettings() always updates the icon immediately (despite being a deprecated API since Win XP SP3)!
I also used SHChangeNotify() and it’s not reliable in Win10. Sometimes it works, sometimes not. It doesn’t change it immediately but takes 1 minutes or so.
Yesterday I played around with SHCNE_UPDATEITEM but couldn’t get consistent results. Sometimes it would update the folder icon, sometimes not. I also tried to add SHCNF_FLUSH and SHCNE_UPDATEDIR but the result was still unreliable.
SHGetSetFolderCustomSettings是专门用于读取和写入desktop.ini的函数
(不过为什么GPT不告诉我,aaaaa)
12345678910111213141516void setFolderIcon(const QString &folderPath, const QString &iconPath, int iconIndex = 0){ SHFOLDERCUSTOMSETTINGS fcs = {0}; // 初始化所有成员为0 fcs.dwSize = sizeof(SHFOLDERCUSTOMSETTINGS); fcs.dwMask = FCSM_ICONFILE; auto iconWStr = iconPath.toStdWString(); // IMPORTANT: 不能写为 iconPath.toStdWString().c_str(),因为返回的是临时对象,导致指针无效 fcs.pszIconFile = LPWSTR(iconWStr.c_str()); fcs.cchIconFile = 0; fcs.iIconIndex = iconIndex; // 这里返回临时对象指针没事,因为语句没结束不会被释放 HRESULT hr = SHGetSetFolderCustomSettings(&fcs, folderPath.toStdWString().c_str(), FCS_FORCEWRITE); if (FAILED(hr)) { qWarning() << "Failed to set folder icon"; }}
游戏结束,根本不需要自行新建desktop.ini巴拉的,直接包办
语法小细节这里有个细节坑了我一下
12auto iconWStr = iconPath.toStdWString();fcs.pszIconFile = LPWSTR(iconWStr.c_str());
这里不能缩写为:
1fcs.pszIconFile = LPWSTR(iconPath.toStdWString().c_str());
否则,最终写入desktop.ini中的路径会变得很奇怪
因为.toStdWString()返回的是一个临时对象,那么.toStdWString().c_str()也就是一个临时对象的指针
随时会被销毁(语句结束后)
所以最终就会造成野指针问题,变成随机字符串,bomb(快用Rust)
那么又有小盆友要问了,为什么这句没事
1SHGetSetFolderCustomSettings(&fcs, folderPath.toStdWString().c_str(), FCS_FORCEWRITE);
因为临时对象在语句结束后销毁,所以在SHGetSetFolderCustomSettings执行过程中都万事大吉
Why SHGetSetFolderCustomSettings好的,那么,凭什么,为什么SHGetSetFolderCustomSettings可以做到立即刷新,他调用了什么API,做了什么操作呢?
什么,你说你不想知道,诶,别走啊
咳咳,留下来的都是好饱饱
好吧,答案是:很遗憾,Windows是闭源操作系统,hhhhhhhhhh
o(╥﹏╥)o
真滴米有办法了吗,不行,我去GitHub上搜一搜
你别说,还真有:nt5src/Source/XPSP1/NT/shell/shell32/fldsets.c at master · tongzx/nt5src (github.com)
听说是XP代码泄露
不过呢,不知道是版本太老,还是可信度太低
我在代码里并没有看到什么特殊操作
123456789...if (SUCCEEDED(hret) && (dwReadWrite & FCS_FORCEWRITE)){ // Make desktop.ini hidden SetFileAttributes(szIniFile, FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM); // Make this a system folder, so that we look for desktop.ini when we navigate to this folder. PathMakeSystemFolder(pszPath);}....
都是我们的基操
所以到底为啥啊,aaaaaa
Windows十大未解之谜,看来只能入职微软了
PeaceRef用desktop.ini更新文件夹图标&立即更改(C++)-腾讯云开发者社区-腾讯云 (tencent.com)
batch file - Changing desktop.ini doesn’t update folder icon automatically in Windows - Stack Overflow
windows - Update folder icon with desktop.ini & instantly change (C++) - Stack Overflow
windows - Refresh Icon Cache Without Rebooting - Super User
c++ - How to refresh the folder icon instantly in Windows - Stack Overflow
如何使用 Desktop.ini 自定义文件夹 - Win32 apps | Microsoft Learn
pathMakeSystemFolderW 函数 (shlwapi.h) - Win32 apps | Microsoft Learn
SHChangeNotify 函数 (shlobj_core.h) - Win32 apps | Microsoft Learn
What If the Custom Folder Icon Does Not Show?
tongzx/nt5src: Source code of Windows XP (NT5). Leaks are not from me. I just extracted the archive and cabinet files. (github.com)
SHGetSetFolderCustomSettings 函数 (shlobj_core.h) - Win32 apps | Microsoft Learn
如何改变文件夹的图标(未完成。。。。。。)-CSDN博客
windows - How can I immediately reload a folder icon when desktop.ini is changed - Stack Overflow
Windows-Server-2003/shell/shlwapi/path.c at 5c6fe3db626b63a384230a1aa6b92ac416b0765f · selfrender/Windows-Server-2003 (github.com)
在 Windows 11 上更改文件夹图标的 5 种方法(和 3 个提示)-云东方 (yundongfang.com)
↩windows清理图标缓存并重新加载_windows 图标缓存刷新-CSDN博客
↩