文件评分 - 外壳扩展的实际示例






4.64/5 (23投票s)
2004年12月28日
6分钟阅读

148749

2319
一个Shell扩展,为文件夹提供新的“评分”列,允许按用户兴趣对文件进行排序
引言
本文介绍了我最近实现的一个Shell扩展的实用示例。我曾向朋友们展示旅行照片,想在我的文件夹中只显示最好的照片。一个可能的解决方案是使用一个优秀的照片管理器,但我希望直接在标准的Explorer界面下工作。
这个想法非常简单,就是在Windows XP下为Explorer的文件夹视图添加一个新的“评分”列。这个新列可用于按用户定义的评分对文件(照片、MP3等)进行排序,并且它与Explorer的照片预览功能协同工作,这样我就可以优先显示最好的照片。这种方法的副作用是,它还可以用于按评分对搜索结果进行排序,从而在文件夹树中获得最好的照片。
这是扩展后的Explorer视图的一个示例
以及上下文菜单中的新条目
目录
Shell Extensions
Windows Shell有一个强大的扩展机制,可以为Shell提供新的功能和交互能力。虽然用C++和COM实现这些扩展曾经很困难,但随着.NET的推出,情况发生了很大变化。(在“参考文献”部分,我列出了一些链接。)
这是一个模式图,解释了可以创建的Shell扩展类型以及本文所使用的内容。
调试
使用.NET调试Shell扩展与调试COM相同(请参阅此处)。只需将Explorer.exe指定为调试会话的可执行文件,并将键的DesktopProcess
DWORD
值设置为1。
HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer
此值告诉Explorer为每个窗口创建一个单独的进程,并为任务栏创建一个单独的进程,从而简化了扩展的调试。
注册
注册过程包含一个扩展类型特定的部分和一个通用部分。通用部分主要由regasm.exe实用程序(由VS.NET调用)完成,该实用程序处理所有COM-.NET连接。如果扩展打算安装在非管理员帐户上,则必须将其注册到Approved set of extensions下。
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved
只需在此键下添加一个新的字符串值,其名称为类别的花括号括起来的GUID,并可选择添加描述。要检查已安装的扩展,可以使用SysInternals的Autoruns实用程序。SysInternals。
扩展特定的注册可以直接通过托管代码使用两个特殊属性ComRegisterFunctionAttribute
和ComUnregisterFunctionAttribute
来完成。下面的代码片段展示了如何实现此功能来注册此扩展。
#region Registration
[System.Runtime.InteropServices.ComRegisterFunctionAttribute()]
static void RegisterServer(String str1)
{
try
{
string keyname = "File Rating";
string guid = "{"+typeof(RatingColumnHandler).GUID.ToString()+"}";
RegistryKey rk,rk2;
rk = Registry.ClassesRoot.OpenSubKey(@"*\shellex\ContextMenuHandlers", true);
...
}
catch(Exception e)
{
}
}
[System.Runtime.InteropServices.ComUnregisterFunctionAttribute()]
static void UnregisterServer(String str1)
{
try
{
string keyname = "File Rating";
string guid = "{"+typeof(RatingColumnHandler).GUID.ToString()+"}";
RegistryKey rk;
rk = Registry.ClassesRoot.OpenSubKey(@"*\shellex\ContextMenuHandlers", true);
rk.DeleteSubKey(keyname, false);
...
}
catch(Exception e)
{
}
}
#endregion
实现
File Rating扩展使用Column Handler在Explorer的详细视图中添加一个新列,该列在文件夹中注册(注册表Classes root中的“Folder/shellex/ColumnHandlers”),并通过IColumnProvider
接口实现。列的值可用于对文件进行排序,甚至在Explorer的预览模式下。
我决定将每个文件夹的评分信息存储在一个.rating文件中,其中每一行包含评分、一个制表符,然后是文件名。这是最简单的解决方案,但存在一些问题,我将在未来的工作中讨论。此文件由Column Handler缓存,每次都可以通过时间戳检查其更改。
用户可以通过直接编辑.rating文件来更改评分,或者更好的是,通过与每个文件关联的新上下文菜单项(注册表Classes root中的“*/shellex/ContextMenuHandlers”)来更改评分。为了正确实现此扩展,需要IShellExtInit
和IContextMenu
两个接口。
IShellExtInit
当扩展将在新文件夹上调用时,将调用此接口的Initialize
方法,该方法提供了获取操作涉及的文件夹和文件的机会。此操作通过使用一些与剪贴板和拖放相关的函数进行,并且可以很好地封装在一个辅助类中。文件列表在初始化阶段保存,并在后续的菜单构建和调用中使用(以根据选择更改菜单)。
IContextMenu
此接口用于获取操作涉及的文件选择的实际菜单(QueryContextMenu
)并处理用户选择的选项(InvokeCommand
)。可选地,它可以向Shell提供帮助信息(GetCommandString
)。
QueryContextMenu
回答了菜单布局的问题,需要一些Win32菜单构建。每个条目都与一个标识符相关联,该标识符在用户选择菜单项时传递给InvokeCommand
。在这种情况下,菜单不依赖于选择,而是使用子菜单来分组选项。
int IContextMenu.QueryContextMenu(HMenu hMenu, int iMenu,
int idCmdFirst, int idCmdLast, CMF uFlags)
{
int id = 1;
if ( (uFlags & (CMF.CMF_VERBSONLY|CMF.CMF_DEFAULTONLY|CMF.CMF_NOVERBS)) == 0 ||
(uFlags & CMF.CMF_EXPLORE) != 0)
{
HMenu submenu = ShellLib.Helpers.CreatePopupMenu();
Helpers.AppendMenu(submenu, MFMENU.MF_STRING|MFMENU.MF_ENABLED,
new IntPtr(idCmdFirst + id++), "Rating ++");
Helpers.AppendMenu(submenu, MFMENU.MF_STRING|MFMENU.MF_ENABLED,
new IntPtr(idCmdFirst + id++), "Rating --");
Helpers.AppendMenu(submenu, MFMENU.MF_STRING|MFMENU.MF_ENABLED,
new IntPtr(idCmdFirst + id++), "Zero Rating");
Helpers.AppendMenu(submenu, MFMENU.MF_STRING|MFMENU.MF_ENABLED,
new IntPtr(idCmdFirst + id++), "About");
Helpers.InsertMenu(hMenu, 5,
MFMENU.MF_BYPOSITION|MFMENU.MF_POPUP|MFMENU.MF_ENABLED,
submenu.handle, "File Rating");
}
return id;
}
改进
这个扩展存在一些问题,但它们并不会真正妨碍其使用。
- 对于只读文件夹,.rating文件解决方案不适用,集中式解决方案更好,但这会增加此扩展的复杂性。
- 当文件被重命名或移动时,评分信息会丢失;这可以通过使用文件夹的FileWatcher来解决,但它会产生副作用。
- 在文件夹中添加一个额外的隐藏文件的解决方案并不理想,因为这样的文件太多了。另一种解决方案(也解决了问题#2)是使用NTFS扩展属性。这是NTFS 5.0的一项功能,允许将属性(如Title、Category,在此情况下是Rating)与文件关联。总的来说,文件评分信息可以存储在基于元数据的元数据文件系统中。
- 当用户通过上下文菜单更改评分时,Explorer视图不会更新,需要执行刷新命令。(有人能给我建议如何做到这一点吗?)
- 评分信息可以通过自动方式计算,例如通过跟踪文件使用情况或其他信息,就像最新的Windows Media Player所做的那样。
结论
这篇简短的文章提供了一个Shell扩展的实用示例,可用于对照片或音频文件等文件进行评分。希望它对某些人有所帮助。
参考文献
- MD5ColumnHandler 提供了
IColumnHandler
的基础实现。 - Dino Esposito 的 Managed Context Menu 提供了.NET下上下文菜单处理程序的实现以及扩展的自动注册,尽管该代码存在一些不可移植的问题(例如,将
HMENU
和HANDLE
视为uint
)。 - 有关C++和COM的Shell编程的参考资料,请参阅系列 "编写Shell扩展的完全傻瓜指南"。