C# aplikace s podporou pluginů
 x   TIP: Přetáhni ikonu na hlavní panel pro připnutí webu
Reklama

C# aplikace s podporou pluginůC# aplikace s podporou pluginů

 

C# aplikace s podporou pluginů

Google       Google       23. 4. 2006       27 758×

Pokud programujete pro platformu .NET Framework a chcete se dozvědět, jak jednoduše implementovat do své aplikace podporu pluginů, čtěte dál! Vše si ukážeme a vysvětlíme na názorném příkladě.

Reklama
Reklama

Platforma NET Framework je svou architekturou a jednoduchostí při manipulaci s assembly (dll knihovnami) přímo ideální pro vytváření aplikací modulárně. Určitě i vy poznáte, jak jednoduché to celé je. Předpokládám však alespoň základní znalosti programování v NET Frameworku. Takže dejme se do práce.

Celý postup budu demonstrovat na našem velmi jednoduchém příkladu modulární aplikace PluginApp. Pokud jste registrovaní uživatelé, můžete si celý příklad i s pluginy stáhnout jako projekt do Visual Studia 2003.NET nebo Visual Studia 2005 (Express) v sekci Download. Zdrojové kódy jsou podrobně okomentovány.

Trochu teorie

Ná následujícím schématu si popíšeme, jak vlastně taková aplikace funguje.

Aplikaci tvoří mimo jiné hlavní formulář, který obsahuje jádro. Jádro komunikuje s hlavním formulářem a pluginy. Je tedy jakýmsi sprostředkovatelem mezi pluginy a hlavním formulářem aplikace. Dále si ukážeme, že nám poslouží i ke komunikaci mezi jednotlivými pluginy.


Praxe

Nejdříve ze všeho si musíme nadefinovat, přes jaké rozhraní bude probíhat komunikace. Každý plugin bude muset implementovat rozhraní IPLuginBase.


/// 
/// Základní rozhraní všech pluginů. Přes toto rozhraní jádro komunikuje s pluginy. 
/// 
public interface IPluginBase
{
	//První metoda, kterou jádro volá a předává i instanci jádra.
	void Load(IApplicationBase app);
	//Touto metodou předá jádro pluginu pole všech pluginů. Díky tomu může probíhat komunikace mezi pluginy.
	void PluginsLoaded(IPluginBase[] plugins);
	//Demonstrační metoda, kterou jádro volá po kliknutí v hlavním menu aplikace.
	void DelejNeco();

	//Metody sloužící k vzájemné komunikaci mezi pluginy.
	object Metoda1(object param);
	object Metoda2(object param);

	//Vlastnost určující název pluginu.
	string Nazev
	{
		get;
	}

	//Vlastnost určující titulek pluginu.
	string Titulek
	{
		get;
	}
}

Metoda Load je volána jako první a předá pluginu instanci jádra (přesněji řečeno rozhraní přes, které bude s jádrem komunikovat), kterou si pak uložíme jako statický člen. Díky tomu pak můžeme z každého místa v pluginu komunikovat s jádrem. Metoda PluginsLoaded je volána, jakmile u všech pluginů proběhla metoda Load. Získáme tím pole rozhraní, které si taktéž uložíme do statického členu a komunikujeme s ostatními pluginy. Metody Metoda1 a Metoda2 jsou více méně dobrovolné. Plugin se implementovat nemusí (jednoduše vrací null) a jádro tyto metody nikdy volat nebude. Slouží výhradně k nějaké speciální funkčnosti, kterou mohou využívat ostatní pluginy.

Nyní si nadefinujeme rozhraní pro komunikaci s jádrem IApplicationBase.


/// 
/// Rozhraní, kterým budou puginy komunikovat s jádrem aplikace.
/// 
public interface IApplicationBase
{
	void Maximalizuj();
	void Ukonci(string duvod);
}

Metoda Maximalizuj maximalizuje hlavní okno aplikace a metoda Ukonci ukončí aplikaci s hlášením obsahujícím důvod.

Tyto dvě rozhraní zkompilujeme do dynamické knihovny PluginBase.dll, která bude takovým „prostředníkem“ mezi pluginy a hlavním exe souborem.

Nyní vytvoříme třídu jádra ApplicationCore, která implementuje rozhraní IApplicationBase.


/// 
/// Základní třída celé aplikace. Díky ní mohou komunikovat pluginy s jádrem.
/// 
public class ApplicationCore : IApplicationBase
{
	public delegate void MaximalizujEventHandler(object sender, EventArgs e);
	public event MaximalizujEventHandler MaximalizujEvent;		

	public ApplicationCore()
	{
			
	}

	/// 
	/// Implementace metody  Ukonci, kterou mohou volat všechny pluginy.
	/// 	
	public void Ukonci(string duvod)
	{
		MessageBox.Show("Aplikace bude ukončena: "+duvod, "PluginApp", MessageBoxButtons.OK, MessageBoxIcon.Warning);
		Application.Exit();
	}

	/// 
	/// Implementace metody  Maximalizuj, kterou mohou volat všechny pluginy.
	/// 
	public void Maximalizuj()
	{
		OnMaximalizuj(this);
	}

	/// 
	/// Metoda vyvolávající údálost MaximalizujEvent.
	/// 	
	protected void OnMaximalizuj(object sender)
	{
		if(MaximalizujEvent != null)
			MaximalizujEvent(sender, new EventArgs());
	}

}

Metoda Ukonci jednoduše ukončí celou aplikaci. Ale metoda Maximalizuj to má složitější. Nemůže se přímo dostat k hlavnímu formuláři, takže si nadefinujeme příslušnou událost, na kterou bude formulář reagovat.

Následuje hlavní formulář MainForm (aby zde byl zdrojový kód přehlednější, vypustil jsme kód generovaný designerem).


/// 
/// Hlavní formulář aplikace.
/// 
public class MainForm : System.Windows.Forms.Form
{
	//Statická instance jádra.
	public static ApplicationCore app;
	//Statický seznam načtených pluginů.
	public static ArrayList plugins;
         .............................
         .............................
        private void MainForm_Load(object sender, System.EventArgs e)
	{
		//Inicializace seznamu pluginů.
		plugins = new ArrayList();
		//Inicializace instance jádra.
		app = new ApplicationCore();
		//Přihlášení k odběru události MaximalizujEvent.
		app.MaximalizujEvent += new PluginApp.ApplicationCore.MaximalizujEventHandler(app_MaximalizujEvent);

		//Načtení pluginů.
		LoadPlugins();
		//Vytvoření položky v hlavním menu pro každý plugin.
		CreateMenu();
	}

	/// 
	/// Obsluha údálosti MaximalizujEvent.
	/// 		
	private void app_MaximalizujEvent(object sender, EventArgs e)
	{
		WindowState = FormWindowState.Maximized;
	}

	/// 
	/// Vytvoření položky v hlavním menu pro každý plugin.
	/// 
	private void CreateMenu()
	{
		//Ověříme si, zda-li má vůbec smysl menu "Pluginy" vytvářet.
		if(plugins.Count > 0)
		{
			//Vytvoříme menu "Pluginy"
			MenuItem menuItem = new MenuItem();
			menuItem.Text = "Pluginy";

			//Pro každý plugin vytvoříme novou položku
			foreach(IPluginBase plugin in plugins)
			{
				MenuItem pluginItem = new MenuItem();					
				pluginItem.Text = plugin.Nazev;
				//Událost "Click" všech položek pluginů obsluhuje jedinná metoda. 
				pluginItem.Click += new EventHandler(pluginItem_Click);

				menuItem.MenuItems.Add(pluginItem);
			}

			mainMenu.MenuItems.Add(1, menuItem);
		}
	}

	/// 
	/// Načtení pluginů.
	/// 
	private void LoadPlugins()
	{
		//Adresář, kde jsou naše pluginy uloženy.
		string pluginDir = Application.StartupPath+@"\Plugins";

		if(Directory.Exists(pluginDir))
		{
			//Zajímají nás pouze dll knihovny.
			string[] files = Directory.GetFiles(pluginDir, "*.dll");

			for(int i = 0; i < files.Length; i++)
			{
				//Knihovnu "PluginBase.dll" ignorujeme.
				if(files[i] != pluginDir+@"\PluginBase.dll")
				{
					try
					{
						//Vytvoříme instanci třídy Assembly.
						Assembly asm = Assembly.LoadFile(files[i]);
						//Vytvoříme instanci třídy "Plugin", která musí implementovat rozhraní "IPluginBase".
						IPluginBase plugin = (IPluginBase)asm.CreateInstance("PluginApp.Plugin");

						//Pokud se nám podařilo získat rozhraní ke komunikaci s pluginem, přidáme jej do seznamu.
						if(plugin != null)
							plugins.Add(plugin);
					}
					catch(Exception e)
					{
						MessageBox.Show("Chyba při načítání pluginu "+files[i]+": "+e.Message, "PluginApp", MessageBoxButtons.OK, MessageBoxIcon.Error);
					}
				}
			}
		}

		//Zavoláme metodu "Load" všech pluginů.
		foreach(IPluginBase plugin in plugins)
		{
			plugin.Load(app);
		}
			
		//Vytvoříme si pole rozhraní všech pluginů, abychom jej pohli předat ostatním pluginům.
		IPluginBase[] pluginy = new IPluginBase[plugins.Count];
			
		for(int x = 0; x < plugins.Count; x++)
		{
			pluginy[x] = (IPluginBase)plugins[x];
		}

		//Jakmile jsou všechny pluginy inicializovány, předáme každému z nich pole rozhraní ke vzájemné komunikaci.
		foreach(IPluginBase plugin in plugins)
		{
			plugin.PluginsLoaded(pluginy);
		}

		label1.Text = "Počet pluginů: "+plugins.Count.ToString();
	}

	/// 
	/// Metoda obsluhující události "Click" položek všech pluginů.
	/// 		
	private void pluginItem_Click(object sender, EventArgs e)
	{
		//K rozeznání, ke kterému pluginu tato položka patří nám poslouží vlastnost Text.
		string pluginName = ((MenuItem)sender).Text;

		//Projdeme seznam pluginů.
		foreach(IPluginBase plugin in plugins)
		{
			//Nalezneme plugin, kterého se tato událost týká.
			if(plugin.Nazev == pluginName)
			{
				//Zavoláme samotný plugin.
				plugin.DelejNeco();
			}
		}
	}
}

Máme zde tedy statický člen jádra aplikace (třídy ApplicationCore) a statický seznam načtenách pluginů (ArrayList obsahující rozhraní IPluginBase). Metoda LoadPlugins si nejprve ověří, zda-li existuje adresář obsahující pluginy (každý plugin je tvořen jedinou dll knihovnou v daném adresáři). Dále si z instance třídy Assembly vytvoříme instanci rozhraní IPluginBase (každý plugin musí obsahovat třídu Plugin implementující rozhraní IPluginBase ve jmeném prostoru PluginApp). Ověříme si, zda-li se nám to povedlo, a přidáme si ji do seznamu. Zavoláme metody Load všech pluginů. Vytvoříme si pole rozhraní všech pluginů a každému pluginu jej pak předáme. Na ukázku si pro každý plugin vytvoříme položku v hlavním menu, kterou vyvoláme metodu DelejNeco.


Tak, a nyní si vytvoříme nějaké pluginy.


public class Plugin : IPluginBase
{
	public static IApplicationBase appBase;
	public static ArrayList pluginy;

	public Plugin()
	{
		pluginy = new ArrayList();
	}

	#region IPluginBase Members

	public string Titulek
	{
		get
		{
			return "Maximalizuje okno";
		}
	}

	public void Load(IApplicationBase app)
	{
		appBase = app;
	}

	public void PluginsLoaded(IPluginBase[] plugins)
	{
		pluginy.AddRange(plugins);
	}

	public object Metoda1(object param)
	{
		return null;
	}

	public object Metoda2(object param)
	{
		return null;
	}

	public string Nazev
	{
		get
		{
			return "PluginA";
		}
	}

	public void DelejNeco()
	{
		//Volání metody Maximalizuj, kterou zprauje jádro.
		appBase.Maximalizuj();
	}

	#endregion
}

Jako první je PluginA. Implementovali jsme rozhraní IPluginBase a metoda DelejNeco volá metodu jádra Maximalizuj. Zde jsou implementace této metody Pluginu B a C.

PluginB


public void DelejNeco()
{
	//Volání metody Ukonci, kterou zprauje jádro.
	appBase.Ukonci("Zkouška komunikace pluginu s jádrem");
}

PluginC


public void DelejNeco()
{
	//Projdeme seznam všech pluginů.
	foreach(IPluginBase plugin in pluginy)
	{
		//Ověříme si, zda-li se nejedná o náš vlastní plugin.
		if(plugin.Nazev != Nazev)
		{
			MessageBox.Show("Spustí se metoda DelejNeco() pluginu "+plugin.Nazev);
			//Zavoláme metodu DelejNeco všech pluginů.
			plugin.DelejNeco();
		}
	}
}

Jak jistě vidíte, PluginC volá metody všech ostatních pluginů. Takže nám funguje i komunikace mezi pluginy.

Chtěl bych ještě podotknout, že zde rozhodně doporučuji zaregistrovat assembly PluginBase.dll do GAC.

Zde je výsledek našeho snažení:

Závěr

Tento příklad byl sestaven s ohledem na kompatibilitu mezi NET Frameworkem 1.1 a 2.0. Pro verzi 2.0 bych rozhodně doporučoval mimo jiné nahradit třídu ArrayList generickou třídou List. Pro skutečné nasazení by se měl vytvořit nějaký systém instalace a např. přidělování Id za běhu pluginům jádrem. To už je ale na každém z vás.

Doufám, že byl tento příklad přínosem a že zboří veškeré obavy z vytváření modulárních aplikací.

×Odeslání článku na tvůj Kindle

Zadej svůj Kindle e-mail a my ti pošleme článek na tvůj Kindle.
Musíš mít povolený příjem obsahu do svého Kindle z naší e-mailové adresy kindle@programujte.com.

E-mailová adresa (např. novak@kindle.com):

TIP: Pokud chceš dostávat naše články každé ráno do svého Kindle, koukni do sekce Články do Kindle.

Hlasování bylo ukončeno    
0 hlasů
Google
Autor má rád programování, čtení, spaní a špagety. Nemá rád dechovku, rajskou.

Nové články

Obrázek ke článku Hackerský kongres přiveze v září do Prahy špičky světové kryptoanarchie

Hackerský kongres přiveze v září do Prahy špičky světové kryptoanarchie

Hackerský kongres HCPP16 pořádá od 30. září do 2. října nezisková organizace Paralelní Polis již potřetí, a to ve stejnojmenném bitcoinovém prostoru v pražských Holešovicích. Letos přiveze na třídenní konferenci přes 40 většinou zahraničních speakerů – lídrů z oblastí technologií, decentralizované ekonomiky, politických umění a aktivismu. Náměty jejich přednášek budou také hacking, kryptoměny, věda, svoboda nebo kryptoanarchie.

Reklama
Reklama
Obrázek ke článku ICT PRO školení zaměřené nejenom na ICT

ICT PRO školení zaměřené nejenom na ICT

Dovolte, abychom se představili. Jsme zaměstnanci společnosti ICT Pro, profesionálové v oblasti poskytování komplexních ICT služeb. Neboli služeb spojených s informačními a komunikačními technologiemi, které dnes - ve 21. století - tvoří  nedílnou součást běžného provozu všech moderních firem.

loadingtransparent (function() { var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true; po.src = 'https://apis.google.com/js/plusone.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s); })();
Hostujeme u Českého hostingu       ISSN 1801-1586       ⇡ Nahoru Webtea.cz logo © 20032016 Programujte.com
Zasadilo a pěstuje Webtea.cz, šéfredaktor Lukáš Churý