Extensión de shell de Windows con C #

Quería escribir una extensión de shell de Windows simple para agregar al menú contextual, y C # es el idioma que más uso en estos días. ¿Es una elección decente para una extensión de shell? ¿Las interfaces son fáciles de usar? ¿Hay una sobrecarga adicional que hace que el menú sea más lento para aparecer?

¿Alguien tiene buenos consejos para empezar?

Una publicación de Raymond: no escriba extensiones de shell en proceso en el código administrado .


Un seguimiento reciente: ahora que la versión 4 de .NET Framework admite los tiempos de ejecución paralelos en proceso, ¿está bien ahora escribir extensiones de shell en el código administrado?

La conclusión es, no, no está bien:

La Guía para implementar extensiones en proceso se ha revisado y continúa con la recomendación de escribir extensiones de shell y extensiones de Internet Explorer (y otros tipos de extensiones en proceso) en código administrado, incluso si está utilizando la versión 4 o superior.

A riesgo de parecer un engaño, EZShellExtensions es un marco maravilloso (pero no libre) para el desarrollo de extensiones de shell en C #. Puede escribir una extensión de menú contextual simple con aproximadamente 20 líneas de código y, lo mejor de todo, nunca tener que meterse con las interfaces COM. Mi compañía lo usa (y su marco de extensión de espacio de nombres) para un conjunto de extensiones actualmente en uso por decenas de miles de clientes y, por lo que sea, nunca hemos tenido un problema con el conflicto de CLR descrito anteriormente.

Aquí hay una muestra rápida para mostrar lo fácil que es:

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

Orientación para la implementación de extensiones en proceso

Conflictos de versiones

Un conflicto de versión puede surgir a través de un tiempo de ejecución que no admite la carga de múltiples versiones de tiempo de ejecución dentro de un único proceso. Las versiones de CLR anteriores a la versión 4.0 entran en esta categoría. Si la carga de una versión de un tiempo de ejecución excluye la carga de otras versiones de ese mismo tiempo de ejecución, esto puede crear un conflicto si la aplicación host u otra extensión en proceso usa una versión conflictiva. En el caso de un conflicto de versión con otra extensión en proceso, el conflicto puede ser difícil de reproducir porque la falla requiere las extensiones en conflicto correctas y el modo de falla depende del orden en el que se cargan las extensiones en conflicto.

Considere una extensión en proceso escrita utilizando una versión del CLR anterior a la versión 4.0. Cada aplicación en la computadora que usa un cuadro de diálogo Abrir archivo podría tener el código administrado del diálogo y la dependencia de CLR correspondiente cargados en el proceso de la aplicación. La aplicación o extensión que primero carga una versión anterior a la 4.0 del CLR en el proceso de la aplicación restringe qué versiones de CLR pueden ser utilizadas posteriormente por ese proceso. Si una aplicación administrada con un cuadro de diálogo Abrir se basa en una versión conflictiva del CLR, entonces la extensión podría no ejecutarse correctamente y podría causar fallas en la aplicación. Por el contrario, si la extensión es la primera en cargarse en un proceso y una versión conflictiva del código administrado intenta lanzarse después de eso (tal vez una aplicación administrada o una aplicación en ejecución carga el CLR a petición), la operación falla. Para el usuario, parece que algunas características de la aplicación dejan de funcionar aleatoriamente, o la aplicación se bloquea misteriosamente.

Tenga en cuenta que las versiones de CLR iguales o posteriores a la versión 4.0 generalmente no son susceptibles al problema de las versiones porque están diseñadas para coexistir entre ellas y con la mayoría de las versiones anteriores a la 4.0 del CLR (con la excepción de la versión 1.0, que no puede coexisten con otras versiones). Sin embargo, pueden surgir problemas distintos a los conflictos de versión como se comenta en el rest de este tema.

Problemas de desempeño

Pueden surgir problemas de rendimiento con los tiempos de ejecución que imponen una penalización de rendimiento significativa cuando se cargan en un proceso. La penalización de rendimiento puede ser en forma de uso de memoria, uso de CPU, tiempo transcurrido o incluso consumo de espacio de direcciones. Se sabe que CLR, JavaScript / ECMAScript y Java son entornos de ejecución de alto impacto. Como las extensiones en proceso se pueden cargar en muchos procesos, y con frecuencia se realizan en momentos sensibles al rendimiento (como cuando se prepara un menú para mostrar al usuario), los tiempos de ejecución de alto impacto pueden afectar negativamente la capacidad de respuesta general.

Un tiempo de ejecución de alto impacto que consume recursos significativos puede causar una falla en el proceso de host u otra extensión en proceso. Por ejemplo, un tiempo de ejecución de alto impacto que consume cientos de megabytes de espacio de direcciones para su almacenamiento dynamic puede provocar que la aplicación de host no pueda cargar un conjunto de datos grande. Además, debido a que las extensiones en proceso se pueden cargar en múltiples procesos, el alto consumo de recursos en una sola extensión puede multiplicarse rápidamente en un alto consumo de recursos en todo el sistema.

Si un tiempo de ejecución permanece cargado o si continúa consumiendo recursos aun cuando la extensión que usa ese tiempo de ejecución se haya descargado, entonces ese tiempo de ejecución no es adecuado para su uso en una extensión.

Problemas específicos del .NET Framework

Las siguientes secciones discuten ejemplos de problemas encontrados con el uso del código administrado para extensiones. No son una lista completa de todos los problemas posibles que pueda encontrar. Los problemas que se tratan aquí son dos razones por las que el código administrado no se admite en extensiones y puntos a considerar cuando evalúa el uso de otros tiempos de ejecución.

  • Reentrada
    Cuando el CLR bloquea un subproceso de apartamento de subproceso único (STA), por ejemplo, debido a un Monitor.Enter, WaitHandle.WaitOne o una instrucción de locking contenida, el CLR, en su configuración estándar, ingresa un bucle de mensaje nested mientras espera. Muchos métodos de extensión tienen prohibido procesar mensajes, y esta reentrada imprevista e inesperada puede dar como resultado un comportamiento anómalo que es difícil de reproducir y diagnosticar.

  • El apartamento multiproceso El CLR crea Contenedores invocables en tiempo de ejecución para los objetos del Modelo de objetos componentes (COM). Estas mismas envolturas invocables en tiempo de ejecución son destruidas más tarde por el finalizador del CLR, que es parte del apartamento multiproceso (MTA). Mover el proxy de la STA al MTA requiere una clasificación, pero no todas las interfaces utilizadas por las extensiones se pueden ordenar.

  • Tiempo de vida de los objetos no determinísticos
    El CLR tiene garantías de vida del objeto más débiles que el código nativo. Muchas extensiones tienen requisitos de recuento de referencias en objetos e interfaces, y el modelo de recolección de basura empleado por el CLR no puede cumplir estos requisitos.

    • Si un objeto CLR obtiene una referencia a un objeto COM, la referencia del objeto COM mantenida por el Contenedor invocable en tiempo de ejecución no se libera hasta que el Contenedor invocable en tiempo de ejecución se recoja basura. El comportamiento de liberación no determinista puede entrar en conflicto con algunos contratos de interfaz. Por ejemplo, el método IPersistPropertyBag :: Load requiere que el objeto no conserve ninguna referencia a la bolsa de propiedades cuando se devuelve el método Load.
    • Si se devuelve una referencia de objeto CLR al código nativo, el Contenedor invocable en tiempo de ejecución renuncia a su referencia al objeto CLR cuando se realiza la llamada final al Liberador del tiempo de ejecución invocable, pero el objeto CLR subyacente no se finaliza hasta que se recolecta basura. La finalización no determinista puede entrar en conflicto con algunos contratos de interfaz. Por ejemplo, los manejadores de miniaturas deben liberar todos los recursos inmediatamente cuando su recuento de referencia caiga a cero.

Usos aceptables del código administrado y otros tiempos de ejecución

Es aceptable usar el código administrado y otros tiempos de ejecución para implementar extensiones fuera del proceso. Entre los ejemplos de extensiones de shell fuera de proceso se incluyen los siguientes:

  • Controladores de vista previa
  • Acciones basadas en línea de comandos, como las registradas bajo subclaves shell \ verb \ command.
  • Objetos COM implementados en un servidor local, para puntos de extensión de Shell que permiten la activación fuera de proceso.

Algunas extensiones se pueden implementar como extensiones en proceso o fuera de proceso. Puede implementar estas extensiones como extensiones fuera de proceso si no cumplen estos requisitos para las extensiones en proceso. La siguiente lista muestra ejemplos de extensiones que se pueden implementar como extensiones en proceso o fuera de proceso:

  • IExecuteCommand asociado a una entrada DelegateExecute registrada bajo una subclave shell \ verb \ command.
  • IDropTarget asociado con el CLSID registrado bajo una subclave shell \ verb \ DropTarget.
  • IExplorerCommandState asociado con una entrada CommandStateHandler registrada bajo una subclave shell \ verb.

SharpShell

SharpShell facilita la creación de extensiones de shell de Windows utilizando .NET Framework.

El código fuente está alojado en https://github.com/dwmkerr/sharpshell . Puede publicar preguntas y solicitarla aquí o allí. Extensiones admitidas

Puede usar SharpShell para comstackr cualquiera de las siguientes extensiones:

  • Menús de contexto de Shell
  • Manejadores de icons
  • Controladores de información
  • Manejadores de caída
  • Controladores de vista previa
  • Controladores de superposición de icons
  • Thumbnail Hanlders
  • Extensiones de la hoja de propiedades

Proyectos que usan SharpShell
1. Menú contextual de Trello
2. REAL Shuffle Player 2.0

Serie de artículos en CodeProject

  • .NET Shell Extensions – Menús contextuales de shell
  • Extensiones .NET Shell – Controladores de icons Shell
  • .NET Shell Extensions – Controladores de información de Shell
  • .NET Shell Extensions – Shell Drop Handlers
  • Extensiones .NET Shell – Controladores de vista previa de Shell
  • .NET Shell Extensions – Controladores de superposición de icons de Shell
  • Extensiones .NET Shell – Controladores de miniaturas de Shell
  • .NET Shell Extensions – Shell Property Sheets