Conseils pour l’implémentation d’extensions In-Process

Les extensions in-process sont chargées dans tous les processus qui les déclenchent. Par exemple, une extension d’espace de noms Shell peut être chargée dans n’importe quel processus qui accède directement ou indirectement à l’espace de noms Shell. L’espace de noms Shell est utilisé par de nombreuses opérations Shell, telles que l’affichage d’une boîte de dialogue de fichier commune, le lancement d’un document via son application associée ou l’obtention de l’icône utilisée pour représenter un fichier. Étant donné que les extensions in-process peuvent être chargées dans des processus arbitraires, veillez à ce qu’elles n’affectent pas négativement l’application hôte ou d’autres extensions in-process.

Un runtime particulièrement important est le Common Language Runtime (CLR), également appelé code managé ou .NET Framework. Microsoft recommande de ne pas écrire des extensions managées in-process dans Windows Explorer ou Windows Internet Explorer et ne les considère pas comme un scénario pris en charge.

Cette rubrique décrit les facteurs à prendre en compte lorsque vous déterminez si un runtime autre que le CLR peut être utilisé par les extensions in-process. Parmi les autres runtimes, citons Java, Visual Basic, JavaScript/ECMAScript, Delphi et la bibliothèque de runtime C/C++. Cette rubrique fournit également certaines raisons pour lesquelles le code managé n’est pas pris en charge dans les extensions in-process.

Conflits de version

Un conflit de version peut survenir par le biais d’un runtime qui ne prend pas en charge le chargement de plusieurs versions du runtime au sein d’un même processus. Les versions du CLR antérieures à la version 4.0 appartiennent à cette catégorie. Si le chargement d’une version d’un runtime empêche le chargement d’autres versions de ce même runtime, cela peut créer un conflit si l’application hôte ou une autre extension in-process utilise une version en conflit. Dans le cas d’un conflit de version avec une autre extension in-process, le conflit peut être difficile à reproduire, car l’échec nécessite les extensions en conflit appropriées et le mode d’échec dépend de l’ordre dans lequel les extensions en conflit sont chargées.

Prenons l’exemple d’une extension in-process écrite à l’aide d’une version du CLR antérieure à la version 4.0. Chaque application sur l’ordinateur qui utilise une boîte de dialogue Ouvrir un fichier peut potentiellement avoir le code managé de la boîte de dialogue et sa dépendance CLR correspondante chargées dans le processus de l’application. L’application ou l’extension qui est la première à charger une version antérieure à 4.0 du CLR dans le processus de l’application limite les versions du CLR qui peuvent être utilisées ultérieurement par ce processus. Si une application managée avec une boîte de dialogue Ouvrir est générée sur une version conflictuelle du CLR, l’extension risque de ne pas s’exécuter correctement et de provoquer des défaillances dans l’application. À l’inverse, si l’extension est la première à charger dans un processus et qu’une version en conflit de code managé tente de se lancer après cela (peut-être une application managée ou une application en cours d’exécution charge le CLR à la demande), l’opération échoue. Pour l’utilisateur, il semble que certaines fonctionnalités de l’application cessent de fonctionner de façon aléatoire ou que l’application se bloque mystérieusement.

Notez que les versions du CLR égales ou ultérieures à la version 4.0 ne sont généralement pas sensibles au problème de contrôle de version, car elles sont conçues pour coexister entre elles et avec la plupart des versions antérieures à la version 4.0 du CLR (à l’exception de la version 1.0, qui ne peut pas coexister avec d’autres versions). Toutefois, des problèmes autres que des conflits de version peuvent survenir, comme indiqué dans le reste de cette rubrique.

Problèmes de performance

Des problèmes de performances peuvent survenir avec les runtimes qui imposent une pénalité de performances importante lorsqu’ils sont chargés dans un processus. La pénalité de performances peut se faire sous forme d’utilisation de la mémoire, d’utilisation du processeur, de temps écoulé ou même de consommation d’espace d’adressage. Le CLR, JavaScript/ECMAScript et Java sont connus pour être des runtimes à fort impact. Étant donné que les extensions in-process peuvent être chargées dans de nombreux processus, et sont souvent effectuées à des moments sensibles aux performances (par exemple, lors de la préparation d’un menu pour afficher l’utilisateur), les runtimes à fort impact peuvent avoir un impact négatif sur la réactivité globale.

Un runtime à fort impact qui consomme des ressources importantes peut provoquer un échec dans le processus hôte ou une autre extension in-process. Par exemple, un runtime à fort impact qui consomme des centaines de mégaoctets d’espace d’adressage pour son tas peut entraîner l’incapacité de l’application hôte à charger un jeu de données volumineux. En outre, étant donné que les extensions in-process peuvent être chargées dans plusieurs processus, une consommation élevée de ressources dans une seule extension peut rapidement se multiplier en consommation élevée de ressources sur l’ensemble du système.

Si un runtime reste chargé ou continue à consommer des ressources même lorsque l’extension qui utilise ce runtime a été déchargée, ce runtime ne peut pas être utilisé dans une extension.

Problèmes spécifiques au .NET Framework

Les sections suivantes décrivent des exemples de problèmes rencontrés avec l’utilisation du code managé pour les extensions. Il ne s’agit pas d’une liste complète de tous les problèmes possibles que vous pourriez rencontrer. Les problèmes décrits ici sont à la fois des raisons pour lesquelles le code managé n’est pas pris en charge dans les extensions et des points à prendre en compte lorsque vous évaluez l’utilisation d’autres runtimes.

Ré-entrance

Lorsque le CLR bloque un thread d’appartement à thread unique (STA), par exemple, en raison d’une instruction Monitor.Enter, WaitHandle.WaitOne ou de verrouillage en attente, le CLR, dans sa configuration standard, entre une boucle de message imbriquée pendant qu’il attend. De nombreuses méthodes d’extension ne sont pas autorisées à traiter les messages, et cette réentrance imprévisible et inattendue peut entraîner un comportement anormal qui est difficile à reproduire et à diagnostiquer.

L’appartement multithread

Le CLR crée des wrappers pouvant être appelé au runtime pour les objets COM (Component Object Model). Ces mêmes wrappers callables d’exécution sont détruits ultérieurement par le finaliseur du CLR, qui fait partie de l’appartement multithread (MTA). Le déplacement du proxy du sta vers l’assistant MTA nécessite un marshaling, mais toutes les interfaces utilisées par les extensions ne peuvent pas être marshalées.

Durées de vie des objets non déterministes

Le CLR offre des garanties de durée de vie d’objet plus faibles que le code natif. De nombreuses extensions ont des exigences de nombre de références sur les objets et les interfaces, et le modèle de garbage collection utilisé par le CLR ne peut pas répondre à ces exigences.

  • Si un objet CLR obtient une référence à un objet COM, la référence d’objet COM détenue par le wrapper d’appel du runtime n’est pas libérée tant que le wrapper pouvant être appelé à l’exécution n’est pas récupéré par le garbage collection. Le comportement de mise en production non déterministe peut entrer en conflit avec certains contrats d’interface. Par exemple, la méthode IPersistPropertyBag::Load exige qu’aucune référence au conteneur de propriétés ne soit conservée par l’objet lorsque la méthode Load est retournée.
  • Si une référence d’objet CLR est retournée au code natif, le wrapper callable runtime abandonne sa référence à l’objet CLR lorsque l’appel final du wrapper runtime callable à Release est effectué, mais l’objet CLR sous-jacent n’est pas finalisé tant qu’il n’est pas récupéré par le garbage collect. La finalisation non déterministe peut entrer en conflit avec certains contrats d’interface. Par exemple, les gestionnaires de miniatures doivent libérer toutes les ressources immédiatement lorsque leur nombre de références tombe à zéro.

Utilisations acceptables du code managé et d’autres runtimes

Il est acceptable d’utiliser du code managé et d’autres runtimes pour implémenter des extensions hors processus. Voici quelques exemples d’extensions Shell hors processus :

  • Gestionnaires en préversion
  • Actions basées sur la ligne de commande, telles que celles inscrites sous les sous-clés decommande deverbe\ de l’interpréteur\ de commandes.
  • Objets COM implémentés sur un serveur local, pour les points d’extension Shell qui permettent une activation hors processus.

Certaines extensions peuvent être implémentées en tant qu’extensions in-process ou out-of-process. Vous pouvez implémenter ces extensions en tant qu’extensions hors processus si elles ne répondent pas à ces exigences pour les extensions in-process. La liste suivante présente des exemples d’extensions qui peuvent être implémentées en tant qu’extensions in-process ou out-of-process :

  • IExecuteCommand associé à une entrée DelegateExecute inscrite sous une sous-cléde commandede verbe\de l’interpréteur\ de commandes.
  • IDropTarget associé au CLSID inscrit sous une sous-clédropTarget duverbe\d’interpréteur\ de commandes.
  • IExplorerCommandState associé à une entrée CommandStateHandler inscrite sous une sous-cléde verbede l’interpréteur de commandes\.