更改文件夹图标 & 使其立即生效
本文最后更新于:2024年7月27日 晚上
前情提要
众所周知,人类更倾向于看图而非文字
图片所包含的信息量更大,也更容易接受
(这就是大家不喜欢读书的原因ba)
言归正传,(这就是大家喜欢GUI
的原因ba)
图像固然重要,但文字也不可或缺
于是乎,本着我都要的心态,现代图形界面中的 文件 & 文件夹 都被设计为:图标 + 文字描述 的形式
需求
系统原生的文件夹图标是个黄色的“文件夹”(物理)
如果所有文件夹的图标长得都一样,那么也就失去了传递特异信息的能力
由于文字的解读要更加困难(后天能力),因此在众多千篇一律的文件夹中快速定位目标则成了一件烦人事
人云
这时候,有的同学要说了:要什么GUI
,我都是直接CLI
的…
打住打住,下一位
同学B:对于Windows
,可以直接在资源管理器中(需要焦点)直接输入想要文件(夹)名称(支持输入法)即可定位
这位同学说得好,不过有了更差异化的图标便更能锦上添花 (๑¯ω¯๑)
设置文件夹图标
GUI
属性-自定义-更改图标[1]
相信大家早就知道了,跳过跳过(skip)
API
咳咳,身为Programmer,我们还是来讨论一下更深入♂♀的内容吧
そもそも,说到底,Windows
设置文件夹图标的原理究竟是什么
本质上就是在这个文件夹内新建了一个desktop.ini
(之前的文章也提到过:Windows 系统级个人文件夹 vs OneDrive简析)
这个文件的属性比较特殊:SH
,aka.System + Hide
1 |
|
这里除了.ico
文件,还可以是exe
,dll
等
我们可以通过以下代码来创建desktop.ini
1 |
|
当你满怀欣喜地盯着文件夹时,—— 文件夹也在盯着你
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
命令为其添加只读属性
或者
1 |
|
或者
1 |
|
大家看到这里可能一头雾水,别急
- 首先,这可能很反直觉,但是文件夹上的只读(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)可以解决这个问题
1 |
|
BUT:
- 重启并不能算立即
- 用户体验,非常非常非常非常,BAD,納得できない
从人机交互和产品设计角度来说,非常失败,无法接受,仅此一项就会让用户流失
更不用说已经有软件能够做到不重启的情况下立即刷新 - FolderIco
ta 能做到,就说明:理论存在,实践开始
毁灭
还有一种方法在民间广为流传,实属暴力楷模:[2]
1 |
|
直接删掉所有缓存文件和注册表项(逼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 addSHCNF_FLUSH
andSHCNE_UPDATEDIR
but the result was still unreliable.
SHGetSetFolderCustomSettings
是专门用于读取和写入desktop.ini
的函数
(不过为什么GPT
不告诉我,aaaaa)
1 |
|
游戏结束,根本不需要自行新建desktop.ini
巴拉的,直接包办
语法小细节
这里有个细节坑了我一下
1 |
|
这里不能缩写为:
1 |
|
否则,最终写入desktop.ini
中的路径会变得很奇怪
因为.toStdWString()
返回的是一个临时对象,那么.toStdWString().c_str()
也就是一个临时对象的指针
随时会被销毁(语句结束后)
所以最终就会造成野指针问题,变成随机字符串,bomb(快用Rust
)
那么又有小盆友要问了,为什么这句没事
1 |
|
因为临时对象在语句结束后销毁,所以在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
代码泄露
不过呢,不知道是版本太老,还是可信度太低
我在代码里并没有看到什么特殊操作
1 |
|
都是我们的基操
所以到底为啥啊,aaaaaa
Windows
十大未解之谜,看来只能入职微软了
Peace
Ref
用desktop.ini更新文件夹图标&立即更改(C++)-腾讯云开发者社区-腾讯云 (tencent.com)
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?
SHGetSetFolderCustomSettings 函数 (shlobj_core.h) - Win32 apps | Microsoft Learn
windows - How can I immediately reload a folder icon when desktop.ini is changed - Stack Overflow