Level Extreme platform
Subscription
Corporate profile
Products & Services
Support
Legal
Français
Expose Host Objects to Plug-Ins
Message
General information
Forum:
ASP.NET
Category:
Object Oriented Programming
Environment versions
Environment:
C# 1.1
OS:
Windows XP SP2
Network:
Windows 2003 Server
Database:
MS SQL Server
Miscellaneous
Thread ID:
01014456
Message ID:
01014678
Views:
18
Yes, doing PlugIns usually involves using either interfaces or base classes that the Plug-in client has to implement.

You want to be careful with this though, lest these interfaces become to complex. Just like anything keep it simple and create multiple interfaces rather than a single one and then have the application check whether the interface is implemented before making the the call to a particular plug in method.

The key issue you're facing probably is the fact that you typically don't have access to the bus object on a form because it's marked protected or private. So if you want this sort of functionality you'll need to expose this stuff as Public. No amount of interface work will let you get around that <g>... As an alternative you can pass information to the plug in from the form. You could have a method on the form that knows how to generically setup a plug in. A method say ConfigurePlugIn. When the Plugin is activated it can call this method and it gets passed the business object or objects or whatever you need to work with.

I just went through this exercise with our Web Monitor product (http://www.west-wind.com/webmonitor/) which has a pretty simple plug-in architecture. Typically you want to support two kinds of interfaces - an interactive 'pop-up' type that can be activated from the menu and a hooked interface that gets invoked when some actions occur. For example, in Web Monitor the Add-in gets notified when a site is checked, when a log entry is written when an email is sent etc.

What I did is hook the hook interfaces with events which is the easiest usually. So you have some place where you load addins and hook up the events. The user either hooks up the events themselves or you use flags for it. For example:
public bool LoadAddIns(WebMonitorSiteList SiteList,WebMonitorMain IDE) 
{
	Assembly Assm;

	this.ErrorMessage = "";
	this.List.Clear();

	if ( !Directory.Exists("addins") )
		return true;

	string[] Files = Directory.GetFiles("addins","*.dll");
	
	Factory = new AppDomainObjectFactory();

	foreach(string File in Files) 
	{
		try 
		{
			Assm = Assembly.LoadFrom(File);
		}
		catch(Exception ex)
		{
			this.ErrorMessage += ex.Message + "\r\n";
			continue;
		}
	
		Type[] Types = Assm.GetTypes();

		foreach(Type T in Assm.GetTypes() ) 
		{
			object[] arr = T.GetCustomAttributes( typeof(WebMonitorAddinAttribute),false);
			if ( arr.Length == 0)
				continue;

			string Description = ((WebMonitorAddinAttribute) arr[0]).Description;																

			// *** It's an addin class

			//Westwind.WebMonitor.IWebMonitorAddin Addin = Activator.CreateInstance( T ) as IWebMonitorAddin;
			IWebMonitorAddin Addin = Factory.CreateObject(Directory.GetCurrentDirectory() + "\\" + File,T.FullName) as IWebMonitorAddin;
			if (Addin == null) 
			{
				this.ErrorMessage = this.ErrorMessage + "Couldn't load add-in " + File + "\r\n";
				continue;
			}

			// Add it to our list
			this.Add(File,Description,Addin);

			// *** Pass down IDE reference to add-in if available
			if (IDE != null)
				Addin.IDE = IDE;

			// *** Now initialize by passing a SiteList
			// *** OnInit should set AddinEventType
			Addin.OnInit(SiteList);
		
			// *** Hook up Message events
			if ( (Addin.AddinEventType & AddinEventTypes.MessageEvents) != 0 )
				SiteList.WebMonitorMessage += new WebMonitorSiteList.delWebMonitorMessage( Addin.OnMessage );

			// *** Run through all sites and attach events
			foreach(WebMonitorSite Site in SiteList.Sites) 
			{
				if ( (Addin.AddinEventType & AddinEventTypes.AllSiteEvents) != 0 || (Addin.AddinEventType & AddinEventTypes.SiteHttpRequest) != 0) 
					Site.OnHttpRequest += new WebMonitorSite.WebMonitorSiteEventHandler( Addin.OnHttpRequest  );

				if ( (Addin.AddinEventType & AddinEventTypes.AllSiteEvents) != 0 || (Addin.AddinEventType & AddinEventTypes.SiteEmail) != 0) 
					Site.OnSendEmail += new WebMonitorSite.WebMonitorSiteEventHandler( Addin.OnSendEmail );

				if ( (Addin.AddinEventType & AddinEventTypes.AllSiteEvents) != 0 || (Addin.AddinEventType & AddinEventTypes.SiteLogError) != 0) 
					Site.OnLogError += new WebMonitorSite.WebMonitorSiteEventHandler( Addin.OnLogError );

				if ( (Addin.AddinEventType & AddinEventTypes.AllSiteEvents) != 0 || (Addin.AddinEventType & AddinEventTypes.SiteLogError) != 0) 
					Site.OnLogDetail  += new WebMonitorSite.WebMonitorSiteEventHandler( Addin.OnLogDetail );
			}
		}
	}


	if (this.ErrorMessage != "")
		return false;

	return true;

}
You can see here I go through each objects that get hooked by the addin and hook up events that call back into the Add-in. All your Application code has to to do then is fire the events:
if (this.OnLogDetail != null)
   this.OnLogDetatil(this);
If your app objects already fire events you won't have to really do anything else.

Note also that in the loader you can assign some sort of global object. In my case I'm passing an instance of the IDE which is the top level form, which in turn has references to all the rest of the application (ie. SiteList and individual Sites).

In your case that global reference might be the Mere Mortals App object or possibly a top level form that has references to the various business objects.

Building solid Add-ins and adin-in architectures can be tough though. You need to be able to do solid error trapping since your app is running foreign code and realistically you probably want to load add-ins into another AppDomain so they can be unloaded and/or refreshed.


+++ Rick ---


>I have a Windows Forms app that's extensible in that it looks for .dlls in a "Plug-In" folder, loads each assembly, iterates through the types, and if a type implements my interface, does stuff. Stuff it does includes grabbing a MenuItem to add to the app's "Plug-In" MenuItem.
>
>This works fine, but it's pretty much a one-way street. That is, the plug-in can't get information from the host. Just for kicks I added a set property to the interface so now the plug-in has a reference to the app's main form - you could do cute things like iterate through the app's MDI children and manipulate which form is activated, or any other public member.
>
>What I really want is to let a plug-in have access to objects in the app. Objects in this case would be data (Mere Mortals business objects) associated with an MDI form.
>
>What I've just started is another interface. I'm thinking my MDI child forms should implement the interface, then when the plug-in has a reference to a child form, could cast it to the new interface, and call, for example, .GetDataSet() or .GetBusinessOjbect().
>
>Is this the right direction to pursue? Any thoughts are appreciated.
>
>Mike
+++ Rick ---

West Wind Technologies
Maui, Hawaii

west-wind.com/
West Wind Message Board
Rick's Web Log
Markdown Monster
---
Making waves on the Web

Where do you want to surf today?
Previous
Next
Reply
Map
View

Click here to load this message in the networking platform