Como carregar plugins em .NET?
-
08-06-2019 - |
Pergunta
Gostaria de fornecer uma maneira de criar plug-ins carregáveis dinamicamente em meu software.A maneira típica de fazer isso é usar o Biblioteca de carga Função WinAPI para carregar uma dll e chamar GetProcAddress para obter um ponteiro para uma função dentro dessa dll.
Minha pergunta é como carrego dinamicamente um plugin no aplicativo C#/.Net?
Solução
O trecho de código a seguir (C#) constrói uma instância de quaisquer classes concretas derivadas de Base
encontrado em bibliotecas de classes (*.dll) no caminho do aplicativo e os armazena em uma lista.
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);
}
}
}
Editar: As aulas referidas por Matt são provavelmente uma opção melhor no .NET 3.5.
Outras dicas
A partir do .NET 3.5, existe uma maneira formalizada e integrada de criar e carregar plug-ins de um aplicativo .NET.Está tudo no System.AddIn espaço para nome.Para mais informações você pode conferir este artigo no MSDN: Suplementos e extensibilidade
Carregando plug-ins dinamicamente
Para obter informações sobre como carregar dinamicamente assemblies .NET, consulte essa questão (e minha resposta).Aqui está um código para carregar a criação de um AppDomain
e carregando um assembly nele.
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();
Descarregando plug-ins
Um requisito típico de uma estrutura de plugins é descarregar os plugins.Para descarregar montagens carregadas dinamicamente (por exemplo,plug-ins e suplementos), você precisa descarregar o conteúdo AppDomain
.Para mais informações, veja este artigo no MSDN sobre descarregamento de AppDomains.
Usando WCF
Existe um pergunta e resposta de estouro de pilha que descrevem como usar o Windows Communication Framework (WCF) para criar uma estrutura de plug-in.
Estruturas de plug-ins existentes
Conheço duas estruturas de plug-ins:
- Mono.Suplementos - Como mencionado em esta resposta para outra pergunta.
- Estrutura de suplemento gerenciado (MAF) - Isto é o
System.AddIn
espaço para nome como mencionado por Matt em sua resposta.
Algumas pessoas falam sobre Estrutura de extensibilidade gerenciada (MEF) como uma estrutura de plug-in ou suplemento, o que não é.Para mais informações, veja esta pergunta do StackOverflow.com e esta pergunta do StackOverflow.com.
Uma dica é carregar todos os plugins e afins em um AppDomain próprio, já que o código em execução pode ser potencialmente malicioso.Um AppDomain próprio também pode ser usado para "filtrar" assemblies e tipos que você não deseja carregar.
AppDomain domain = AppDomain.CreateDomain("tempDomain");
E para carregar um assembly no domínio do aplicativo:
AssemblyName assemblyName = AssemblyName.GetAssemblyName(assemblyPath);
Assembly assembly = domain.Load(assemblyName);
Para descarregar o domínio do aplicativo:
AppDomain.Unload(domain);
Sim, ++ para Matt e System.AddIn (um artigo de duas partes da revista MSDN sobre System.AddIn está disponível aqui e aqui).Outra tecnologia que você pode querer observar para ter uma ideia de onde o .NET Framework pode estar indo no futuro é a Estrutura de extensibilidade gerenciada atualmente disponível em formato CTP no Codeplex.
Basicamente você pode fazer isso de duas maneiras.
A primeira é importar kernel32.dll e usar LoadLibrary e GetProcAddress como você usou antes:
[DllImport("kernel32.dll")]
internal static extern IntPtr LoadLibrary(String dllname);
[DllImport("kernel32.dll")]
internal static extern IntPtr GetProcAddress(IntPtr hModule, String procname);
A segunda é fazer isso no estilo .NET:usando reflexão.Verifique o namespace System.Reflection e os seguintes métodos:
Primeiro você carrega o assembly pelo seu caminho, depois obtém o tipo (classe) dele pelo seu nome, depois obtém o método da classe pelo seu nome novamente e finalmente chama o método com os parâmetros relevantes.
O artigo é um pouco mais antigo, mas ainda é aplicável para a criação de uma camada de extensibilidade em seu aplicativo:
Permita que os usuários adicionem funcionalidade aos seus aplicativos .NET com macros e plug-ins