65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.64/5 (23投票s)

2004年12月28日

6分钟阅读

viewsIcon

148749

downloadIcon

2319

一个Shell扩展,为文件夹提供新的“评分”列,允许按用户兴趣对文件进行排序

引言

本文介绍了我最近实现的一个Shell扩展的实用示例。我曾向朋友们展示旅行照片,想在我的文件夹中只显示最好的照片。一个可能的解决方案是使用一个优秀的照片管理器,但我希望直接在标准的Explorer界面下工作。

这个想法非常简单,就是在Windows XP下为Explorer的文件夹视图添加一个新的“评分”列。这个新列可用于按用户定义的评分对文件(照片、MP3等)进行排序,并且它与Explorer的照片预览功能协同工作,这样我就可以优先显示最好的照片。这种方法的副作用是,它还可以用于按评分对搜索结果进行排序,从而在文件夹树中获得最好的照片。

这是扩展后的Explorer视图的一个示例

Sample image

以及上下文菜单中的新条目

Sample image

目录

  1. Shell扩展 - 概述、调试、注册
  2. 实现
  3. 改进
  4. 结论
  5. 参考文献

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

扩展特定的注册可以直接通过托管代码使用两个特殊属性ComRegisterFunctionAttributeComUnregisterFunctionAttribute来完成。下面的代码片段展示了如何实现此功能来注册此扩展。

#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”)来更改评分。为了正确实现此扩展,需要IShellExtInitIContextMenu两个接口。

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;
}

改进

这个扩展存在一些问题,但它们并不会真正妨碍其使用。

  1. 对于只读文件夹,.rating文件解决方案不适用,集中式解决方案更好,但这会增加此扩展的复杂性。
  2. 当文件被重命名或移动时,评分信息会丢失;这可以通过使用文件夹的FileWatcher来解决,但它会产生副作用。
  3. 在文件夹中添加一个额外的隐藏文件的解决方案并不理想,因为这样的文件太多了。另一种解决方案(也解决了问题#2)是使用NTFS扩展属性。这是NTFS 5.0的一项功能,允许将属性(如Title、Category,在此情况下是Rating)与文件关联。总的来说,文件评分信息可以存储在基于元数据的元数据文件系统中。
  4. 当用户通过上下文菜单更改评分时,Explorer视图不会更新,需要执行刷新命令。(有人能给我建议如何做到这一点吗?)
  5. 评分信息可以通过自动方式计算,例如通过跟踪文件使用情况或其他信息,就像最新的Windows Media Player所做的那样。

结论

这篇简短的文章提供了一个Shell扩展的实用示例,可用于对照片或音频文件等文件进行评分。希望它对某些人有所帮助。

参考文献

© . All rights reserved.