Linee guida per l'implementazione di estensioni In-Process
Le estensioni in-process vengono caricate in tutti i processi che li attivano. Ad esempio, un'estensione dello spazio dei nomi shell può essere caricata in qualsiasi processo che accede allo spazio dei nomi shell direttamente o indirettamente. Lo spazio dei nomi Shell viene usato da molte operazioni della shell, ad esempio la visualizzazione di una finestra di dialogo di file comune, l'avvio di un documento tramite l'applicazione associata o il recupero dell'icona utilizzata per rappresentare un file. Poiché le estensioni in-process possono essere caricate in processi arbitrari, è necessario prestare attenzione a non influire negativamente sull'applicazione host o su altre estensioni in-process.
Un runtime di particolare nota è il CLR (Common Language Runtime), noto anche come codice gestito o .NET Framework. Microsoft sconsiglia di scrivere estensioni in-process gestite per Windows Explorer o Windows Internet Explorer e non le considera uno scenario supportato.
In questo argomento vengono illustrati i fattori da considerare quando si determina se un runtime diverso da CLR è adatto per l'uso da parte delle estensioni in-process. Esempi di altri runtime includono Java, Visual Basic, JavaScript/ECMAScript, Delphi e la libreria di runtime C/C++. Questo argomento fornisce anche alcuni motivi per cui il codice gestito non è supportato nelle estensioni in-process.
Conflitti di versione
Un conflitto di versione può verificarsi tramite un runtime che non supporta il caricamento di più versioni di runtime all'interno di un singolo processo. Le versioni di CLR precedenti alla versione 4.0 rientrano in questa categoria. Se il caricamento di una versione di un runtime impedisce il caricamento di altre versioni dello stesso runtime, può creare un conflitto se l'applicazione host o un'altra estensione in-process usa una versione in conflitto. Nel caso di un conflitto di versione con un'altra estensione in-process, il conflitto può essere difficile da riprodurre perché l'errore richiede le estensioni in conflitto corrette e la modalità di errore dipende dall'ordine in cui vengono caricate le estensioni in conflitto.
Si consideri un'estensione in-process scritta usando una versione di CLR precedente alla versione 4.0. Ogni applicazione nel computer che usa un file finestra di dialogo Apri potrebbe avere il codice gestito del dialogo e la relativa dipendenza CLR caricata nel processo dell'applicazione. L'applicazione o l'estensione che per primo carica una versione del CLR precedente alla 4.0 nel processo dell'applicazione limita quali versioni del CLR possano essere usate successivamente da tale processo. Se un'applicazione gestita con un finestra di dialogo Apri è basata su una versione in conflitto di CLR, l'estensione potrebbe non essere eseguita correttamente e potrebbe causare errori nell'applicazione. Viceversa, se l'estensione è la prima a caricarsi in un processo e una versione in conflitto del codice gestito tenta di avviarsi dopo tale operazione (ad esempio un'applicazione gestita o un'applicazione in esecuzione carica CLR su richiesta), l'operazione non riesce. All'utente sembra che alcune funzionalità dell'applicazione interrompono in modo casuale il funzionamento o l'applicazione si arresta misteriosamente in modo anomalo.
Si noti che le versioni di CLR uguali o successive alla versione 4.0 non sono generalmente soggette al problema di controllo delle versioni perché sono progettate per coesistere tra loro e con la maggior parte delle versioni precedenti alla 4.0 di CLR (ad eccezione della versione 1.0, che non può coesistere con altre versioni). Tuttavia, i problemi diversi dai conflitti di versione possono verificarsi come descritto nella parte restante di questo argomento.
Problemi di prestazioni
I problemi di prestazioni possono verificarsi con i runtime che impongono un significativo calo delle prestazioni quando vengono caricati in un processo. La penalità sulle prestazioni può essere sotto forma di utilizzo della memoria, utilizzo della CPU, tempo trascorso o persino consumo dello spazio degli indirizzi. CLR, JavaScript/ECMAScript e Java sono noti come runtime ad alto impatto. Poiché le estensioni in-process possono essere caricate in molti processi e vengono spesso eseguite in momenti sensibili alle prestazioni (ad esempio quando si prepara un menu da visualizzare), i runtime ad impatto elevato possono influire negativamente sulla velocità di risposta complessiva.
Un ambiente di esecuzione ad alto impatto che utilizza risorse significative può causare un guasto nel processo host o in un'altra estensione in-process. Ad esempio, un runtime ad alto impatto che utilizza centinaia di megabyte di spazio indirizzi per l'heap può comportare l'impossibilità di caricare un set di dati di grandi dimensioni nell'applicazione host. Inoltre, poiché le estensioni in-process possono essere caricate in più processi, un consumo elevato di risorse in una singola estensione può moltiplicarsi rapidamente in un utilizzo elevato delle risorse nell'intero sistema.
Se un runtime rimane caricato o continua a usare le risorse anche quando l'estensione che usa tale runtime è stata scaricata, tale runtime non è adatto per l'uso in un'estensione.
Problemi specifici di .NET Framework
Le sezioni seguenti illustrano esempi di problemi riscontrati con l'uso del codice gestito per le estensioni. Non sono un elenco completo di tutti i possibili problemi che potrebbero verificarsi. I problemi illustrati qui sono sia le ragioni per cui il codice gestito non è supportato nelle estensioni sia i punti da considerare quando si valuta l'uso di altri runtime.
Rientranza
Quando il CLR blocca un thread apartment a thread singolo (STA), ad esempio a causa di Monitor.Enter, WaitHandle.WaitOne o dell'istruzione lock conteso, il CLR, nella sua configurazione standard, entra in un ciclo di messaggi annidato mentre attende. Molti metodi di estensione non possono elaborare messaggi, e questa reentrancy imprevedibile può causare un comportamento anomalo difficile da riprodurre e diagnosticare.
Il Multithreaded Apartment
Il CLR crea runtime callable wrappers per gli oggetti COM (Component Object Model). Questi stessi wrapper chiamabili di runtime vengono eliminati in un secondo momento dal finalizzatore CLR, che fa parte dell'apartment multithreading (MTA). Lo spostamento del proxy da STA a MTA richiede il marshaling, ma non tutte le interfacce utilizzate dalle estensioni possono essere marshalled.
Durata degli oggetti non deterministici
CLR ha garanzie di durata dell'oggetto più deboli rispetto al codice nativo. Molte estensioni hanno requisiti di conteggio dei riferimenti per oggetti e interfacce e il modello di Garbage Collection usato da CLR non può soddisfare questi requisiti.
- Se un oggetto CLR ottiene un riferimento a un oggetto COM, il riferimento all'oggetto COM mantenuto dal Runtime Callable Wrapper non viene rilasciato finché il Runtime Callable Wrapper non viene sottoposto a Garbage Collection. Il comportamento di rilascio non deterministico può essere in conflitto con alcuni contratti di interfaccia. Ad esempio, il metodo IPersistPropertyBag::Load richiede che nessun riferimento al contenitore delle proprietà venga conservato dall'oggetto quando restituisce il metodo Load.
- Se viene restituito un riferimento a un oggetto CLR al codice nativo, il Runtime Callable Wrapper rinuncia al riferimento all'oggetto CLR quando viene eseguita la chiamata finale del Runtime Callable Wrapper a Release, ma l'oggetto CLR sottostante non viene finalizzato fino a quando non viene sottoposto a Garbage Collection. La finalizzazione non deterministica può essere in conflitto con alcuni contratti di interfaccia. Ad esempio, i gestori di anteprima devono rilasciare immediatamente tutte le risorse quando il conteggio dei riferimenti scende a zero.
Usi accettabili di codice gestito e altri runtime
È accettabile usare codice gestito e altri runtime per implementare estensioni out-of-process. Alcuni esempi di estensioni shell fuori processo sono:
- Gestori di anteprima
- Azioni basate sulla riga di comando, ad esempio quelle registrate nella shell \verbo\comando sottochiavi.
- Oggetti COM implementati su un server locale, per punti di estensione della Shell che consentono l'attivazione fuori dal processo.
Alcune estensioni possono essere implementate come estensioni in-process o out-of-process. È possibile implementare queste estensioni come estensioni out-of-process se non soddisfano questi requisiti per le estensioni in-process. L'elenco seguente mostra esempi di estensioni che possono essere implementate come estensioni in-process o out-of-process:
- IExecuteCommand associato a una voce DelegateExecute registrata in una shell\verbo\comando sottochiave.
- IDropTarget associati al CLSID registrato in una shell \verbo\DropTarget sottochiave.
- IExplorerCommandState associato a una voce CommandStateHandler registrata sotto una sottochiave verbo shell\.