Windows shell extension with C#
Asked Answered
T

3

42

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?

Any one have good pointers for getting started?

Triable answered 3/2, 2010 at 18:54 Comment(4)
Times change, and this question's answer is no longer valid. With 4.0, you can now write managed shell extensions.Piscatelli
You can write shell extensions with .NET 4.0, but it's not officially supported, yetUlcer
don't understand why this is closed, writing shell extensions is a very common programming task on Windows and I consider the question asked very legitimate. See also: codeproject.com/KB/shell/columnhandler.aspx (on writing a column handler in C#)Krugersdorp
@Will Is it supported to write Windows Shell Extension using .NET 4 today? Microsoft has not fully tested all of the scenarios involved with managed shell extensions and has not determined whether it will support managed shell extensions for the long term. Therefore, Microsoft will not support managed shell extensions and recommends against writing them.Krill
P
25

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


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?

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

The Guidance for implementing in-process extensions has 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.

Perrine answered 3/2, 2010 at 19:1 Comment(5)
it can be done but its a bad idea. I know MS dev team that wrote shell ext in c# and they ended up recoding in c++ nativeKt
The latest .Net 4.0 runtime supports in process side-by-side loading of the .Net 4.0 runtime (and ALL future runtimes) with earlier .Net runtimes. See following excerpt from msdn.microsoft.com/en-us/magazine/ee819091.aspx "With the ability to have multiple runtimes in process with any other runtime, we can now offer general support for writing managed shell extensions—even those that run in-process with arbitrary applications on the machine."Grizzled
Are the authors implying that it's OK to drop support for apps written in FWs previous to 4.0?Perrine
@Grizzled There's (finally) a follow-up from Raymond Chen on that one.Perrine
There's no rule against writing out-of-process extensions in .NET, assuming you can figure out how to do that and follow the refcount contracts, though.Etana
E
18

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.

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.

Issues Specific to the .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 Apartment The 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.

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.

SharpShell

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

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

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

  • Shell Context Menus
  • Icon Handlers
  • Info Tip Handlers
  • Drop Handlers
  • Preview Handlers
  • Icon Overlay Handlers
  • Thumbnail Hanlders
  • Property Sheet Extensions

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

Article Series at CodeProject

Expurgatory answered 9/2, 2014 at 2:30 Comment(0)
K
16

At the risk of looking like a shill, EZShellExtensions is 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.

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));
   }
}
Knowhow answered 3/2, 2010 at 20:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.