本文主要讲述了如何通过windows shell外壳编程接口,实现和SVN状态类似的图标覆盖功能。在查找资料和学习的过程,我参考借鉴了TortoiseGit
项目的Shell部分,该项目和SVN对于图标覆盖的处理都指向了一个名为TortoiseOverlays
的项目,个人认为值得一阅。
Intro
不知使用SVN进行代码版本控制的你,是否有遇到过SVN的图标突然不显示了,于是在网络上搜索 svn icons not showing
或 svn状态图标不显示
….一般来讲结果可能会如下:
-
卸载重装SVN………
-
打开注册表修改某某某…….
-
… …
浏览查阅一番,解决的问题的动力渐渐消失,反而,越来越多的名字进入我的搜索视野,shellicons、图标覆盖,shell对象等等。于是,我想该如何做才能实现像SVN一样的效果呢?(逐渐远离最初的搜索目的…)于是,我又搜索一番,如何实现windows徽标覆盖?哦吼,原来十几年就有文章介绍了,原来有大把的文章介绍呢…那么我呢,只好左抄抄右抄抄,东拼拼西凑凑,站在巨人的肩膀上啦,哦吼~不好意思了诸位。
Shell Interface
实现windows图标覆盖主要使用了IShellIconOverlayIdentifier接口,并继承实现以下方法:
|
|
此方法用于指示图标文件的位置。其中,以pwszIconFile
返回一个以 null 结尾的 Unicode 字符串,其中包含包含图标的文件的全路径。 .dll、.exe和 .ico 文件类型都是可接受的。 如果返回文件名,则必须在 pdwFlags 中设置ISIOI_ICONFILE标志。
Notes:此方法在dll初始化加载时由 Shell 调用,然后将图标添加到系统映像列表,此后变无法修改之,而文件名和索引仅仅用于标识图标覆盖。
也就是说,如果在dll初始化时加载的图标内容是一个A
的形状,那么后继explorer在进行图标覆盖时展示的都是A
,即使你在加载之后将其替换为B
的形状,也无济于事。因为图标只会加载一次。
|
|
此方法用户指定图标覆盖的优先级, 取值范围为0~100,0表示最高优先级,通常都设为0.
Notes: 此优先级值的目的是帮助 Shell 解决为单个对象指定多个图标覆盖时出现的冲突。 Shell 首先使用一组内部规则来确定最高优先级图标覆盖。 如果这些规则无法解决冲突,则 GetPriority 分配给图标覆盖的值确定优先级。
|
|
此方法表示是否展示覆盖图标,当返回S_OK时表示展示,S_FALSE时表示不展示。入参pwszPath
是一个Unicode 字符串的文件路径,我们可以在此基础添加业务逻辑用于处理图标覆盖的展示。例如,当文件后缀名为.txt
时返回S_OK,否则返回S_FALSE.
Dev & Impl
有了之前右键菜单的工程建立、编码和调试经验,再实现windows的图标覆盖变得十分容易。当然,这本身也不难,毕竟MSDN也有相应的实现文章见参考。
Project
此部分内容于文章《windows 右键菜单实践》重复,就不做介绍了。
Coding
在此,我们以实现给.txt
文件添加图标覆盖为例。
|
|
|
|
在此,我们还需修改一下.rgs
文件,值得注意的为了使我们的图标排序更靠前,在名称的前面加了很多的空格。
|
|
Debug & Demo
调试部分内容于文章《….》重复,就不做介绍了。
How to Create Shell Icons?
首先,可以做这么一个尝试:用下面的工具打开SVN的状态图标(相信你能很容易就找到SVN的状态图标,无论是从本地还是github)看看,就会发现左边展示了不同尺寸的png图标,即16*16、32*32、48*48、256*256…..
我们可以使用如下两个工具制作windows的图标:
-
Axialis IconWorkshop 专业、强大,30天的免费试用期。在参考链接中有一篇关于如何使用其制作图标的文章可供参阅
-
Greenfish Icon Editor Pro 1.72 免费,但没有Axialis IconWorkshop强大
此外,我还找到一个png转ico格式的工具,即能够将多张尺寸(整除8)的.png合并成一张.ico,见参考的png2ico
链接。当然,你也可以动手编写一个类似的工具,也不是什么难事。
|
|
Finally
图标个数问题:因此回到最初的那个问题,也许你的SVN状态图标不见了可能仅仅是因为“名额”被别的软件占用了…..是的,图标的个数是有限的(上限应该是15个,见参考链接),当系统中存在多个图标时,如果你的图标排在别人的后面,那么将得不到展示的机会。因此,为了排在别人的前面,不得不使用一些稍显流氓的手段以确保我们的覆盖图标总能显示。而方法便是:给徽标注册表值前面加上很多空格(毕竟空格是ASCII码中能用中排在最前面的字符了)。
随心所欲地触发图标刷新:当文件发生改变时,比如修改时间、文件名等发生了变化那么explorer.exe便会触发覆盖图标的刷新。但当上述变化没有发生时,那么也就不会触发刷新,即图标的刷新是一种被动行为。有没有可能实现在我们想要刷新的时候刷新图标呢?即使文件信息未发生改变。答案当然是可行的。在windows中有一个接口:
|
|
其中的参数众多,详情请见链接参阅MSDN文档。一个例子是:
|
|
无法实现图标主题风格切换实时生效:在SVN中我们可以使用不同状态图标风格,然而这些主题切换操作并不会立即生效,而是需要重启电脑(或是重启explorer.exe)。这个问题我们在说明IShellIconOverlayIdentifier::GetOverlayInfo
时已经提到,这是因为图标的加载仅会发生一次。
设计视觉效果更好的图标:我们惊奇的发现,SVN的状态图标并不是布满整个画布,而是将有效的内容仅仅绘制在左下角(经测量占比大约在0.625~0.628),很显然是有意为之。为此,我尝试比较了布满和居于左下角的图标的覆盖效果,结果显示在某些缩放比例下,布满的总是要逊色于居于左下角的。
Reference
Why is there a limit of 15 shell icon overlays? - The Old New Thing
tortoisesvn/src/TortoiseOverlays
如何实现图标覆盖处理程序 - Win32 apps | Microsoft Learn
dkfans/png2ico: Converts PNG files to Windows .ICO icon resource files.
png2ico - PNG to icon converter (Linux,GNU,Windows,Unix)
How to create your own Windows icons? - Axialis Software
SHChangeNotify function (shlobj_core.h) - Win32 apps | Microsoft Learn
💭notes:封面照片来自哦吼,记录汤山的美好之行.