Estensione della shell di Windows con C #

Volevo scrivere una semplice estensione della shell di windows da aggiungere al menu di scelta rapida, e C # è la lingua che utilizzo maggiormente in questi giorni. È una scelta decente per un’estensione della shell? Le interfacce sono facili da raggiungere? C’è un overhead aggiuntivo che fa sì che il menu sia più lento a comparire?

Qualcuno ha buoni consigli per iniziare?

    Un post di Raymond: non scrivere estensioni della shell in-process nel codice gestito .


    Un recente follow-up: ora che la versione 4 di .NET Framework supporta runtime in-process side-by-side, è ora ansible scrivere estensioni della shell nel codice gestito?

    La linea di fondo è, no, non va bene:

    La Guida per l’implementazione delle estensioni in-process è stata rivista e continua la raccomandazione di scrivere estensioni della shell ed estensioni di Internet Explorer (e altri tipi di estensioni in-process) nel codice gestito, anche se si utilizza la versione 4 o successiva.

    A rischio di apparire come uno shill, EZShellExtensions è un meraviglioso (ma non gratuito) framework per lo sviluppo di estensioni di shell in C #. È ansible scrivere un’estensione semplice del menu di contesto con circa 20 righe di codice e, soprattutto, non bisogna mai scherzare con le interfacce COM. La mia azienda lo usa (e il suo framework di estensione dello spazio dei nomi) per un insieme di estensioni attualmente in uso da decine di migliaia di clienti e, per quello che vale, non abbiamo mai avuto un problema con il conflitto CLR descritto sopra.

    Ecco un rapido esempio per dimostrare quanto sia facile:

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

    Guida per l’implementazione di estensioni in-process

    Conflitti di versione

    Un conflitto di versione può derivare da un runtime che non supporta il caricamento di più versioni di runtime all’interno di un singolo processo. Le versioni del CLR precedenti alla 4.0 rientrano in questa categoria. Se il caricamento di una versione di un runtime preclude il caricamento di altre versioni dello stesso runtime, questo può creare un conflitto se l’applicazione host o un’altra estensione in elaborazione utilizza una versione in conflitto. Nel caso di una versione in conflitto con un’altra estensione in elaborazione, il conflitto può essere difficile da riprodurre poiché l’errore richiede le estensioni in conflitto corrette e la modalità di errore dipende dall’ordine in cui vengono caricate le estensioni in conflitto.

    Considerare un’estensione in fase di elaborazione scritta utilizzando una versione del CLR precedente alla 4.0. Ogni applicazione sul computer che utilizza una finestra di dialogo Apri file potrebbe potenzialmente caricare il codice gestito della finestra di dialogo e la dipendenza CLR corrispondente nel processo dell’applicazione. L’applicazione o l’estensione che per prima cosa carica una versione pre-4.0 del CLR nel processo dell’applicazione limita le versioni del CLR che possono essere successivamente utilizzate da quel processo. Se un’applicazione gestita con una finestra di dialogo Apri è costruita su una versione in conflitto del CLR, l’estensione potrebbe non riuscire a funzionare correttamente e potrebbe causare errori nell’applicazione. Viceversa, se l’estensione è la prima a caricare in un processo e successivamente viene avviata una versione in conflitto del codice gestito (forse un’applicazione gestita o un’applicazione in esecuzione carica il CLR su richiesta), l’operazione non riesce. Per l’utente, sembra che alcune funzionalità dell’applicazione smettano in modo casuale o l’applicazione si blocchi in modo misterioso.

    Si noti che le versioni del CLR uguali o successive alla versione 4.0 non sono generalmente suscettibili al problema di versione perché sono progettate per coesistere tra loro e con la maggior parte delle versioni precedenti al 4.0 CLR (ad eccezione della versione 1.0, che non può coesistono con altre versioni). Tuttavia, problemi diversi dai conflitti di versione possono sorgere come discusso nel resto di questo argomento.

    Problemi di prestazione

    I problemi di prestazioni possono sorgere con tempi di esecuzione che impongono una significativa riduzione delle prestazioni quando vengono caricati in un processo. La penalizzazione delle prestazioni può essere sotto forma di utilizzo della memoria, utilizzo della CPU, tempo trascorso o persino consumo di spazio dell’indirizzo. Il CLR, JavaScript / ECMAScript e Java sono noti per essere runtime ad alto impatto. Poiché le estensioni in-process possono essere caricate in molti processi e spesso vengono eseguite in momentjs sensibili alle prestazioni (ad esempio durante la preparazione di un menu per la visualizzazione dell’utente), i runtime a impatto elevato possono avere un impatto negativo sulla reattività generale.

    Un runtime ad alto impatto che consuma risorse significative può causare un errore nel processo host o in un’altra estensione in elaborazione. Ad esempio, un runtime ad alto impatto che consuma centinaia di megabyte di spazio degli indirizzi per il relativo heap può impedire all’applicazione host di caricare un set di dati di grandi dimensioni. Inoltre, poiché le estensioni in-process possono essere caricate in più processi, l’elevato consumo di risorse in una singola estensione può rapidamente moltiplicarsi in un elevato consumo di risorse nell’intero sistema.

    Se un runtime rimane caricato o continua a consumare risorse anche quando l’estensione che utilizza quel runtime ha scaricato, quel runtime non è adatto per l’uso in un’estensione.

    Problemi specifici di .NET Framework

    Le sezioni seguenti illustrano alcuni esempi di problemi riscontrati nell’utilizzo del codice gestito per le estensioni. Non sono un elenco completo di tutti i possibili problemi che potresti incontrare. I problemi discussi qui sono entrambi i motivi per cui il codice gestito non è supportato nelle estensioni e punti da considerare quando si valuta l’utilizzo di altri runtime.

    • Re-entrancy
      Quando il CLR blocca un thread di apartment a thread singolo (STA), ad esempio, a causa di un’istruzione di lock Monitor.Enter, WaitHandle.WaitOne o contesa, il CLR, nella sua configurazione standard, entra in un ciclo di messaggi nidificati mentre attende. A molti metodi di estensione è vietato elaborare i messaggi e questa imprevedibile e inattesa ripresa può portare a comportamenti anomali difficili da riprodurre e diagnosticare.

    • The Multithreaded Apartment Il CLR crea Runtime Callable Wrappers per oggetti COM (Component Object Model). Questi stessi Runtime Callable Wrapper vengono distrutti in seguito dal finalizzatore CLR, che fa parte dell’appartamento multithreaded (MTA). Lo spostamento del proxy dallo STA sull’MTA richiede il marshalling, ma non tutte le interfacce utilizzate dalle estensioni possono essere sottoposte a marshalling.

    • Cicli di vita non deterministica
      Il CLR ha garanzie di durata dell’object più deboli rispetto al codice nativo. Molte estensioni hanno requisiti di conteggio di riferimento su oggetti e interfacce e il modello di garbage collection impiegato dal CLR non può soddisfare questi requisiti.

      • Se un object CLR ottiene un riferimento a un object COM, il riferimento all’object COM contenuto dal Runtime Callable Wrapper non viene rilasciato finché il Runtime Callable Wrapper non viene raccolto. Il comportamento di rilascio non deterministico può entrare in conflitto con alcuni contratti di interfaccia. Ad esempio, il metodo IPersistPropertyBag :: Load richiede che nessun riferimento al bean di proprietà venga mantenuto dall’object quando viene restituito il metodo Load.
      • Se un riferimento a un object CLR viene restituito al codice nativo, il Runtime Callable Wrapper rinvierà il suo riferimento all’object CLR quando viene eseguita la chiamata finale al rilascio di Runtime Callable Wrapper, ma l’object CLR sottostante non viene finalizzato fino a quando non viene raccolto. La finalizzazione non deterministica può entrare in conflitto con alcuni contratti di interfaccia. Ad esempio, i gestori di miniature sono tenuti a rilasciare tutte le risorse immediatamente quando il loro conteggio dei riferimenti scende a zero.

    Usi accettabili di codice gestito e altri tempi di esecuzione

    È accettabile utilizzare il codice gestito e altri runtime per implementare estensioni out-of-process. Esempi di estensioni della shell out-of-process includono quanto segue:

    • Gestori di anteprima
    • Azioni basate sulla riga di comando come quelle registrate nelle sottochiavi shell \ verb \ command.
    • Oggetti COM implementati in un server locale, per i punti di estensione Shell che consentono l’triggerszione out-of-process.

    Alcune estensioni possono essere implementate come estensioni in-process o out-of-process. È ansible implementare queste estensioni come estensioni out-of-process se non soddisfano questi requisiti per le estensioni in-process. Il seguente elenco mostra esempi di estensioni che possono essere implementate come estensioni in-process o out-of-process:

    • IExecuteCommand associato a una voce DelegateExecute registrata sotto una sottochiave shell \ verb \ command.
    • IDropTarget associato al CLSID registrato sotto una sottochiave shell \ verb \ DropTarget.
    • IExplorerCommandState associato a una voce CommandStateHandler registrata in una sottochiave shell \ verb.

    SharpShell

    SharpShell semplifica la creazione di estensioni della shell di Windows tramite .NET Framework.

    Il codice sorgente è ospitato su https://github.com/dwmkerr/sharpshell – puoi postare domande e richieste di funzioni qui o là. Estensioni supportate

    È ansible utilizzare SharpShell per compilare le seguenti estensioni:

    • Menu di scelta rapida della shell
    • Gestori di icone
    • Gestori di punte di informazioni
    • Drop Handlers
    • Gestori di anteprima
    • Icon Overlay Handlers
    • Antichi anteprime
    • Estensioni foglio proprietà

    Progetti che utilizzano SharpShell
    1. Menu contestuale Trello
    2. REAL Shuffle Player 2.0

    Serie di articoli su CodeProject

    • Estensioni della shell .NET – Menu di scelta rapida della shell
    • Estensioni della shell .NET – Gestore di icone di shell
    • Estensioni della shell .NET – Gestori di punta di informazioni sulla shell
    • Estensioni della shell .NET – Gestore di shell drop
    • Estensioni della shell .NET – Gestori di anteprima della shell
    • Estensioni della shell .NET – Gestore di overlay di icone di shell
    • Estensioni della shell .NET – Gestori di shell di anteprima
    • Estensioni della shell .NET – Fogli della shell