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

.NET Shell Extensions - Shell Thumbnail Handlers (缩略图处理程序)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.85/5 (15投票s)

2013年3月17日

CPOL

8分钟阅读

viewsIcon

128100

downloadIcon

4506

使用 .NET 创建 Shell 缩略图处理程序扩展!

引言

Shell 缩略图处理程序(有时也称为 Shell 缩略图提供程序)是 COM 服务器,您可以编写它们来定制 Windows Shell 中缩略图图标的外观。我们很熟悉其中许多,例如,我们期望看到 JPEG 文件的缩略图,但事实证明,创建自己的缩略图处理程序非常简单,尤其是借助 SharpShell 库的帮助。

在本文中,我将向您展示如何为文本文件编写 Shell 缩略图处理程序。如果您想创建自己的 shell 缩略图提供程序,这是一个很好的起点。

上图:文本文件示例缩略图处理程序的屏幕截图。此缩略图处理程序会渲染文本的前几行。

系列文章

本文是 '.NET Shell Extensions' 系列的一部分,该系列包括

  1. .NET Shell Extensions - Shell Context Menus (右键菜单扩展)
  2. .NET Shell Extensions - Shell Icon Handlers (图标处理程序)
  3. .NET Shell Extensions - Shell Info Tip Handlers (信息提示处理程序)
  4. .NET Shell Extensions - Shell Drop Handlers (拖放处理程序)
  5. .NET Shell Extensions - Shell Preview Handlers (预览处理程序)
  6. .NET Shell Extensions - Shell Icon Overlay Handlers (图标叠加处理程序)
  7. .NET Shell Extensions - Shell Thumbnail Handlers (缩略图处理程序)
  8. .NET Shell Extensions - Shell Property Sheets (属性表处理程序)

入门

通常,编写 Shell 扩展处理程序涉及创建一个实现一套定义明确的 COM 接口的类。现在,由于这些 COM 接口通常不包含在 .NET Framework 中,因此我们必须自己实现它们。但是,SharpShell 库包含了所有这些接口,并且为扩展提供了基类,这使得实现它们非常简单。现在,让我们开始创建文本文件缩略图处理程序。

我们的目标

我们将在稍后分步构建扩展,但首先让我们看看最终结果,因为我认为它很好地展示了 SharpShell 使实现这些处理程序变得多么容易。下面是扩展的完整代码 - 如您所见,它非常简单明了!现在不用太担心细节,因为我们将在下一步从头开始构建扩展。

/// <summary>
/// The TxtThumbnailHandler is a ThumbnailHandler for text files
/// </summary>
[ComVisible(true)]
[COMServerAssociation(AssociationType.FileExtension, ".txt")]
public class TxtThumbnailHandler : SharpThumbnailHandler
{
    /// <summary>
    /// Initializes a new instance of the <see cref="TxtThumbnailHandler"/> class
    /// </summary>
    public TxtThumbnailHandler()
    {
        //  Create our lazy objects
        lazyThumbnailFont = new Lazy<Font>(() => new Font("Courier New", 12f));
        lazyThumbnailTextBrush = new Lazy<Brush>(() => new SolidBrush(Color.Black));
    }
 
    /// <summary>
    /// Gets the thumbnail image
    /// </summary>
    /// <param name="width">The width of the image that should be returned.</param>
    /// <returns>
    /// The image for the thumbnail
    /// </returns>
    protected override Bitmap GetThumbnailImage(uint width)
    {
        //  Attempt to open the stream with a reader
        try
        {
            using(var reader = new StreamReader(SelectedItemStream))
            {
                //  Read up to ten lines of text
                var previewLines = new List<string>();
                for (int i = 0; i < 10; i++)
                {
                    var line = reader.ReadLine();
                    if (line == null)
                        break;
                    previewLines.Add(line);
                }
 
                //  Now return a preview of the lines
                return CreateThumbnailForText(previewLines, width);
            }
        }
        catch (Exception exception)
        {
            //  Log the exception and return null for failure
            LogError("An exception occurred opening the text file.", exception);
            return null;
        }
    }
 
    /// <summary>
    /// Creates the thumbnail for text, using the provided preview lines
    /// </summary>
    /// <param name="previewLines">The preview lines.</param>
    /// <param name="width">The width.</param>
    /// <returns>
    /// A thumbnail for the text
    /// </returns>
    private Bitmap CreateThumbnailForText(IEnumerable<string> previewLines, uint width)
    {
        //  Create the bitmap dimensions
        var thumbnailSize = new Size((int) width, (int) width);
 
        //  Create the bitmap
        var bitmap = new Bitmap(thumbnailSize.Width, 
                                thumbnailSize.Height, PixelFormat.Format32bppArgb);
 
        //  Create a graphics object to render to the bitmap
        using (var graphics = Graphics.FromImage(bitmap))
        {
            //  Set the rendering up for anti-aliasing
            graphics.TextRenderingHint = TextRenderingHint.AntiAlias;
 
            //  Draw the page background
            graphics.DrawImage(Properties.Resources.Page, 0, 0, 
                               thumbnailSize.Width, thumbnailSize.Height);
            
            //  Create offsets for the text
            var xOffset = width * 0.2f;
            var yOffset = width * 0.3f ;
            var yLimit = width - yOffset;
 
            graphics.Clip = new Region(new RectangleF(xOffset, yOffset, 
              thumbnailSize.Width - (xOffset * 2), thumbnailSize.Height - width*.1f));
 
            //  Render each line of text
            foreach (var line in previewLines)
            {
                graphics.DrawString(line, lazyThumbnailFont.Value, 
                                    lazyThumbnailTextBrush.Value, xOffset, yOffset);
                yOffset += 14f;
                if (yOffset + 14f > yLimit)
                    break;
            }
        }
 
        //  Return the bitmap
        return bitmap;
    }
 
    /// <summary>
    /// The lazy thumbnail font
    /// </summary>
    private readonly Lazy<Font> lazyThumbnailFont;
 
    /// <summary>
    /// The lazy thumbnail text brush
    /// </summary>
    private readonly Lazy<Brush> lazyThumbnailTextBrush;
}

要获得一个可工作的 shell 扩展,这并不需要太多工作!现在让我们深入了解细节。

步骤 1:创建项目

首先,创建一个新的 C# 类库项目。

提示:您可以使用 Visual Basic 而不是 C# - 在本文中,源代码是 C#,但创建 Visual Basic Shell 扩展的方法是相同的。

在此示例中,我们将项目命名为 'TxtThumbnailHandler'。将 'Class1.cs' 文件重命名为 'TxtThumbnailHandler.cs'。

现在添加以下引用

  1. System.Windows.Forms
  2. System.Drawing

这些引用包含了 SharpShell 库的其他部分所需的各种有用的小部件,例如图标和上下文菜单。

提示:如果您使用 Nuget 安装 SharpShell(请参见“第 2 步”),则无需添加这些引用 - 它们将自动添加。

步骤 2:引用 SharpShell

现在我们需要添加对核心 SharpShell 库的引用。你可以通过几种不同的方式做到这一点:

添加引用

下载文章顶部的“SharpShell Core Library”zip 文件,并添加对 SharpShell.dll 文件的引用。

提示:本文中的下载在撰写本文时是正确的 - 如果您需要最新版本,请使用 Nuget(如下所述)或从 sharpshell.codeplex.com 获取库。

使用 Nuget

如果您安装了 Nuget,只需快速搜索 SharpShell 并直接安装它 - 或在 https://nuget.net.cn/packages/SharpShell 上获取包详细信息。

重要提示SharpShell 正在定期更新,我经常收到功能请求并快速实现它们,因此我强烈建议使用 Nuget 包来引用 SharpShell - 自动保持其最新状态更容易。

第 3 步:从 SharpThumbnailHandler 派生

现在我们已经设置好了项目,我们可以将 TxtThumbnailHandler 类从 SharpThumbnailHandler 派生,它是 Thumbnail Handler Shell Extensions 的基类。

public class TxtThumbnailHandler : SharpThumbnailHandler
{
} 

对于缩略图处理程序,基类中有一个抽象函数,我们必须在派生类中实现它。

GetThumbnailImage

protected abstract Bitmap GetThumbnailImage(uint width);

此函数将被调用以获取缩略图的位图。理解以下几点非常重要:

  1. 缩略图处理程序通过流进行初始化,而不是文件路径。这意味着您不知道您正在预览的文件的路径,您只知道它的流。
  2. 文件流存储在 SelectedItemStream 属性中。您可以使用它来读取文件并构建缩略图。
  3. 尽量遵守 width 变量,它是系统所需的width。但是,最坏的情况下,它也会缩放您的图像(如果需要)。
  4. 通常,您会将位图的height设置为与width相同,但也可以小于,系统会尊重这一点。但是,缩略图的高度不能大于其宽度。

现在让我们看看如何在我们的示例中实现此函数。

protected override Bitmap GetThumbnailImage(uint width)
{
    //  Create a stream reader for the selected item stream
    try
    {
        using(var reader = new StreamReader(SelectedItemStream))
        {
            //  Read up to ten lines of text
            var previewLines = new List<string>();
            for (int i = 0; i < 10; i++)
            {
                var line = reader.ReadLine();
                if (line == null)
                    break;
                previewLines.Add(line);
            }
 
            //  Now return a preview of the lines
            return CreateThumbnailForText(previewLines, width);
        }
    }
    catch (Exception exception)
    {
        //  Log the exception and return null for failure
        LogError("An exception occurred opening the text file.", exception);
        return null;
    }
}

首先,我们确保在大部分逻辑周围使用异常处理程序。

提示:SharpShell 会将所有对抽象函数的调用包装在异常处理程序中,并在服务器以调试模式编译时将异常记录到 Windows 事件日志。但如果我们预计到异常,最好自己处理异常。不要忘记,您可以调用 'Log' 或 'LogError' 来写入 SharpShell Windows 应用程序事件日志。

接下来,我们为 SelectedItemStream(即我们正在预览的 shell 项的流)创建一个流读取器。然后,我们从中读取几行文本,并传递给 CreateThumbnailForText 函数。

因此,我们还需要创建 CreateThumbnailForText 函数。

private Bitmap CreateThumbnailForText(IEnumerable<string> previewLines, uint width)
{
    //  Create the bitmap dimensions
    var thumbnailSize = new Size((int) width, (int) width);
 
    //  Create the bitmap
    var bitmap = new Bitmap(thumbnailSize.Width, thumbnailSize.Height, 
                            PixelFormat.Format32bppArgb);
 
    //  Create a graphics object to render to the bitmap
    using (var graphics = Graphics.FromImage(bitmap))
    {
        //  Set the rendering up for anti-aliasing
        graphics.TextRenderingHint = TextRenderingHint.AntiAlias;
 
        //  Draw the page background
        graphics.DrawImage(Properties.Resources.Page, 0, 0, thumbnailSize.Width, 
                           thumbnailSize.Height);
        
        //  Create offsets for the text
        var xOffset = width * 0.2f;
        var yOffset = width * 0.3f ;
        var yLimit = width - yOffset;
 
        graphics.Clip = new Region(new RectangleF(xOffset, yOffset, 
                thumbnailSize.Width - (xOffset * 2), thumbnailSize.Height - width*.1f));
 
        //  Render each line of text
        foreach (var line in previewLines)
        {
            graphics.DrawString(line, lazyThumbnailFont.Value, 
                                lazyThumbnailTextBrush.Value, xOffset, yOffset);
            yOffset += 14f;
            if (yOffset + 14f > yLimit)
                break;
        }
    }
 
    //  Return the bitmap
    return bitmap;
} 

同样,这里的逻辑相当简单。我们创建一个请求大小的位图,确保使用 32 位 ARGB 模式(即,确保我们有一个 alpha 通道)。然后我们创建一个图形对象在其上绘制。

之后,我们将一个*.png 资源文件渲染到位图上(资源文件在示例项目中,它是一张页面图像)。现在我们只需在页面图像上渲染一些文本(确保在刚刚绘制的图像边界内)。

就是这样 - 我们可以返回位图,我们就完成了!嗯,实际上,此函数中有两个成员变量,这是它们的声明:

    public TxtThumbnailHandler()
    {
        //  Create our lazy objects
        lazyThumbnailFont = new Lazy<Font>(() => new Font("Courier New", 12f));
        lazyThumbnailTextBrush = new Lazy<Brush>(() => new SolidBrush(Color.Black));
    }
 
    /// <summary>
    /// The lazy thumbnail font
    /// </summary>
    private readonly Lazy<Font> lazyThumbnailFont;
 
    /// <summary>
    /// The lazy thumbnail text brush
    /// </summary>
    private readonly Lazy<Brush> lazyThumbnailTextBrush;  

步骤 4:处理 COM 注册

只剩下几件事情要做。首先,我们必须为我们的类添加 ComVisible 属性。这是因为我们的类是一个 COM 服务器,必须对尝试使用它的其他代码可见。

[ComVisible(true)]
public class TxtThumbnailHandler : SharpThumbnailHandler

接下来,我们必须为程序集指定强名称。虽然有绕过此要求的方法,但通常这是最佳方法。为此,右键单击项目并选择 '属性'。然后转到 '签名'。选择 '签名程序集',为密钥指定 '新建' 并选择一个密钥名称。您可以选择为密钥添加密码,但这并非必需。

最后,我们需要将我们的扩展与我们希望使用它的某些类型的 shell 项相关联。我们可以使用 COMServerAssociation 属性来完成此操作。

[ComVisible(true)]
[COMServerAssociation(AssociationType.FileExtension, ".txt")]
public class TxtThumbnailHandler : SharpThumbnailHandler

那么我们在这里做了什么?我们告诉 SharpShell,在注册服务器时,我们希望将其与*.txt 文件相关联。

您可以关联文件、文件夹、类、驱动器等 - 有关使用关联属性的完整文档可在 CodePlex 网站上的 COM Server Associations 中找到。

提示:许多 SharpShell 服务器支持 'AssociationType.ClassOfExtension' 关联。使用此选项,您可以提供类似*.txt 的内容,服务器将为所有文本文件(可能是txttexttxtfiledat 等)注册。请注意,缩略图处理程序不支持 ClassOfExtension 关联类型!

我们完成了!构建项目会创建一个 TxtThumbnailHandler 程序集,该程序集可以注册为 COM 服务器,将扩展添加到系统中,从而可以快速预览文本文件的内容。

调试 Shell Extension

如果您看过我关于 .NET Shell 扩展的其他文章,您可能会认出 'Server Manager' 工具。这是 SharpShell 源代码中的一个工具,可用于帮助调试 Shell 扩展。

提示:如果您想要最新版本的工具,可以从 CodePlex 网站预编译下载。

打开 Server Manager 工具,使用 文件 > 加载服务器 加载 TxtThumbnailHandler.dll 文件。您也可以将服务器拖到主窗口中。选择服务器将显示一些关于它的详细信息。选择服务器。

现在您可以选择 '在测试 Shell 中显示文本服务器'。与服务器关联的所有文件都会以粗体突出显示,选择文件,然后选择右侧的缩略图选项卡 - 将使用您的缩略图处理程序来生成缩略图预览。

安装和注册 Shell Extension

有关如何安装和注册这些扩展的详细信息,您可以查阅 .NET Shell 扩展 - Shell 上下文菜单中 ' 安装和注册 Shell 扩展 ' 部分 - 该过程相同。

故障排除

对 Shell 扩展进行故障排除非常困难,因此我有一个页面,我正在不断更新一些技巧,它位于 CodePlex 网站上:

历史

  • 2013 年 3 月 17 日:初始版本
© . All rights reserved.