Comment charger des plugins en .NET ?
-
08-06-2019 - |
Question
J'aimerais proposer un moyen de créer des plugins chargeables dynamiquement dans mon logiciel.Une manière typique de procéder consiste à utiliser le Charger la bibliothèque Fonction WinAPI pour charger une DLL et appeler ObtenirProcAddress pour obtenir un pointeur vers une fonction à l’intérieur de cette dll.
Ma question est de savoir comment charger dynamiquement un plugin dans une application C#/.Net ?
La solution
L'extrait de code suivant (C#) construit une instance de toutes les classes concrètes dérivées de Base
trouvé dans les bibliothèques de classes (*.dll) dans le chemin de l'application et les stocke dans une liste.
using System.IO;
using System.Reflection;
List<Base> objects = new List<Base>();
DirectoryInfo dir = new DirectoryInfo(Application.StartupPath);
foreach (FileInfo file in dir.GetFiles("*.dll"))
{
Assembly assembly = Assembly.LoadFrom(file.FullName);
foreach (Type type in assembly.GetTypes())
{
if (type.IsSubclassOf(typeof(Base)) && type.IsAbstract == false)
{
Base b = type.InvokeMember(null,
BindingFlags.CreateInstance,
null, null, null) as Base;
objects.Add(b);
}
}
}
Modifier: Les classes mentionnées par Mat sont probablement une meilleure option dans .NET 3.5.
Autres conseils
Depuis .NET 3.5, il existe un moyen formalisé et intégré de créer et de charger des plugins à partir d'une application .NET.Tout est dans le Système.AddIn espace de noms.Pour plus d'informations, vous pouvez consulter cet article sur MSDN : Compléments et extensibilité
Chargement dynamique des plug-ins
Pour plus d’informations sur la façon de charger dynamiquement des assemblys .NET, consultez cette question (et ma réponse).Voici un code à charger pour créer un AppDomain
et y charger un assembly.
var domain = AppDomain.CreateDomain("NewDomainName");
var pathToDll = @"C:\myDll.dll";
var t = typeof(TypeIWantToLoad);
var runnable = domain.CreateInstanceFromAndUnwrap(pathToDll, t.FullName)
as IRunnable;
if (runnable == null) throw new Exception("broke");
runnable.Run();
Déchargement des plug-ins
Une exigence typique d’un framework de plugins est de décharger les plugins.Pour décharger des assemblys chargés dynamiquement (par ex.plug-ins et compléments), vous devez décharger le contenant AppDomain
.Pour plus d'informations, voir cet article sur MSDN sur le déchargement des domaines d'application.
Utiliser WCF
Il y a un question et réponse sur le débordement de pile qui décrivent comment utiliser Windows Communication Framework (WCF) pour créer une infrastructure de plug-in.
Frameworks de plug-ins existants
Je connais deux frameworks de plug-ins :
- Mono.Compléments - Comme mentionné dans cette réponse à une autre question.
- Cadre de compléments gérés (MAF) - C'est le
System.AddIn
espace de noms comme mentionné par Matt dans sa réponse.
Certains parlent de Cadre d'extensibilité géré (MEF) en tant que framework de plug-in ou de complément, ce qui n'est pas le cas.Pour plus d'informations, voir cette question StackOverflow.com et cette question StackOverflow.com.
Une astuce consiste à charger tous les plugins et autres dans votre propre AppDomain, car le code en cours d'exécution peut être potentiellement malveillant.Un propre AppDomain peut également être utilisé pour « filtrer » les assemblys et les types que vous ne souhaitez pas charger.
AppDomain domain = AppDomain.CreateDomain("tempDomain");
Et pour charger un assembly dans le domaine d'application :
AssemblyName assemblyName = AssemblyName.GetAssemblyName(assemblyPath);
Assembly assembly = domain.Load(assemblyName);
Pour décharger le domaine d'application :
AppDomain.Unload(domain);
Oui, ++ pour Matt et System.AddIn (un article du magazine MSDN en deux parties sur System.AddIn est disponible ici et ici).Une autre technologie que vous voudrez peut-être examiner pour avoir une idée de l'orientation future du .NET Framework est la Cadre d'extensibilité géré actuellement disponible sous forme CTP sur Codeplex.
Fondamentalement, vous pouvez le faire de deux manières.
La première consiste à importer kernel32.dll et à utiliser LoadLibrary et GetProcAddress comme vous l'avez utilisé auparavant :
[DllImport("kernel32.dll")]
internal static extern IntPtr LoadLibrary(String dllname);
[DllImport("kernel32.dll")]
internal static extern IntPtr GetProcAddress(IntPtr hModule, String procname);
La seconde consiste à le faire à la manière .NET :en utilisant la réflexion.Vérifiez l’espace de noms System.Reflection et les méthodes suivantes :
Vous chargez d’abord l’assembly par son chemin, puis en récupérez le type (classe) par son nom, puis récupérez à nouveau la méthode de la classe par son nom et enfin appelez la méthode avec les paramètres appropriés.
L'article est un peu plus ancien, mais toujours applicable pour créer une couche d'extensibilité au sein de votre application :