modular application

  • Thread starter Saso Zagoranski
  • Start date
S

Saso Zagoranski

Hi!

I mentioned this in my other post today but at that time I though I had it
all figured out. When
I got a reply regarding my other questing I saw that I don't :)

Here's my problem:

I would like to have an application where I would write the core and the
users could write
their own addins for the application.
Even the basic features I would provide would be addins.

Here's my idea: Create a basic form, which would contain the ModuleLoader
class. That class
would read the modules.xml file and dynamiclly load all the modules.
All the modules would have to implement the interface IModule, plus
IModuleMenu if the module
would add something to the menubar.
Here comes the tricky part...
Some of the modules could provide some services. Let's say I have a module,
which communicates with
a GPS device and provides the service GPS. Another module would display a
map based on the GPS location. How would the module Map get the location
from another module?
The module could "ask" the ModuleLoader to point to the module, which
provides the service "GPS" but
I still don't have an idea as how to get the information from the module. I
could require that
if the module provides GPS service that he must have public properties:
X,Y,Z...

Another problem is module execution. Do I give each module it's own
execution thread?
Or perhaps just the ones that provide services?

Any ideas would REALLY help...
saso
 
S

Saso Zagoranski

That's the approach I was thinking about...
Just a few more questions...
Do your plugins inherit from IPlugin or do the implement it? I mean do you
have this:
class MyPlugin : System.Object, IPlugin ? (or MyPlugin:
System.Windows.Forms, IPlugin or ...)

In this case, isn't the Type of MyPlugin actually MyPlugin? To get the
interface
you would then have to use MyPlugin.GetType().GetInterface("IPlugin") and to
call
the methods of this interface you would then use MethodInfo.Invoke(...).
And most importantly... You can't cast an object of type MyPlugin to
IPlugin, can you?

Here's what I do so far:
I load all the types in an assembly. I go through all the types and search
if those types implement
IModule. I create an instance of the object which implements the IModule
interface with:
object myObject = Activator.CreateInstance(typeofObject);
I then use the MethodInfo.Invoke and PropertyInfo.GetValue, to start the
OnConnect method of
the plugin (OnConnect is part of IModule) and to get the list of Services
(also part of IPlugin) that the plugin provides (if any).
I then add "myObject", it's services and it's type into an array at the
host. So when some plugin would call:
Host.FindPlugin("GPS");
The Host would find that the GpsPlugin provides the GPS service and it would
provide the
object reference and Type to the plugin which requires the question.
The main problem is:
How do I cast the GpsPlugin (which is stored at the host and received as
System.Object) to the type
of GpsPlugin (note that the host stored the Type information)?

I wouldn't want to hardcode the casting... Is this even possible?

Thanks,
saso
 
S

Sami Vaaraniemi

Here's what I do in a situation that is very similar to yours. My plugins
(or modules as you call them) implement interface IPlugin, and the shell
application that loads the plugins implements interface IHost. When the host
loads a plugin it gives a reference to IHost to the plugin.

One of the methods in IHost is declared like so:

IPLugin GetPlugin(Type t);

In the implementation of this method, the host goes over all loaded plugins
and returns a reference to the first plugin that implements type 't'.

Applying this approach to your case, in order to obtain a reference to the
GPS plugin and to get the location, your Map plugin would something similar
to this:

IPlugin plugin = ihost.GetPlugin(typeof(IGpsPlugin));
IGpsPlugin gpsPlugin = (IGpsPlugin)plugin;
Location loc = gpsPlugin.Location;

This approach works for me just fine. Depending on your particular needs you
might want to tweak the logic, but anyway this is the big picture.

Regards,
Sami
 
S

Sami Vaaraniemi

Saso Zagoranski said:
That's the approach I was thinking about...
Just a few more questions...
Do your plugins inherit from IPlugin or do the implement it? I mean do you
have this:
class MyPlugin : System.Object, IPlugin ? (or MyPlugin:
System.Windows.Forms, IPlugin or ...)

My plugins inherit from IPlugin - which is the same as saying they implement
IPlugin. They also happen to be Forms, so they look like this (the second
option above):

class MyPlugin : System.Windows.Forms, IPlugin

Sometimes the plugins implement a plugin-specific interface, e.g.,

interface ISpecialPlugin : IPlugin {...}

class MySpecialPlugin : System.Windows.Forms, ISpecialPlugin

All these interfaces (IPlugin, ISpecialPlugin, IHost) are defined in an
assembly which is referenced by the plugins and by the shell.
In this case, isn't the Type of MyPlugin actually MyPlugin? To get the
interface
you would then have to use MyPlugin.GetType().GetInterface("IPlugin") and to
call
the methods of this interface you would then use MethodInfo.Invoke(...).
And most importantly... You can't cast an object of type MyPlugin to
IPlugin, can you?

Yes, the actual type of MyPlugin is MyPlugin. Due to the fact that MyPlugin
and all other plugins inherit from IPlugin, the shell can easily access the
IPlugin interface when it loads the plugin:

Assembly asm = Assembly.LoadFrom(<url>); // this string comes from
e.g., a config file
IPlugin plugin = (IPlugin)asm.CreateInstance("MyPlugin"); // the string
"MyPlugin" string comes from e.g., a config file

This way the shell can call the methods of IPlugin directly and there is no
need to invoke them through reflection. The shell only uses the interface,
it never uses the actual type MyPlugin.
Here's what I do so far:
I load all the types in an assembly. I go through all the types and search
if those types implement
IModule. I create an instance of the object which implements the IModule
interface with:
object myObject = Activator.CreateInstance(typeofObject);
I then use the MethodInfo.Invoke and PropertyInfo.GetValue, to start the
OnConnect method of
the plugin (OnConnect is part of IModule) and to get the list of Services
(also part of IPlugin) that the plugin provides (if any).

This sounds fine to me except that you don't necessarily have to use
MethodInfo.Invoke. Instead, simply cast your object to IModule:

object myObject = Activator.CreateInstance(typeofObject);
IModule module = (IModule)myObject;

In order for this to work though, you need to define the interface IModule
in a separate assembly and have your loader application reference that
assembly.
I then add "myObject", it's services and it's type into an array at the
host. So when some plugin would call:
Host.FindPlugin("GPS");
The Host would find that the GpsPlugin provides the GPS service and it would
provide the
object reference and Type to the plugin which requires the question.
The main problem is:
How do I cast the GpsPlugin (which is stored at the host and received as
System.Object) to the type
of GpsPlugin (note that the host stored the Type information)?

My earlier comments already answer this. Define the interface IGpsPlugin in
a shared assembly. Have the plugin reference that assembly. Then in the
plugin you can simply write:

IGpsPlugin gpsPlugin = (IGpsPlugin)Host.FindPlugin(typeof(IGpsPlugin));

You will not need to use reflection if you define the interfaces in a
separate assembly, and have the host and the plugins reference this
assembly. I know that some developers prefer the flexibility of reflection
over having interfaces. My personal preference (in this case) is type safety
through interfaces.
I wouldn't want to hardcode the casting... Is this even possible?

Thanks,
saso

Sure,
Sami
 
S

Saso Zagoranski

Thanks Sami!
You really helped me a lot!

Just one more thing :)
How do you "cast" an object if you have the Type of the object stored in
some variable t?
So you can't use:
myGpsObject = (Gps) myObject;

but instead:
myGpsObject = ??? t.GetType() ???

Again,
thanks a lot for your help
 
S

Sami Vaaraniemi

Saso Zagoranski said:
Thanks Sami!
You really helped me a lot!

Just one more thing :)
How do you "cast" an object if you have the Type of the object stored in
some variable t?
So you can't use:
myGpsObject = (Gps) myObject;

but instead:
myGpsObject = ??? t.GetType() ???

You cannot get an instance out of a type simply by casting. When you cast,
you always cast a reference to an instance, like so:

object obj = new SomeClass();
// Now cast 'obj' to a reference to 'SomeClass'
SomeClass sc = (SomeClass)obj;

Btw, there are a number of articles on the net that show how to create a
pluggable architecture. I know there is one on www.codeproject.com (I can't
seem to access it now). There's another one written in VB.NET on
developerfusion.com: http://www.developerfusion.com/show/4371/ .

I also wrote a quick C# sample that shows one way of doing a pluggable
architecture. It is very similar to the articles above. My sample is
available for download at http://www.capehill.net/pluginsample.zip . I might
add some security specific stuff to this sample later if I have the time...

Sami
 
S

Saso Zagoranski

I did something very similar to what you have...
I just have a config file to load the modules...

What do you mean by security? Controling access to the modules? Where can I
find some
reading on this subject?

Again, thanks for all your help
saso
 
S

Sami Vaaraniemi

Saso Zagoranski said:
I did something very similar to what you have...
I just have a config file to load the modules...

What do you mean by security? Controling access to the modules? Where can I
find some
reading on this subject?

If your application allows for extensibility via remotely loaded plugins,
you will probably want to make sure that you load only plugins that you know
you can trust. In practice this would boil down to checking that the plugin
assemblies are properly signed.

There are some articles e.g., on MSDN that touch this topic briefly, e.g.,
http://msdn.microsoft.com/netframework/using/building/windows/analystreports/smartclient.aspx .

Sami
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top