Plugin Application Design Advice

D

Dave Weber

I'm in the process of writing an open-source plugin-enabled application.
I know that you can define the API in several ways, and right now it
is equipped as an Interface.

The gist of the functionality, is when a timer expires, the app will
notify the plugins that it's time for them to do their work. Now, the
plugins may take an indeterminate period of time (e.g.: they have
nothing to do, or they have a few seconds of work to do).

Now, I want to notify the user as to the status of the plugin via a
systemtray icon changing to a "busy" state. So currently, I have a
second timer running every second, which polls the plugins to ask them
if they're busy. This seems like an awefully bad practice, but it works.

I'm just wondering if it would be better to use some sort of callback
function so that the plugins can tell the manager "i'm busy" and "i'm
free" as opposed to a "ADHD kid w/ too much sugar-esque" "are you busy?
are you busy? are you busy?..."

If i'm better off using a delegate as a callback, do I have to abandon
the interface and move towards an abstract class?
 
S

Samuel R. Neff

The interface can require an event such as StatusChanged that the
plugins must trigger to provide you with an update on the status
(well, the interface can require that they have the event, it's up to
the plugin authors to be polite and actually raise the event when
appropriate).

HTH,

Sam


I'm in the process of writing an open-source plugin-enabled application.
I know that you can define the API in several ways, and right now it
is equipped as an Interface.

The gist of the functionality, is when a timer expires, the app will
notify the plugins that it's time for them to do their work. Now, the
plugins may take an indeterminate period of time (e.g.: they have
nothing to do, or they have a few seconds of work to do).

Now, I want to notify the user as to the status of the plugin via a
systemtray icon changing to a "busy" state. So currently, I have a
second timer running every second, which polls the plugins to ask them
if they're busy. This seems like an awefully bad practice, but it works.

I'm just wondering if it would be better to use some sort of callback
function so that the plugins can tell the manager "i'm busy" and "i'm
free" as opposed to a "ADHD kid w/ too much sugar-esque" "are you busy?
are you busy? are you busy?..."

If i'm better off using a delegate as a callback, do I have to abandon
the interface and move towards an abstract class?

B-Line is now hiring one Washington D.C. area VB.NET
developer for WinForms + WebServices position.
Seaking mid to senior level developer. For
information or to apply e-mail resume to
sam_blinex_com.
 
D

Dave Weber

Good idea, but another (related) problem. I've got a static method
running in another thread, and it cannot raise the event because its not
static.

A C++ answer would be to just pass in a this pointer and be done w/ it,
but the ThreadStart delegate can't take any passed parameters. I'm
missing something here, but I'm not sure what (and I'll feel pretty
silly once I get the answer I'm sure).

<example>

public event StatusDelegate StatusChanged;

private void SignalStatus(PluginStatus status)
{
if(StatusChanged != null)
{
StatusChanged(status);
}
}

public static void ThreadProc()
{
// Does not work, because "SignalStatus" is not static
SignalStatus(PluginStatus.busy);

Thread.Sleep(2000); // do some work

// Does not work, because "SignalStatus" is not static
SignalStatus(PluginStatus.idle);
}

</example>
 
S

Samuel R. Neff

No problem. Don't use a static method. :)

Or use the ThreadPool.QueueUserWorkItem which uses a WaitCallback
delegate which does accept a state object which can be used for the
callback.

Or, if you're really intent on using a static and creating your own
thread, then create a proxy class which has the sole purpose to store
parameters and then call a second delegate with the parameters when
it's start method is called (see below). But this is quite a bit
overkill for the problem at hand, imo.

HTH,

Sam



using System;
using System.Threading;

public class ThreadStartWithParam
{
public static void Test()
{
object[] parameters = new object[] {"this is a
param"};
ThreadStartProxy p = new ThreadStartProxy(parameters,
new WaitCallback(Start));
Thread t = new Thread(new ThreadStart(p.Start));
t.Start();
t.Join();
}

public static void Start(object state)
{
Console.WriteLine(state.ToString());
}

}

public class ThreadStartProxy
{
private object[] _params;
private Delegate _callback;

public ThreadStartProxy(object[] parameters, Delegate
callback)
{
_params = parameters;
_callback = callback;
}

public void Start()
{
_callback.DynamicInvoke(_params);
}
}






Good idea, but another (related) problem. I've got a static method
running in another thread, and it cannot raise the event because its not
static.

A C++ answer would be to just pass in a this pointer and be done w/ it,
but the ThreadStart delegate can't take any passed parameters. I'm
missing something here, but I'm not sure what (and I'll feel pretty
silly once I get the answer I'm sure).

<example>

public event StatusDelegate StatusChanged;

private void SignalStatus(PluginStatus status)
{
if(StatusChanged != null)
{
StatusChanged(status);
}
}

public static void ThreadProc()
{
// Does not work, because "SignalStatus" is not static
SignalStatus(PluginStatus.busy);

Thread.Sleep(2000); // do some work

// Does not work, because "SignalStatus" is not static
SignalStatus(PluginStatus.idle);
}

</example>
B-Line is now hiring one Washington D.C. area VB.NET
developer for WinForms + WebServices position.
Seaking mid to senior level developer. For
information or to apply e-mail resume to
sam_blinex_com.
 
D

Dave Weber

I'm testing a.... well, for lack of a better term, its a hack. There! I
said it! :)

Basically, in the class, I created:

static MyClass _thisPtr;

in the constructor:

_thisPtr = this;

in the thread:

this.MyEvent()

I think that it'll work, and gets around the parameter passing, and
gives me a this pointer, relatively few lines of code, relatively easy
to understand. (which are all ideal traits)

Your other ideas are definately on the plate if this doesn't work.
 
S

Samuel R. Neff

Be sure to see what happens the second time you create an object and
the two are running simultaneously. :)

Or, check in the constructor to be sure the object is only
instantiated once (throw an error if _thisPtr!=null).

You can't use a singleton in this scenario because the interface can't
enforce a singleton pattern (and you wouldn't want to require all
plug-in developers to use a singleton when their implementation of the
interface wouldn't otherwise call for it).

HTH,

Sam


I'm testing a.... well, for lack of a better term, its a hack. There! I
said it! :)

Basically, in the class, I created:

static MyClass _thisPtr;

in the constructor:

_thisPtr = this;

in the thread:

this.MyEvent()

I think that it'll work, and gets around the parameter passing, and
gives me a this pointer, relatively few lines of code, relatively easy
to understand. (which are all ideal traits)

Your other ideas are definately on the plate if this doesn't work.

B-Line is now hiring one Washington D.C. area VB.NET
developer for WinForms + WebServices position.
Seaking mid to senior level developer. For
information or to apply e-mail resume to
sam_blinex_com.
 
D

Dave Weber

I'll definitely keep it in mind. Conceptually, I don't think this is
going to be a problem, because logically each plugin would be loaded &
instantiated once, because each plugin would logically do a separate thing.

All of this is (of course) subject to change, because I'm working on a
DemoPlugin which is nothing more than a way to unit-test my main app,
plugin manager, and tweak the interface as I need it. (Which
incidentally, the whole eventing idea is being used elsewhere. It's a
really good idea, for several things I'm doing that were just plain ugly
before)

i'm going to ponder this for a bit, just to decide what the best
direction to take is.
 

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