编写一个弹出式菜单的shell程序并实现_[翻译] 从零开始的 .Net Shell 扩展教程 (五) - Shell 预览处理程序...

58d05d69e6caad274f20dd037361cb30.png
Vukr:[翻译] 从零开始的 .Net Shell 扩展教程 (四) - Shell 拖拽处理程序​zhuanlan.zhihu.com
d9cf06a916387e757b022de92af3d5ce.png

使用 .Net 快速为 Windows 资源管理器或 Outlook 创建 Shell 预览处理程序!

介绍

Shell Preview Handler 是在系统中注册的 DLL, 它可以使您直接在 Windows 资源管理器中为选中的项目创建外观丰富的预览. 它们也可以在 Outlook 中使用. 在本文中, 我将向您展示如何使用 .Net 和 SharpShell库来创建提示 Preview Handler 扩展.

90d98309d25bf448f01d65145056045e.png

上图: 我的机器上充满图表的文件夹. 当选择图标并打开预览窗格后, "图标预览处理程序" 扩展将显示文件中每个图标不同大小的图标图像.

系列文章

本文是 ".NET Shell扩展" 系列的一部分,其中包括:

  1. .NET Shell扩展-Shell 上下文菜单
  2. .NET Shell扩展-Shell 图标处理程序
  3. .NET Shell扩展-Shell 信息提示处理程序
  4. .NET Shell扩展-Shell Drop 处理程序
  5. .NET Shell扩展-Shell 预览处理程序
  6. .NET Shell扩展-Shell 图标叠加处理程序
  7. .NET Shell扩展-Shell 缩略图处理程序
  8. .NET Shell扩展-Shell 属性表
  9. .NET Shell扩展-部署 SharpShell 服务

预览处理程序如何工作

预览处理程序非常酷. 自从 SharpShell 增加了对它的支持以来, 我就一直保持预览窗格的打开状态, 并为它们的实用性感到惊讶. 本质上, 当您在资源管理器中选择一个文件并打开预览处理程序时, 系统将执行以下操作:

  1. 检查注册表以查看该文件类型是否有预览处理程序.
  2. 如果存在, 则创建一个 prevhost 进程的实例并加载预览处理程序的 DLL.
  3. 实例化对象. 该对象实现了 IPreviewHandler.
  4. 告诉该对象父窗口以及其大小等信息
  5. 通知该对象渲染预览, 并提供文件详细信息 (路径, 文件流或 IshellItem)

这是一个非常简单的过程, 预览处理程序的文档也十分详细. 然而, 编写所有的 COM 管道需要大量的平台调用 (P/Invoke), 一个小错误就会导致在资源管理器中引发异常, 并且通常还很麻烦. 而 SharpShell 会为您预先实现这些管道, 您所要做的也仅剩创建 UI 和处理逻辑了.

让我们开始创建图标预览处理程序.

第一步: 建立项目

首先, 创建一个新的 C# 类库 (C# Class Library) 项目.

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

在这个例子中, 我们将项目叫做 'IconPreviewHandler'. 将 'Class1.cs' 文件重命名为 'IconPreviewHandler.cs'.

现在添加下列引用:

  1. System.WIndows.Forms
  2. System.Drawing

这些引用被 SharpShell 库的其他部分所需, 例如图标和上下文菜单.

提示: 如果您使用 Nuget 来安装 SharpShell (见 "第二步"), 您就不再需要添加这些引用了 - 它们将会被自动添加

第二步: 引用 SharpShell

现在我们需要添加核心库 SharpShell 的引用. 您可以通过几种不同的方式进行此操作.

添加引用

下载文章顶部名为 "SharpShell Library" 的 zip 文件, 并对下载的 SharpShell.dll 添加引用.

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

使用 Nuget

如果已安装 Nuget, 则只需要快速 SharpShell 并直接安装即可. 或者在https://www.nuget.org/packages/SharpShell中获取软件包的详细信息.

第三步: 从 SharpPreviewHandler 继承 (Deriving from SharpPreviewHandler)

现在我们已经建立了项目. 使你的类 IconPreviewHandler 继承 SharpPreviewHandler. SharpPreviewHandler 是预览程序 Shell 扩展的基类.

public class IconPreviewHandler : SharpPreviewHandler
{
}

对于预览处理程序, 必需在子类中实现基类的一个抽象函数.

DoPreview

protected abstract PreviewHandlerControl DoPreview();

DoPreview 当类需要为预览创建用户界面时被调用. 它返回一个 PreviewHandlerControl - 这仅仅是一个 UserControl, 以及一些额外的虚函数. 通常, 将发生下列情况:

  1. 子类创建一些用户控件的实例.
  2. 用户控件根据 SelectFilePath 属性来填充数据.
  3. 返回用户控件

这些实际上就是您需要做的.

因此, 对于该项目而言, 添加一个新的用户控件, 并称其为 IconHandlerPreviewControl. 将其基类更改为 PreviewHandlerControl, 然后将 "DoPreview" 功能添加到您的 IconPreviewHandler 类中:

/// <summary>
/// DoPreview must create the preview handler user interface and initialize it with data
/// provided by the shell.
/// </summary>
/// <returns>
/// The preview handler user interface
/// </returns>
protected override PreviewHandlerControl DoPreview() {
    //  Create the handler control
    var handler = new IconPreviewHandlerControl();
    //  Do we have a file path? If so, we can do a preview
    if(!string.IsNullOrEmpty(SelectedFilePath))
        handler.DoPreview(SelectedFilePath);
    //  Return the handler control
    return handler;
}

这里, 我们实际上调用了 IconHandlerPreviewControl 的函数 DoPreview, 它还没有被实现. 如果您需要细节的话, 可以在文章的顶部获取源码. 不过它非常简单, 可以插入到此处:

public void DoPreview(string selectedFilePath) {
    //  Load the icons
    try {
        var multiIcon = new MultiIcon();
        multiIcon.Load(selectedFilePath);
        //  Add the icon images
        foreach (var iconImage in multiIcon.SelectMany(singleIcon => singleIcon))
            iconImages.Add(iconImage);
        //  Add the icons to the control
        AddIconsToControl();
    }
    catch {
        //  Maybe we could show something to the user in the preview
        //  window, but for now we'll just ignore any exceptions.
    }
}

我们使用出色的 IconLib 库加载图标, 然后存储每个对象. 之后调用 AddIconsToControl 函数将它们实际添加至 UI:

private void AddIconsToControl()
{
    //  Go through each icon, keep track of y pos
    int yPos = 12;
    foreach (var iconImage in iconImages) {
        //  Create the description
        var descriptionLabel = new Label {
            Location = new Point(12, yPos),
            Text = string.Format("{0}x{1} - {2}",
                   iconImage.Size.Width, iconImage.Size.Height, iconImage.PixelFormat),
            AutoSize = true,
            BackColor = Color.White
        };
        yPos += 20;

        //  Create the picture box
        var pictureBox = new PictureBox {
            Location = new Point(12, yPos),
            Image = iconImage.Icon.ToBitmap(),
            Width = iconImage.Size.Width,
            Height = iconImage.Size.Height
        };
        yPos += iconImage.Size.Height + 20;
        panelImages.Controls.Add(descriptionLabel);
        panelImages.Controls.Add(pictureBox);

        //  Keep track of generated labels
        generatedLabels.Add(descriptionLabel);
    }
}

其他的一些成员变量在这里不再赘述. 上面的部分是核心功能 - 遍历每个图标图像并创建一个描述它的标签和一个包含图像的图片框.

第四步: 处理 COM 注册

还有一些事情要做. 首先, 我们必须将 COMVisible 属性添加到我们的类中. 这是因为我们的类是 COM 服务, 因此它必须可见.

[ComVisible(true)]
public class IconPreviewHandler : SharpPreviewHandler

接下来, 我们必须给这个程序集一个强名称 (strong name). 有很多方式可以实现此需求, 不过我们有一个最好的方法. 为此, 我们需要在项目上单击鼠标右键, 然后点击 "属性", 然后转到 "签名", 选择 "为程序集签名", 单击 "选择强名称密钥文件" 下的下拉列表, 选择 "新建". 之后新建一个密钥, 您可以根据需要对密钥进行密码保护, 但这不是必需的.

最后一步, 我们需要将我们的扩展与部分 Shell 项的类型关联. 我们可以通过 COMServerAssociation 属性来做到这一点:

[COMServerAssociation(AssociationType.ClassOfExtension, ".ico")]
[DisplayName("Icon Preview Handler")]
public class IconPreviewHandler : SharpPreviewHandler

那么, 我们在这一步都做了什么? 我们告诉 SharpShell 当注册这个服务时, 我们希望它与系统中的 icofile 类型进行关联.

您可以与文件夹, 驱动器, 未知文件, 以及特定的扩展名相关联. 有关此功能的完整文档, 请访问SharpShell CodePlex页面上的COM 服务关联.

译者注: 这个页面已经停止服务了. 文档请访问此 Github链接.

我们也设置了 DispalyName 属性. DisplayName 会被注册在注册表中. 如果处理程序失败, 也会使用到它 (在预览窗格里显示 "图标预览处理程序无法加载" 或类似内容的消息).

就这样, 构建该项目将创建 IconPreviewHandler 程序集. 这个程序集可以注册为 COM 服务, 从而允许您快速查看 ico 文件包含的图像.

高级特性

PreviewHandlerControl 类包含下列虚函数:

/// <summary>
/// Sets the color of the background, if possible, to coordinate with the windows
/// color scheme.
/// </summary>
 /// <param name="color">The color.</param>
protected virtual void SetVisualsBackgroundColor(Color color){}

/// <summary>
/// Sets the color of the text, if possible, to coordinate with the windows
/// color scheme.
/// </summary>
/// <param name="color">The color.</param>
protected virtual void SetVisualsTextColor(Color color){}

/// <summary>
/// Sets the font, if possible, to coordinate with the windows
/// color scheme.
/// </summary>
/// <param name="font">The font.</param>
protected virtual void SetVisualsFont(Font font){}

您可以重写它们来改变您 UI 的颜色和字体, 以匹配当前系统主题中自定义的颜色和字体.

调试 Shell 扩展

如果您看过 .Net Shell 扩展 - 上下文菜单 一文, 可能会会想起工具 'Server Manager'. 这是一个包含在 SharpShell 源码中的工具, 可以用来帮助调试 Shell 扩展.

提示: 如果您想要这个工具的最新版本 可以从 CodePlex 页面预先构建 译者注: 请前往 Github release 页面下载. 另外如果使用 scoop 的话, 同样可以在我的 buckets 中找到该程序

打开 Server Manager, 然后使用 'File' > 'Load Server' 来加载构建好的的 server 文件(DLL). 您同样可以直接将 server 文件拖拽进主窗口. 选中这个 server , 右侧将会显示它的一些细节信息. 选中这个服务.

现在按下 'Test Server' 或使用 'Server > Test'. 这将打开测试 Shell, 该 Shell 将模拟将要对服务进行的调用, 就像 Windows Shell正在进行调用一样. 因为这是一个托管应用程序, 因此您可以快速将调试器附加到这个 Shell 上, 然后查看您的服务是怎么运行的. 它可以让你测试你的服务, 而不需要在系统安装或注册它, 这将节省大量时间 (当在资源管理器中进行实际测试时, 您将不得不多次重启资源管理器来释放您的 dll, 这样您才可以更新您的 dll 文件).

安装和注册 Shell 扩展

您可以查看文章 ".Net Shell 扩展 - 上下文菜单" 中的 "安装和注册 Shell 扩展" 部分, 以获取有关如何安装和注册这些扩展的详细信息, 过程是相同的.

有用的资源

Building Preview Handlers: MSDN 上关于 Shell 扩展的页面. 注意, 有很少的一部分是关于提示信息处理程序的.

CodePlex上的SharpShell: SharpShell 项目的所在地-包括文档,讨论以及最新的源代码和发行版. (译者注: 已迁移至 Github)

What's Next?

随着时间的推移, SharpShell 将提供一种使用 .Net 创建所有可用 Shell 扩展的机制. 到目前为止, 上下文菜单扩展, 图标处理程序和信息提示处理程序已完全受支持 - follow CodePlex项目以在添加新功能时保持最新状态.

历史

  • 2014年5月20日: 最新版本

License

本文以及所有相关的源代码和文件均已获得 MIT 许可证许可.

译注

  1. 文章来源: .NET-Shell-Extensions-Shell-Preview-Handlers - CodeProject
  2. 原文作者: Dave Kerr, A softer Developer from UK. Blog: www.dwmkerr.com, Github: dwmkerr@Github
  3. 本文最早发表于 2014年5月20日, 截至翻译时最新更新为 2014年5月21日, 翻译日期为 2020年3月8日. 互联网日新月异, 请注意信息时效.
  4. 文中的代码均测试通过, 文中除了标有 "译者注" 的部分之外均为原文, 译者水平有限, 如有疑惑之处请戳原文或 PM 我, 谢谢茄子.
  5. 纠结了好久, 最终还是决定代码中的英文注释不翻译. 这部分与代码相关较大, 希望能尽量保持原汁原味.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值