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

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

 

C# aplikace s podporou pluginů

Google       Google       23. 4. 2006       33 288×

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ě.

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 Stavebnice umělé inteligence 1

Stavebnice umělé inteligence 1

Článek popisuje první část stavebnice umělé inteligence. Obsahuje lineární a plošnou optimalizaci.  Demo verzi je možné použít pro výuku i zájmovou činnost. Profesionální verze je určena pro vývojáře, kteří chtějí integrovat popsané moduly do svých systémů.

Obrázek ke článku Hybridní inteligentní systémy 2

Hybridní inteligentní systémy 2

V technické praxi využíváme často kombinaci různých disciplín umělé inteligence a klasických výpočtů. Takovým systémům říkáme hybridní systémy. V tomto článku se zmíním o určitém typu hybridního systému, který je užitečný ve velmi složitých výrobních procesech.

Obrázek ke článku Jak vést kvalitně tým v IT oboru: Naprogramujte si ty správné manažerské kvality

Jak vést kvalitně tým v IT oboru: Naprogramujte si ty správné manažerské kvality

Vedení týmu v oboru informačních technologií se nijak zvlášť neliší od jiných oborů. Přesto však IT manažeři čelí výzvě v podobě velmi rychlého rozvoje a tím i rostoucími nároky na své lidi. Udržet pozornost, motivaci a efektivitu týmu vyžaduje opravdu pevné manažerské základy a zároveň otevřenost a flexibilitu pro stále nové výzvy.

Obrázek ke článku Síla týmů se na home office může vytrácet. Odborníci radí, jak z pracovních omezení vytěžit maximum

Síla týmů se na home office může vytrácet. Odborníci radí, jak z pracovních omezení vytěžit maximum

Za poslední rok se podoba práce zaměstnanců změnila k nepoznání. Především plošné zavedení home office, které mělo být zpočátku jen dočasným opatřením, je pro mnohé už více než rok každodenní realitou. Co ale dělat, když se při práci z domova ztrácí motivace, zaměstnanci přestávají komunikovat a dříve fungující tým se rozpadá na skupinu solitérů? Odborníci na personalistiku dali dohromady několik rad, jak udržet tým v chodu, i když pracovní podmínky nejsou ideální.

Hostujeme u Českého hostingu       ISSN 1801-1586       ⇡ Nahoru Webtea.cz logo © 20032024 Programujte.com
Zasadilo a pěstuje Webtea.cz, šéfredaktor Lukáš Churý