使用 C# 的 Windows shell 扩展

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2194572/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-07 00:17:28  来源:igfitidea点击:

Windows shell extension with C#

c#windows-shellshell-extensions

提问by Pete McKinney

I was wanting to write a simple windows shell extension to add to the context menu, and C# is the language I most use these days. Is it a decent choice for a shell extension? Are the interfaces easy to get to with it? Is there additional overhead that causes the menu to be slower to pop up?

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

Any one have good pointers for getting started?

任何人都有很好的入门指南?

采纳答案by GSerg

A Raymond's post: Do not write in-process shell extensions in managed code.

Raymond 的帖子:不要在托管代码中编写进程内 shell 扩展



A recent follow-up: 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?

最近的跟进:既然 .NET Framework 的第 4 版支持进程内并行运行时,现在可以在托管代码中编写 shell 扩展吗?

The bottom line is, no, it is not okay:

底线是,不,这是不行的:

The Guidance for implementing in-process extensionshas been revised, and it continues the recommendation against writing shell extensions and Internet Explorer extensions (and other types of in-process extensions) in managed code, even if you're using version 4 or higher.

实施进程内扩展指南已经过修订,它继续建议不要在托管代码中编写 shell 扩展和 Internet Explorer 扩展(以及其他类型的进程内扩展),即使您使用的是版本 4 或更高版本。

回答by ladenedge

At the risk of looking like a shill, EZShellExtensionsis a wonderful (but non-free) framework for shell extension development in C#. You can write a simple context menu extension with about 20 lines of code and, best of all, never have to mess with COM interfaces. My company uses it (and their namespace extension framework) for a set of extensions currently in use by tens of thousands of customers and, for what it's worth, we've never had a problem with the CLR conflict described above.

冒着看起来像个骗子的风险,EZShellExtensions是一个很棒(但非免费)的 C# 外壳扩展开发框架。您可以使用大约 20 行代码编写一个简单的上下文菜单扩展,最重要的是,您不必与 COM 接口混淆。我的公司将它(以及他们的命名空间扩展框架)用于目前由数以万计客户使用的一组扩展,而且,就其价值而言,我们从未遇到过上述 CLR 冲突问题。

Here's a quick sample to show how easy it is:

这是一个快速示例,展示了它是多么容易:

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

回答by Ehsan Mohammadi

Guidance for Implementing In-Process Extensions

实施进程内扩展的指南

Version Conflicts

版本冲突

A version conflict can arise through a runtime that does not support the loading of multiple runtime versions within a single process. Versions of the CLR prior to version 4.0 fall into this category. If the loading of one version of a runtime precludes the loading of other versions of that same runtime, this can create a conflict if the host application or another in-process extension uses a conflicting version. In the case of a version conflict with another in-process extension, the conflict can be difficult to reproduce because the failure requires the right conflicting extensions and the failure mode depends on the order in which the conflicting extensions are loaded.

Consider an in-process extension written using a version of the CLR prior to version 4.0. Every application on the computer that uses a file Open dialog box could potentially have the dialog's managed code and its attendant CLR dependency loaded into the application's process. The application or extension that is first to load a pre-4.0 version of the CLR into the application's process restricts which versions of the CLR can be used subsequently by that process. If a managed application with an Open dialog box is built on a conflicting version of the CLR, then the extension could fail to run correctly and could cause failures in the application. Conversely, if the extension is the first to load in a process and a conflicting version of managed code tries to launch after that (perhaps a managed application or a running application loads the CLR on demand), the operation fails. To the user, it appears that some features of the application randomly stop working, or the application mysteriously crashes.

Note that versions of the CLR equal to or later than version 4.0 are not generally susceptible to the versioning problem because they are designed to coexist with each other and with most pre-4.0 versions of the CLR (with the exception of version 1.0, which cannot coexist with other versions). However, issues other than version conflicts can arise as discussed in the remainder of this topic.

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

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

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

Performance Issues

性能问题

Performance issues can arise with runtimes that impose a significant performance penalty when they are loaded into a process. The performance penalty can be in the form of memory usage, CPU usage, elapsed time, or even address space consumption. The CLR, JavaScript/ECMAScript, and Java are known to be high-impact runtimes. Since in-process extensions can be loaded into many processes, and are often done so at performance-sensitive moments (such as when preparing a menu to be displayed the user), high-impact runtimes can negatively impact overall responsiveness.

A high-impact runtime that consumes significant resources can cause a failure in the host process or another in-process extension. For example, a high-impact runtime that consumes hundreds of megabytes of address space for its heap can result in the host application being unable to load a large dataset. Furthermore, because in-process extensions can be loaded into multiple processes, high resource consumption in a single extension can quickly multiply into high resource consumption across the entire system.

If a runtime remains loaded or otherwise continues to consume resources even when the extension that uses that runtime has unloaded, then that runtime is not suitable for use in an extension.

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

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

如果运行时保持加载或以其他方式继续消耗资源,即使使用该运行时的扩展已卸载,则该运行时不适合在扩展中使用。

Issues Specific to the .NET Framework

.NET Framework 特有的问题

The following sections discuss examples of issues found with using managed code for extensions. They are not a complete list of all possible issues that you might encounter. The issues discussed here are both reasons that managed code is not supported in extensions and points to consider when you evaluate the use of other runtimes.

  • Re-entrancy
    When the CLR blocks a single-threaded apartment (STA) thread, for example, due to a Monitor.Enter, WaitHandle.WaitOne, or contended lock statement, the CLR, in its standard configuration, enters a nested message loop while it waits. Many extension methods are prohibited from processing messages, and this unpredictable and unexpected reentrancy can result in anomalous behavior which is difficult to reproduce and diagnose.

  • The Multithreaded ApartmentThe CLR creates Runtime Callable Wrappers for Component Object Model (COM) objects. These same Runtime Callable Wrappers are destroyed later by the CLR's finalizer, which is part of the multithreaded apartment (MTA). Moving the proxy from the STA to the MTA requires marshaling, but not all interfaces used by extensions can be marshalled.

  • Non-Deterministic Object Lifetimes
    The CLR has weaker object lifetime guarantees than native code. Many extensions have reference count requirements on objects and interfaces, and the garbage-collection model employed by the CLR cannot fulfill these requirements.

    • If a CLR object obtains a reference to a COM object, the COM object reference held by the Runtime Callable Wrapper is not released until the Runtime Callable Wrapper is garbage-collected. Nondeterministic release behavior can conflict with some interface contracts. For example, the IPersistPropertyBag::Load method requires that no reference to the property bag be retained by the object when the Load method returns.
    • If a CLR object reference is returned to native code, the Runtime Callable Wrapper relinquishes its reference to the CLR object when the Runtime Callable Wrapper's final call to Release is made, but the underlying CLR object is not finalized until it is garbage-collected. Nondeterministic finalization can conflict with some interface contracts. For example, thumbnail handlers are required to release all resources immediately when their reference count drops to zero.

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

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

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

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

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

Acceptable Uses of Managed Code and Other Runtimes

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

It is acceptable to use managed code and other runtimes to implement out-of-process extensions. Examples of out-of-process Shell extensions include the following:

  • Preview handlers
  • Command-line-based actions such as those registered under shell\verb\command subkeys.
  • COM objects implemented in a local server, for Shell extension points that allow out-of-process activation.

Some extensions can be implemented either as in-process or out-of-process extensions. You can implement these extensions as out-of-process extensions if they do not meet these requirements for in-process extensions. The following list shows examples of extensions that can be implemented as either in-process or out-of-process extensions:

  • IExecuteCommand associated with a DelegateExecute entry registered under a shell\verb\command subkey.
  • IDropTarget associated with the CLSID registered under a shell\verb\DropTarget subkey.
  • IExplorerCommandState associated with a CommandStateHandler entry registered under a shell\verb subkey.

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

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

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

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

SharpShell

夏普壳

SharpShell makes it easy to create Windows Shell Extensions using the .NET Framework.

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

The source code is hosted on https://github.com/dwmkerr/sharpshell- you can post questions and feature request here or there. Supported Extensions

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

You can use SharpShell to build any of the extensions below:

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

  • Shell Context Menus
  • Icon Handlers
  • Info Tip Handlers
  • Drop Handlers
  • Preview Handlers
  • Icon Overlay Handlers
  • Thumbnail Hanlders
  • Property Sheet Extensions
  • Shell 上下文菜单
  • 图标处理程序
  • 信息提示处理程序
  • 丢弃处理程序
  • 预览处理程序
  • 图标覆盖处理程序
  • 缩略图处理程序
  • 属性表扩展

Projects that use SharpShell
1. Trello Context Menu
2. REAL Shuffle Player 2.0

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

Article Series at CodeProject

CodeProject 上的文章系列