【问题标题】:Windows shell extension with C#使用 C# 的 Windows shell 扩展
【发布时间】:2011-01-12 18:25:25
【问题描述】:

我想编写一个简单的 Windows shell 扩展来添加到上下文菜单中,而 C# 是我这些天最常用的语言。它是 shell 扩展的一个不错的选择吗?接口是否易于使用?是否有额外的开销导致菜单弹出速度变慢?

谁有好的入门指南?

【问题讨论】:

标签: c# windows-shell shell-extensions


【解决方案1】:

Raymond 的帖子:Do not write in-process shell extensions in managed code


近期跟进:Now that version 4 of the .NET Framework supports in-process side-by-side runtimes, is it now okay to write shell extensions in managed code?

底线是,不,这不行:

Guidance for implementing in-process extensions 已修订,它继续建议不要在托管代码中编写 shell 扩展和 Internet Explorer 扩展(以及其他类型的进程内扩展),即使您使用的是版本 4 或更高版本。

【讨论】:

  • 可以做到,但这是个坏主意。我知道 MS 开发团队用 c# 编写了 shell ext,他们最终用 c++ native 重新编码
  • 最新的 .Net 4.0 运行时支持在进程中并行加载 .Net 4.0 运行时(以及所有未来的运行时)与早期的 .Net 运行时。请参阅msdn.microsoft.com/en-us/magazine/ee819091.aspx 的以下摘录“由于能够将多个运行时与任何其他运行时一起处理,我们现在可以为编写托管 shell 扩展提供一般支持——即使是那些在机器上与任意应用程序一起在进程内运行的扩展。”
  • 作者是否暗示可以放弃对在 4.0 之前的 FW 中编写的应用程序的支持?
  • @logicnp (最后)有一个来自 Raymond Chen 的follow-up
  • 没有规定禁止在 .NET 中编写进程外扩展,但前提是您可以弄清楚如何做到这一点并遵循 refcount 协定。
【解决方案2】:

Guidance for Implementing In-Process Extensions

版本冲突

不支持在单个进程中加载​​多个运行时版本的运行时可能会出现版本冲突。 CLR 4.0 之前的版本属于这一类。如果一个运行时版本的加载排除了同一运行时其他版本的加载,如果宿主应用程序或另一个进程内扩展使用冲突版本,这可能会产生冲突。如果版本与另一个进程内扩展发生冲突,则冲突可能难以重现,因为失败需要正确的冲突扩展,而失败模式取决于加载冲突扩展的顺序。

考虑使用 4.0 之前的 CLR 版本编写的进程内扩展。计算机上使用文件打开对话框的每个应用程序都可能将对话框的托管代码及其附带的 CLR 依赖项加载到应用程序的进程中。首先将 CLR 的 4.0 之前版本加载到应用程序进程中的应用程序或扩展限制了该进程随后可以使用的 CLR 版本。如果带有“打开”对话框的托管应用程序是基于 CLR 的冲突版本构建的,则扩展可能无法正确运行,并可能导致应用程序出现故障。相反,如果扩展是第一个加载到进程中的,并且托管代码的冲突版本在此之后尝试启动(可能托管应用程序或正在运行的应用程序按需加载 CLR),则操作将失败。在用户看来,应用程序的某些功能似乎随机停止工作,或者应用程序神秘地崩溃了。

请注意,等于或高于 4.0 版的 CLR 版本通常不会受到版本控制问题的影响,因为它们被设计为可以与大多数 4.0 之前的 CLR 版本共存(1.0 版除外) ,不能与其他版本共存)。但是,如本主题的其余部分所述,可能会出现版本冲突以外的问题。

性能问题

运行时可能会出现性能问题,当它们被加载到进程中时会造成显着的性能损失。性能损失的形式可能是内存使用、CPU 使用、运行时间,甚至是地址空间消耗。 CLR、JavaScript/ECMAScript 和 Java 是众所周知的高影响运行时。由于进程内扩展可以加载到许多进程中,并且通常在性能敏感的时刻(例如在准备向用户显示的菜单时)加载,因此高影响的运行时会对整体响应能力产生负面影响。

消耗大量资源的高影响运行时可能会导致主机进程或另一个进程内扩展出现故障。例如,一个高影响的运行时消耗数百兆字节的地址空间用于其堆可能会导致主机应用程序无法加载大型数据集。此外,由于进程内扩展可以加载到多个进程中,单个扩展中的高资源消耗会迅速成倍增加整个系统的高资源消耗。

如果运行时保持加载状态,或者即使在使用该运行时的扩展已卸载时仍继续消耗资源,则该运行时不适合在扩展中使用。

.NET Framework 特有的问题

以下部分讨论了使用托管代码进行扩展时发现的问题示例。它们并不是您可能遇到的所有可能问题的完整列表。这里讨论的问题既是扩展中不支持托管代码的原因,也是评估其他运行时使用时需要考虑的要点。

  • 重入
    当 CLR 阻塞单线程单元 (STA) 线程时,例如,由于 Monitor.Enter、WaitHandle.WaitOne 或竞争锁定语句,CLR 在其标准配置中,在等待时进入嵌套消息循环。许多扩展方法被禁止处理消息,而这种不可预测和意外的重入可能会导致难以重现和诊断的异常行为。

  • 多线程单元 CLR 为组件对象模型 (COM) 对象创建运行时可调用包装器。这些相同的运行时可调用包装器稍后会被 CLR 的终结器销毁,该终结器是多线程单元 (MTA) 的一部分。将代理从 STA 移动到 MTA 需要编组,但并非扩展使用的所有接口都可以编组。

  • 非确定性对象生命周期
    与本机代码相比,CLR 具有较弱的对象生命周期保证。许多扩展对对象和接口都有引用计数要求,而 CLR 采用的垃圾收集模型无法满足这些要求。

    • 如果 CLR 对象获得对 COM 对象的引用,则在对运行时可调用包装器进行垃圾回收之前,不会释放运行时可调用包装器持有的 COM 对象引用。非确定性发布行为可能与某些接口契约发生冲突。例如,IPersistPropertyBag::Load 方法要求在 Load 方法返回时对象不保留对属性包的引用。
    • 如果 CLR 对象引用返回到本机代码,则在 Runtime Callable Wrapper 对 Release 的最终调用时,Runtime Callable Wrapper 放弃其对 CLR 对象的引用,但底层 CLR 对象直到它被垃圾时才最终确定 -集。非确定性最终确定可能与某些接口契约发生冲突。例如,缩略图处理程序需要在引用计数降至零时立即释放所有资源。

托管代码和其他运行时的可接受用途

使用托管代码和其他运行时来实现进程外扩展是可以接受的。进程外 Shell 扩展的示例包括:

  • 预览处理程序
  • 基于命令行的操作,例如在 shell\verb\command 子键下注册的操作。
  • 在本地服务器中实现的 COM 对象,用于允许进程外激活的 Shell 扩展点。

一些扩展可以作为进程内或进程外扩展来实现。如果这些扩展不满足进程内扩展的这些要求,您可以将它们实现为进程外扩展。以下列表显示了可以作为进程内或进程外扩展实现的扩展示例:

  • IExecuteCommand 与在 shell\verb\command 子项下注册的 DelegateExecute 条目相关联。
  • IDropTarget 与在 shell\verb\DropTarget 子项下注册的 CLSID 关联。
  • IExplorerCommandState 与在 shell\verb 子项下注册的 CommandStateHandler 条目相关联。

SharpShell

SharpShell 使使用 .NET 框架创建 Windows Shell 扩展变得容易。

源代码托管在https://github.com/dwmkerr/sharpshell - 您可以在这里或那里发布问题和功能请求。 支持的扩展

您可以使用 SharpShell 构建以下任何扩展:

  • Shell 上下文菜单
  • 图标处理程序
  • 信息提示处理程序
  • 丢弃处理程序
  • 预览处理程序
  • 图标覆盖处理程序
  • 缩略图处理程序
  • 属性表扩展

使用 SharpShell 的项目
1.Trello Context Menu
2.REAL Shuffle Player 2.0

CodeProject 上的文章系列

【讨论】:

    【解决方案3】:

    EZShellExtensions 是一个出色的(但非免费的)C# 中 shell 扩展开发框架,冒着看起来像个骗子的风险。你可以用大约 20 行代码编写一个简单的上下文菜单扩展,而且最重要的是,永远不必弄乱 COM 接口。我的公司将它(和他们的命名空间扩展框架)用于目前被成千上万的客户使用的一组扩展,而且,就其价值而言,我们从未遇到过上述 CLR 冲突的问题。

    这里有一个简单的示例来展示它是多么容易:

    [Guid("00000000-0000-0000-0000-000000000000"), ComVisible(true)]
    [TargetExtension(".txt", true)]
    public class SampleExtension : ContextMenuExtension
    {
       protected override void OnGetMenuItems(GetMenuitemsEventArgs e)
       {
          e.Menu.AddItem("Sample Extension", "sampleverb", "Status/help text");
       }
    
       protected override bool OnExecuteMenuItem(ExecuteItemEventArgs e)
       {
          if (e.MenuItem.Verb == "sampleverb")
             ; // logic
          return true;
       }
    
       [ComRegisterFunction]
       public static void Register(Type t)
       {
          ContextMenuExtension.RegisterExtension(typeof(SampleExtension));
       }
    
       [ComUnregisterFunction]
       public static void UnRegister(Type t)
       {
          ContextMenuExtension.UnRegisterExtension(typeof(SampleExtension));
       }
    }
    

    【讨论】:

      猜你喜欢
      • 2014-12-20
      • 2011-08-08
      • 2012-07-20
      • 1970-01-01
      • 2016-05-13
      • 1970-01-01
      • 2011-08-27
      • 2011-11-29
      • 2012-11-24
      相关资源
      最近更新 更多