O primeiro problema que tenho de enfrentar para conseguir usar um sistema de plugins é conseguir identificar os plugins que temos disponíveis e os inicializar. Para isso vou criar o PluginManager.
De alguma forma tenho de definir como é que os plugins serão descobertos pela aplicação: Lidos automaticamente de uma directoria pré-definida, previamente registados usando uma UI na aplicação, ficheiros de configuração, etc. Como não quero ter de decidir isso para já, vou implementar a forma mais fácil que me consiga lembrar e, tendo o cuidado de não ter o uso desta implementação especifica ‘hard-coded’ em lugar algum, vou permitir que mais tarde se possa mudar o plugin manager (quem sabe usando.. um plugin!)
1: public interface IPluginManager
2: {
3: IPlugin[] LoadPlugins();
4: }
5:
6: public interface IPlugin
7: {
8: }
Temos então uma interface para o PluginManager que apenas tem um método, que devolve todos os plugins que foram encontrados.
Para a primeira implementação do PluginManager, segui o caminho mais simples: Primeiro procuro na directoria da aplicação por todas as dll’s que existam:
1: List <IPlugin> result = new List<IPlugin>();
2: DirectoryInfo directoryInfo = new DirectoryInfo(Application.StartupPath);
3: FileInfo[] files = directoryInfo.GetFiles("*.dll");
De seguida, em cada ficheiro, procuro por classes que implementem a interface IPlugin:
1: foreach (FileInfo file in files)
2: {
3: Assembly assembly = Assembly.LoadFile(file.FullName);
4: foreach (Type type in assembly.GetTypes())
5: {
6: Type[] interfaces = type.GetInterfaces();
7: foreach (Type interfaceType in interfaces)
8: {
9: if (interfaceType == typeof(IPlugin))
10: {
11: ...
12: }
13: }
14: }
15: }
Basta depois criar instâncias destas classes, retornando-as no final.
1: ConstructorInfo constructor = type.GetConstructor(Type.EmptyTypes);
2: try
3: {
4: if (constructor != null)
5: result.Add((IPlugin) constructor.Invoke(new object[0]));
6: }
7: catch (Exception)
8: {
9: //Could not load plugin
10: //TODO: report back result of process.
11: }
12: break;
Aqui, após procurar o construtor sem argumentos, o único cuidado a ter é, ao invocar o construtor, apanhar todas as excepções que possam ocorrer. Isto, na verdade, deverá ser um cuidado a ter sempre que no código vá invocar algo num plugin, pois temos de assumir que, eventualmente, algum plugin irá estar menos bem implementado, prevenindo assim possíveis problemas na aplicação.
Para já as excepções encontradas ao invocar os plugins são ignoradas mas, após a implementação do host/controller da aplicação, vamos arranjar uma forma de apresentar feedback ao utilizador sobre as operações dos plugins.
Já agora, se tiverem a usar a framework 3.5, vejam antes aqui como initializar os plugins usando o novo namespace System.AddIn. Reduz-se esta classe a 3 linhas de código!

1 comment
Comments feed for this article
Agosto 1, 2008 às 11:50 am
Plugins: Shell « using System.Reflection;
[...] classes descritas até agora (parte 1 e parte 2) estão num projecto sem qualquer acesso a forms, não incluindo o [...]